例如免费卷饼的优惠券.用户A收到免费卷饼的优惠券,然后用户B收到免费卷饼的优惠券. 2张优惠券应该有独特的优惠券代码.
生成不容易伪造的代码的最佳方式是什么?我不希望用户以随机数字输入成功率很高,并兑换其他人的优惠券.
我想这个想法就像一张背面有唯一号码的礼品卡是我正在寻找的.
解决方法
>这意味着该格式的所有可能代码的数量远远大于要发行的代码数量.根据简单地尝试代码(考虑重复尝试的脚本)的简单性,那么您可能需要所有可能的代码超过已发布的代码的百万分之一或十亿以上.这听起来很高,但可能在相对较短的字符串.
>这也意味着您使用的代码必须在所有可能的代码中尽可能随机地选择.这是必要的,以避免用户弄清楚大多数有效的代码以“AAA”开头.更复杂的用户可能会发现您的“随机”代码使用可攻击的随机数生成器(Ruby的默认rand()对于随机数据是快速和统计的好的,但是以这种方式可以使用,所以不要使用它).
这样一个安全代码的出发点是加密PRNG的输出. Ruby具有securerandom库,您可以使用它来获取如下原始代码:
require 'securerandom' SecureRandom.hex # => "78c231af76a14ef9952406add6da5d42"
这段代码足够长,可以覆盖任何实际数量的凭证(每个人都有数百万个),没有任何有意义的重复机会或容易猜测.但是,从物理副本输入是有点尴尬.
一旦你知道如何产生一个随机的,几乎无法估量的代码,你的下一个问题就是理解用户体验,并决定你可以以可用性的名义实际地危害安全性.您需要牢记最终用户的价值,因此有可能尝试获取有效的代码.我不能为你答复,但可以提出一些关于可用性的一般观点:
>避免模糊字符在打印中,有时难以看到1,I和l之间的区别.我们经常明白它应该是从上下文,但是一个随机的字符串没有这个上下文.通过测试0对O,5对S等,不得不尝试一些代码的差异,这将是一个糟糕的用户体验.
>使用小写字母或大写字母,但不能同时使用.区分大小写不会被用户的百分之百的年龄所理解.
>匹配代码时接受变体.允许空格和破折号.也许甚至允许0和O表示同样的事情.这最好是通过处理输入文本来实现,因为它在正确的情况下,条带分隔符等.
>在打印中,将代码分成几个小部分,用户更容易在字符串中找到它们的位置,并一次输入几个字符.
>不要让代码太长.我建议12个字符,3组4.
>这是一个有趣的一个 – 你可能想扫描代码可能的粗鲁的话,或避免生成它们的字符.如果您的代码仅包含字符K,U,F,C,那么很可能会冒犯用户.这通常不是一个问题,因为用户看不到大多数计算机安全代码,但这些代码将会打印出来!
# Random,unguessable number as a base20 string # .reverse ensures we don't use first character (which may not take all values) raw_string = SecureRandom.random_number( 2**80 ).to_s( 20 ).reverse # e.g. "3ecg4f2f3d2ei0236gi" # Convert Ruby base 20 to better characters for user experience long_code = raw_string.tr( '0123456789abcdefghij','234679QWERTYUPADFGHX' ) # e.g. "6AUF7D4D6P4AH246QFH" # Format the code for printing short_code = long_code[0..3] + '-' + long_code[4..7] + '-' + long_code[8..11] # e.g. "6AUF-7D4D-6P4A"
这种格式有20 ** 12个有效的代码,这意味着你可以发出十亿个你自己的代码,一个用户只需猜到一个正确的代码就有四分之一的机会.在密码学圈子中,这是非常糟糕的(这个代码对本地快速攻击是不安全的),但是对于注册用户提供免费卷饼的网络表单,以及在哪里你会注意到一个用脚本尝试四百万次的人,这是可以的.