如何生成验证码/号码?

时间:2020-03-05 18:48:20  来源:igfitidea点击:

我正在开发一个应用程序,用户必须拨打电话并用手机的键盘输入验证码。

我希望能够检测出他们输入的数字是否正确。电话系统无法访问有效号码列表,但会根据算法(例如信用卡号)验证号码。

以下是一些要求:

  • 键入有效的随机码一定很困难
  • 输入错字(数字换位,数字错误),一定很难获得有效的代码
  • 我必须要有合理数量的可能组合(比如说1M)
  • 该代码必须尽可能短,以免引起用户错误

鉴于这些要求,我们将如何生成这样的数字?

编辑 :

@Haaked:该代码必须是数字,因为用户使用手机来键入它。

@matt b:第一步,代码显示在网页上,第二步是调用并键入代码。我不知道用户的电话号码。

Folowup:我发现了几种算法来检查数字的有效性(请参阅这个有趣的Google Code项目:checkDigits)。

解决方案

回答

对于1M组合,我们需要6位数字。为了确保没有任何意外有效的代码,我建议使用9位数字,并且有1/1000的机会使用随机代码。我还建议使用另一个数字(共10个数字)执行完整性检查。就分发模式而言,随机就足够了,校验位将确保单个错误不会导致正确的代码。

编辑:显然我没有完全阅读请求。使用信用卡号,我们可以对其进行哈希处理(MD5或者SHA1或者类似的名称)。然后,我们在适当的位置截断(例如9个字符)并转换为10. 然后添加校验位,这或者多或者少地适合目的。

回答

I must have a reasonnable number of possible combinations (let's say 1M)
  The code must be as short as possible, to avoid errors from the user

好吧,如果我们希望它具有至少一百万个组合,那么我们至少需要六位数字。够短吗?

回答

创建验证码时,我们可以访问呼叫者的电话号码吗?

如果是这样,我将使用呼叫者的电话号码并通过某种哈希函数运行它,以便我们可以确保在步骤1中提供给呼叫者的验证码与他们在步骤2中输入的验证码相同(以确保他们没有使用朋友的验证码,或者只是做出了非常幸运的猜测)。

关于散列,我不确定是否可以采用10位数字并得出小于10位的散列结果(我想我们必须承受一定的冲突),但我认为这将有助于确保用户就是他们所说的。

如果步骤1中使用的电话号码与步骤2中呼叫的电话号码不同,那么这当然是行不通的。

回答

一定只是数字吗?我们可以创建一个介于1到1M之间的随机数(尽管我建议更高),然后Base32对其进行编码。我们需要做的下一件事是对值(使用秘密盐值)进行哈希处理,然后base32对哈希进行编码。然后将两个字符串添加在一起,也许用短划线隔开。

这样,我们可以通过算法验证传入的代码。我们只需要获取代码的左侧,使用秘密盐对其进行哈希处理,然后将该值与代码的右侧进行比较即可。

回答

假设我们已经知道如何检测用户按下了哪个键,那么应该可以轻松地做到这一点。在安全领域,存在"一次性"密码的概念。有时将其称为"一次性密码"。通常,这些值仅限于(易于键入的)ASCII值。因此,[a-zA-z0-9]和一堆易于键入的符号。如逗号,句号,半冒号和括号。但是,就我们而言,我们可能希望将范围限制为[0-9],并可能包括*和#。

我无法解释有关如何一次性生成(或者工作)这些一次性代码的所有技术细节。它背后有一些中级数学,我会亲自屠夫而无需先对其进行审查。可以说我们使用一种算法来生成一次一次性密码流。不管我们之前知道的代码多么简单,后面的代码都不应该猜测!在情况下,我们只需将列表中的每个密码用作用户的随机代码。

除了直接自己解释实现的细节外,我还将带我们转到9页的文章,我们可以自己自己阅读该文章:https://www.grc.com/ppp.htm

回答

听起来我们有一个不言而喻的要求,即必须通过算法快速确定该代码有效。这将排除我们仅分发一个时区号码列表的可能性。

人们过去有几种方法可以做到这一点。

  • 制作一个公用密钥和专用密钥。使用私钥对数字0-999,999进行编码,然后分发结果。我们需要输入一些随机数以使结果出现在更长的版本中,并且必须将结果从64转换为10. 输入数字后,将其转换回base64,应用私钥,并查看有趣的数字是否低于1,000,000(丢弃随机数字)。
  • 使用可逆哈希函数
  • 使用以特定值播种的PRN中的前一百万个数字。 "检查"功能可以获取种子,并且知道接下来的一百万个值是好的。它既可以每次生成它们,并在收到代码时一一检查,也可以在程序启动时将它们全部存储在一个表中,进行排序,然后使用二进制搜索(最大比较数),因为一百万个整数不是整数空间。

还有很多其他选项,但是这些选项很常见且易于实现。

-亚当

回答

我们链接到支票号码项目,并且使用"编码"功能似乎是一个很好的解决方案。它说:

encode may throw an exception if 'bad' data (e.g. non-numeric) is passed to it, while verify only returns true or false. The idea here is that encode normally gets it's data from 'trusted' internal sources (a database key for instance), so it should be pretty usual, in fact, exceptional that bad data is being passed in.

因此,听起来我们可以将编码函数传递给数据库密钥(例如5位数字),并且可以得到一个满足我们要求的数字。

回答

经过研究,我认为我将采用ISO 7064 Mod 97,10公式。由于它用于验证IBAN(国际银行帐号),因此看起来非常可靠。

公式很简单:

  • 取一个数字:123456
  • 应用以下公式获得2位数的校验和:mod(98-mod(number * 100,97),97)=> 76
  • Concat编号和校验和以获得代码=> 12345676
  • 要验证代码,请验证mod(code,97)== 1

测试 :

  • mod(12345676,97)= 1=>良好
  • mod(21345676,97)= 50=>不好!
  • mod(12345678,97)= 10=>不好!

显然,该算法捕获了大多数错误。

另一个有趣的选择是Verhoeff算法。它只有一个验证码,并且更难以实现(与上面的简单公式相比)。

回答

我们想对代码进行分段。它的一部分应该是其余代码的16位CRC。

如果我们只需要一个验证号,则只需使用一个序列号即可(假设我们只有一个生成点)。这样,我们就知道不会重复。

然后,为该序列添加该序列号的CRC-16前缀和一些私钥。我们可以将任何东西用作私钥,只要我们将其保持私密即可。使它变大,至少要有一个GUID,但这可能是古腾堡项目写给《战争与和平》的文字。只是需要保持秘密和不断。拥有私钥会阻止人们伪造密钥,但是使用16位CR使其更容易破解。

要进行验证,我们只需将数字分为两部分,然后对序列号和私钥进行CRC-16校验。

如果要进一步遮掩顺序部分,则将CRC分为两部分。将3位数字放在序列的前面,再将2位放在序列的后面(零填充,以便CRC的长度保持一致)。

此方法还允许我们从较小的键开始。前10个键将是6位数字。