代码之家  ›  专栏  ›  技术社区  ›  Costo

如何生成验证码/编号?

  •  25
  • Costo  · 技术社区  · 16 年前

    我正在开发一个应用程序,在这个应用程序中,用户必须打一个电话,然后用手机键盘输入一个验证号码。

    我想知道他们输入的数字是否正确。电话系统无法访问有效号码列表,但它将根据算法(如信用卡号码)验证号码。

    以下是一些要求:

    • 必须很难输入有效的随机码
    • 如果我输入错别字,一定很难得到有效的代码(数字的转换,错误的数字)
    • 我必须有合理数量的可能组合(比如1百万)
    • 代码必须尽可能短,以避免用户出错。

    考虑到这些要求,您将如何生成这样一个数字?

    编辑:

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

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

    以下:我发现了一些算法 检查 数字的有效性(参见本有趣的谷歌代码项目: checkDigits )

    9 回复  |  直到 16 年前
        1
  •  29
  •   Glen Selle Kashif    11 年前

    经过一些研究,我想我会去 ISO 7064 97,10版 公式。它看起来相当可靠,因为它被用来验证IBAN(国际银行帐号)。

    公式非常简单:

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

    测试:

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

    显然,这个算法捕获了大部分错误。

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

        2
  •  4
  •   Kyle Cronin    16 年前

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

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

        3
  •  2
  •   Jim McKeeth    16 年前

    您想分段您的代码。它的一部分应该是其余代码的16位CRC。

    如果您只需要一个验证号,那么只需使用序列号(假设您有一个生成点)。这样你就知道你不会收到副本。

    然后在序列前面加上该序列号的CRC-16和一些私钥。您可以使用任何东西作为私钥,只要您保持它是私有的。做一件大事,至少是 GUID ,但它可以是文本 War and Peace from project Gutenberg . 只是需要保持秘密和不变。拥有一个私钥可以防止人们伪造一个密钥,但是使用16位CR更容易破解。

    为了验证,您只需将数字分成两部分,然后取序列号和私钥的CRC-16。

    如果您想使顺序部分更加模糊,那么将CRC分为两部分。将3位数字放在序列的前面,将2位数字放在序列的后面(零位,这样CRC的长度就一致了)。

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

        4
  •  1
  •   Haacked    16 年前

    必须是数字吗?你可以创建一个1到1百万之间的随机数(我建议更高一点),然后 Base32 encode it . 接下来需要做的是散列该值(使用秘密的salt值),base32对散列进行编码。然后将两个字符串附加在一起,可能用破折号分隔。

    这样,您就可以根据算法验证输入的代码。您只需取代码的左侧,使用您的秘密盐散列它,并将该值与代码的右侧进行比较。

        5
  •  0
  •   Chris Upchurch    16 年前
    • 我必须有合理数量的可能组合(比如1百万)
    • 代码必须尽可能短,以避免用户出错。

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

        6
  •  0
  •   matt b    16 年前

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

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

    关于散列,我不确定是否可以取一个10位数的数字,并得出一个10位数的散列结果(我猜你必须经历一定程度的冲突),但我认为这将有助于确保用户是他们所说的人。

    当然,如果第1步中使用的电话号码与第2步中使用的电话号码不同,这将不起作用。

        7
  •  0
  •   Rob Rolnick    16 年前

    假设您已经知道如何检测用户点击了哪个键,那么这应该是相当容易做到的。在安全世界中,有“一次性”密码的概念。这有时被称为“一次性密码”。通常,这些密码仅限于(易于输入)ASCII值。所以,[a-za-z0-9]和一堆容易打字的符号。例如逗号、句点、分号和括号。不过,在您的情况下,您可能希望将范围限制为[0-9],并可能包括*和。

    我无法解释如何充分生成(或工作)这些一次性代码的所有技术细节。它背后有一些中间的数学知识,我先不复习就把它砍掉了。只要说您使用一个算法来生成一次性密码流就足够了。不管你知道多少以前的代码,后面的代码都不可能猜到!在您的例子中,您只需使用列表中的每个密码作为用户的随机代码。

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

        8
  •  0
  •   Adam Davis    16 年前

    听起来您有一个无法言喻的要求,即必须通过算法快速确定代码是否有效。这就排除了你只是简单地给出一个一次性密码簿的列表。

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

    1. 生成公钥和私钥。使用私钥对数字0-999999进行编码,并分发结果。您需要输入一些随机数,以使结果显示为较长的版本,并且必须将结果从base 64转换为base 10。当你输入一个数字时,把它转换回base64,应用私钥,看看有趣的数字是否低于1000000(丢弃随机数字)。
    2. 使用A reversible hash function
    3. 使用以特定值输入的prn中的前百万个数字。“checking”函数可以获取种子,并知道接下来的百万值是好的。它可以每次生成它们,并在收到代码时逐个检查,或者在程序启动时将它们全部存储在一个表中,进行排序,然后使用二进制搜索(最大比较数),因为一百万个整数不是整个空间。

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

    -亚当

        9
  •  0
  •   Michael Sharek    16 年前

    你链接到 check digits 项目,使用“encode”函数似乎是一个很好的解决方案。它说:

    如果将“坏”数据(如非数字)传递给编码器,则encode可能会引发异常,而verify只返回true或false。这里的想法是,encode通常从“可信”的内部源(例如数据库密钥)获取数据,因此它应该是非常常见的,事实上,异常的是坏数据正在传入。

    因此,听起来您可以将一个数据库键(例如,5位数字)传递给编码函数,然后您可以得到一个满足您需求的数字。

    推荐文章