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

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

以下是一些要求:

  • 输入有效的随机代码一定很困难
  • 如果我输入错误(数字换位、数字错误),那么很难获得有效的代码
  • 我必须有合理数量的可能组合(假设 1M)
  • 代码必须尽可能短,以避免用户出错

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

编辑 :

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

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

跟进 :我发现了几种算法 查看 数字的有效性(参见这个有趣的 Google Code 项目: 校验位).

有帮助吗?

解决方案

经过一番研究后,我想我会选择 ISO 7064 模组 97,10 公式。它看起来相当可靠,因为它用于验证 IBAN(国际银行帐号)。

公式很简单:

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

测试 :

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

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

另一个有趣的选择是 Verhoeff算法. 。它只有一位验证位,实现起来比较困难(与上面的简单公式相比)。

其他提示

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

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

您想要对代码进行分段。其中一部分应该是代码其余部分的 16 位 CRC。

如果您想要的只是验证号,则只需使用序列号(假设您有单点生成)。这样你就知道你没有得到重复的信息。

然后,使用该序列号的 CRC-16 和一些私钥作为序列前缀。您可以使用任何东西作为私钥,只要您保持其私密性即可。让它变得更大,至少 GUID, ,但它可能是文本 古腾堡计划的战争与和平. 。只需要保密且持续。拥有私钥可以防止人们伪造密钥,但使用 16 位 CR 可以更容易破解。

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

如果您想更加模糊连续部分,请将 CRC 分成两部分。在序列的前面放置 3 位数字,在后面放置 2 位数字(补零,以便 CRC 的长度一致)。

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

它必须只是数字吗?您可以创建一个 1 到 1M 之间的随机数(不过我建议更高),然后 Base32编码它. 。您需要做的下一件事是对该值进行哈希处理(使用秘密盐值)并对哈希值进行 Base32 编码。然后将两个字符串附加在一起,可能用破折号分隔。

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

  • 我必须有合理数量的可能组合(假设 1M)
  • 代码必须尽可能短,以避免用户出错

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

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

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

关于散列,我不确定是否有可能采用 10 位数字并得出 < 10 位数字的散列结果(我猜你必须忍受一定程度的冲突),但我认为这将有助于确保用户就是他们所说的那样。

当然,如果第 1 步中使用的电话号码与第 2 步中拨打的电话号码不同,则此方法将不起作用。

假设您已经知道如何检测用户按下的键,那么这应该相当容易实现。在安全领域,存在“一次性”密码的概念。有时将其称为“一次性密码”。通常,这些仅限于(易于键入)ASCII值。因此,[a-zA-z0-9] 和一堆易于输入的符号。如逗号、句点、分号和括号。不过,就您而言,您可能希望将范围限制为 [0-9],并可能包括 * 和 #。

我无法解释这些一次性代码如何充分生成(或工作)的所有技术细节。它背后有一些中级数学知识,我会在没有先自己复习的情况下将其删除。只要说您使用一种算法来生成一次性密码流就足够了。不管前面的代码你知道多少,后面的代码应该是猜不出来的!在您的情况下,您只需使用列表中的每个密码作为用户的随机代码。

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

听起来您有一个不言而喻的要求,即必须通过算法快速确定代码是否有效。这将排除您简单地分发一份一次性记事本号码列表的情况。

过去人们有多种方法来做到这一点。

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

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

-亚当

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

如果“坏”数据(例如非数字)传递给它,而 verify 仅返回 true 或 false。这里的想法是,编码通常从“受信任”的内部源(例如数据库密钥)获取数据,因此实际上,传递错误数据应该是很常见的情况。

因此,听起来您可以向编码函数传递一个数据库密钥(例如 5 位数字),然后您就可以得到一个满足您要求的数字。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top