如何生成验证码/号码?

时间:2008-09-05 16:28:35

标签: algorithm checksum error-checking data-consistency

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

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

以下是一些要求:

  • 输入有效的随机码
  • 一定很困难
  • 如果我输入错字(数字换位,数字错误),则必须难以拥有有效代码
  • 我必须有合理数量的可能组合(比方说1M)
  • 代码必须尽可能短,以避免来自用户的错误

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

编辑:

@Haaked:代码必须是数字,因为用户用手机输入代码。

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

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

9 个答案:

答案 0 :(得分:29)

经过一些研究,我想我会选择 ISO 7064 Mod 97,10 公式。它似乎非常可靠,因为它用于验证IBAN(国际银行帐号)。

公式非常简单:

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

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

    显然,该算法可以捕获大部分错误。

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

答案 1 :(得分:4)

对于1M组合,您需要6位数字。为了确保没有任何意外有效的代码,我建议9位数字,随机代码的工作概率为1/1000。我还建议使用另一个数字(总共10个)来执行integrity check。就分布模式而言,随机就足够了,校验位将确保单个错误不会产生正确的代码。

编辑:显然我没有完全阅读您的请求。使用信用卡号码,您可以对其执行哈希(MD5或SHA1或类似的东西)。然后在适当的位置截断(例如9个字符)并转换为基数10.然后添加校验位,这应该或多或少地适用于您的目的。

答案 2 :(得分:2)

您想要细分代码。部分应该是其余代码的16位CRC。

如果您只想要一个验证码,那么只需使用一个序列号(假设您有一个生成点)。这样你知道你没有得到重复。

然后在序列前面加上该序列号的CRC-16和一些私钥。您可以将任何内容用于私钥,只要您将其保密即可。做一些大事,至少GUID,但它可能是War and Peace from project Gutenberg的文本。只需要保密和不变。使用私钥可以防止人们伪造密钥,但使用16位CR可以更容易破解。

要验证您只是将数字拆分为两部分,然后获取序列号和私钥的CRC-16。

如果您想更多地模糊顺序部分,则将CRC拆分为两部分。在序列的前面放置3位数字,在序列的后面放置2个数字(零填充,因此CRC的长度是一致的)。

此方法也允许您从较小的键开始。前10个键将是6位数。

答案 3 :(得分:1)

它必须只是数字吗?您可以创建1到1M之间的随机数(我建议甚至更高)然后Base32 encode it。接下来你需要做的是散列该值(使用秘密盐值)和base32编码散列。然后将两个字符串附加在一起,也许用短划线分隔。

这样,您就可以通过算法验证传入的代码。您只需使用代码的左侧,使用您的秘密盐哈希,并将该值与代码的右侧进行比较。

答案 4 :(得分:0)

  
      
  • 我必须有一个合理数量的可能组合(比方说1M)
  •   
  • 代码必须尽可能短,以避免来自用户的错误
  •   

好吧,如果你想让它拥有至少一百万种组合,那么你至少需要六位数。这够短吗?

答案 5 :(得分:0)

创建验证码时,您是否可以访问来电者的电话号码?

如果是这样,我会使用来电者的电话号码并通过某种散列功能运行它,这样您就可以保证在步骤1中给调用者的验证码与他们在步骤2中输入的验证码相同(确保他们没有使用朋友的验证码,或者他们只是做了一个非常幸运的猜测。)

关于散列,我不确定是否可以取一个10位数的数字并得出一个哈希结果< 10位数(我想你必须忍受一定程度的碰撞)但我认为这有助于确保用户是他们所说的人。

当然,如果步骤1中使用的电话号码与步骤2中拨打的电话号码不同,则无法使用。

答案 6 :(得分:0)

假设您已经知道如何检测用户点击的密钥,那么这应该是相当容易的。在安全领域,存在“一次性”密码的概念。这有时被称为“一次性密码”。通常这些仅限于(容易打字的)ASCII值。所以,[a-zA-z0-9]和一堆容易打字的符号。像逗号,句号,半结肠和括号。但是,在您的情况下,您可能希望将范围限制为[0-9],并且可能包括*和#。

我无法解释这些一次性代码如何充分生成(或工作)的所有技术细节。它背后有一些中间数学,我没有先自己检查它而是屠夫。只需说您使用算法生成一次性密码流即可。无论你以前的代码如何知道,后续的代码都应该是不可能的猜测!在您的情况下,您只需使用列表中的每个密码作为用户的随机代码。

我不会自己解释实施的细节,而是会引导您阅读一篇9页的文章,您可以自己阅读:https://www.grc.com/ppp.htm

答案 7 :(得分:0)

听起来你有一个不言而喻的要求,即必须通过算法快速确定代码是否有效。这将排除您简单地分发一次填充数字的列表。

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

  1. 制作公钥和私钥。使用私钥对数字0-999,999进行编码,然后分发结果。您需要输入一些随机数来使结果出现在更长的版本中,并且您必须将结果从base 64转换为基数10.当您输入一个数字时,将其转换回base64,应用私钥,看看互联号码是否低于1,000,000(丢弃随机数)。
  2. 使用reversible hash function
  3. 使用来自PRN的特定值的第一百万个数字。 “检查”功能可以获得种子,并知道下一百万个值是好的。它可以每次生成它们并在接收到代码时逐个检查,或者在程序启动时将它们全部存储在一个表中,排序,然后使用二进制搜索(最多比较),因为一百万个整数不是很多太空了。
  4. 还有许多其他选项,但这些选项很常见且易于实现。

    - 亚当

答案 8 :(得分:0)

您链接到check digits项目,使用“编码”功能似乎是一个很好的解决方案。它说:

  如果将“坏”数据(例如非数字)传递给它,

encode可能会抛出异常,而verify只返回true或false。这里的想法是编码通常从“可信”的内部源(例如数据库密钥)获取数据,因此它应该是非常常见的,事实上,传递不良数据是非常常见的。

所以听起来你可以将编码功能传递给数据库密钥(例如5位),你可以得到一个符合你要求的编号。