生成独特的,难以猜测的"优惠券"代码

Dee*_*kor 17 ruby algorithm ruby-on-rails uniqueidentifier

我的Rails应用程序需要为用户生成电子优惠券.每张优惠券都应该有一个独特的优惠券代码,可以在我们的系统上兑换.

例如,免费墨西哥卷饼的优惠券.User A收到免费墨西哥卷饼User B的优惠券,然后收到免费墨西哥卷饼的优惠券.2张优惠券应该有唯一的优惠券代码.

生成像这样不易伪造的代码的最佳方法是什么?我不希望用户输入随机数和兑换其他人优惠券的成功率很高.

我想这就像背面有一个唯一编号的礼品卡就是我要找的东西.

Nei*_*ter 40

代码需要是不可取的,因为在向用户提供奖励之前,您可以执行的唯一验证是检查他们输入的代码是否存在于"已颁发"代码列表中.

  • 这意味着该格式中所有可能代码的数量远远大于您要发布的代码数量.根据简单地尝试代码是多么容易(考虑到重复尝试的脚本),那么您可能需要所有可能的代码,使发布的代码数量超过一百万或十亿或更多.这听起来很高,但可以在相对较短的字符串中使用.

  • 这也意味着您使用的代码必须在所有可能的代码中随机选择.这对于避免用户确定大多数有效代码以"AAA"开头是必要的.更复杂的用户可能会发现你的"随机"代码使用了一个可破解的随机数生成器(Ruby的默认值rand()对于随机数据来说速度快且统计上很好,但是以这种方式可以破解,所以不要使用它).

这种安全代码的起点是加密PRNG的输出.Ruby有一个securerandom库,您可以使用它来获取这样的原始代码:

require 'securerandom'
SecureRandom.hex
# => "78c231af76a14ef9952406add6da5d42"
Run Code Online (Sandbox Code Playgroud)

这段代码足够长,可以覆盖任何实际数量的代金券(地球上每个人都有数百万),没有任何重复的机会或容易猜测.但是,从物理副本输入有点尴尬.

一旦你知道如何生成一个随机的,几乎无法解释的代码,你的下一个问题就是了解用户体验并决定你可以在可用性的名义上实际损害安全性的程度.您需要牢记最终用户的价值,因此有人可能会努力获得有效的代码.我不能为你解答,但可以对可用性做一些一般性的观点:

  • 避免模​​棱两可的人物.在打印时,有时很难看出之间的差1,Il例如.我们经常从上下文中理解它应该是什么,但随机字符串不具有此上下文.通过测试0vs O,5vs S等来尝试几种代码变体将是一个糟糕的用户体验.

  • 使用小写字母或大写字母但不能同时使用.案例敏感性将不会被用户的某个%年龄理解或遵循.

  • 匹配代码时接受变体.允许空格和破折号.也许甚至允许0O意味着相同的事情.这最好通过处理输入文本,使其在正确的情况下,剥离分隔符字符等.

  • 在打印中,将代码分成几个小部分,用户可以更容易地在字符串中找到它们的位置并一次键入几个字符.

  • 不要让代码太长.我建议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"
Run Code Online (Sandbox Code Playgroud)

20**12这种格式有一些有效的代码,这意味着您可以发出十亿个自己的代码,并且用户只有四分之一的机会猜测一个正确的代码.在加密圈中会非常糟糕(这段代码对快速本地攻击不安全),但是对于向注册用户提供免费卷饼的网络表单,以及你会注意到有人用脚本尝试四百万次的情况,这是可以的.


bax*_*ang 8

最近我写了优惠券代码gem,完全一样.该算法借鉴了Algorithm :: CouponCode CPAN模块.

优惠券代码不仅应该是唯一的,还应该易于阅读和打字,同时它仍然是安全的.尼尔的解释和解决方案很棒.这个宝石提供了一种方便的方法和奖励验证功能.

>> require 'coupon_code'
>> code = CouponCode.generate
=> "1K7Q-CTFM-LMTC"
>> CouponCode.validate(code)
=> "1K7Q-CTFM-LMTC"
>> CouponCode.validate('1K7Q-CTFM-LMTO') # Invalid code
=> nil
Run Code Online (Sandbox Code Playgroud)


Vou*_*ify 5

创建无法猜测的优惠券代码的关键是,可能的代码空间很大,只有一小部分实际上有效。让我们以8个字符长的字母数字字符串为例:

字母数字= 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-63个字符

在这种情况下,63^8 = 248155780267521可能会有代码。这意味着,如果您发布十亿个代码,则猜测一个代码的概率将为10^9/63^8 = 0.000004...-一百万分之四。

但是,这并不会阻止用户运行一个脚本,该脚本会一直尝试直到找出有效的代码。为了阻止这种蛮力攻击,您需要计算每个用户的尝试次数并禁止某个限制。

如果您正在寻找一个可以完全定制输出优惠券代码(长度,字符集,前缀,后缀和模式)的库,请查看voucher-code-generator-js-用JavaScript编写的库。用法示例:

voucher_codes.generate({
    length: 8,
    count: 1000,
});
Run Code Online (Sandbox Code Playgroud)

它将生成1000个随机的唯一代码,每个代码长8个字符。

另一个例子:

voucher_codes.generate({
    pattern: "###-###-###",
    count: 1000,
});
Run Code Online (Sandbox Code Playgroud)

它将按照给定的模式生成1000个随机唯一代码。

源代码相对简单。我敢打赌,如果JS不是您最喜欢的语言,您可以轻松地将其重写为任何其他语言;)

如果您需要用于凭证代码管理的全面解决方案(包括防止暴力破解),您可能会对Voucherify感兴趣。