我的Rails应用程序需要为用户生成电子优惠券。每个优惠券应该有一个独特的优惠券代码,可以在我们的系统上赎回。
例如免费玉米煎饼的优惠券。User A
收到免费玉米煎饼的优惠券,然后User B
收到免费玉米煎饼的优惠券。这两张优惠券应该有唯一的优惠券代码。
生成这样一个不易伪造的代码的最佳方法是什么?我不希望用户在输入随机数和替换其他人的优惠券方面有很高的成功率。
我想,就像一张背面有一个独特号码的礼品卡一样,我正在寻找它。
发布于 2014-03-11 18:30:57
代码需要是不可猜测的,因为在给用户奖励之前,您可以执行的唯一验证是检查他们输入的代码是否存在于您的“发布”代码列表中。
rand()
对随机数据具有快速和统计上的好处,但以这种方式是可破解的,所以不要使用它)。这种安全代码的起点是密码PRNG的输出。Ruby有一个securerandom
库,您可以使用它获取如下的原始代码:
require 'securerandom'
SecureRandom.hex
# => "78c231af76a14ef9952406add6da5d42"
这段代码足够长,足以涵盖任何实际数量的代金券(地球上每个人都有数百万),没有任何有意义的重复机会,也不容易猜测。但是,从物理副本中输入是有点尴尬的。
一旦您知道如何生成一个随机的、几乎无法猜测的代码,您的下一个问题就是理解用户体验,并决定如何以可用性的名义实际地损害安全性。您需要记住对最终用户的价值,因此需要记住某人可能会多么努力地尝试获得有效的代码。我不能为你回答这个问题,但我可以提出一些关于可用性的一般性观点:
1
、I
和l
之间的区别。我们经常从上下文中理解它应该是什么,但是随机字符串的字符没有这个上下文。必须通过测试0
与O
、5
与S
等测试代码的几个变体,这将是一种糟糕的用户体验。0
和O
表达相同的意思。这是最好的处理输入文本,所以它是在正确的情况下,条形分隔符等。K
、U
、F
、C
等字符,那么很有可能冒犯用户。这通常不是一个问题,因为用户看不到大多数计算机安全代码,但这些代码将在打印!把所有这些放在一起,我就可以生成一个可用的代码:
# Random, unguessable number as a base20 string
# .rjust(12, '0') covers for unlikely, but possible small numbers
# .reverse ensures we don't use first character (which may not take all values)
raw = SecureRandom.random_number( 2**80 ).to_s( 20 ).rjust(12, '0').reverse
# e.g. "3ecg4f2f3d2ei0236gi"
# Convert Ruby base 20 to better characters for user experience
long_code = raw.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
有效代码,这意味着您可以发出10亿个您自己的代码,并且每四百万个用户中就有一个简单地猜测一个正确的代码。在密码学领域,这将是非常糟糕的(这段代码对快速的本地攻击是不安全的),但是对于向注册用户提供免费玉米煎饼的web表单来说,如果您注意到有人尝试了四百万次使用一个脚本,这是可以的。
发布于 2014-12-22 05:11:28
最近,我写了优惠券-代码宝石,它做了完全相同的事情。该算法借鉴算法::CouponCode CPAN模块。
优惠券代码不仅应该是唯一的,而且在仍然安全的情况下容易阅读和输入。尼尔的解释和解决方案很棒。这个gem提供了一种方便的方法来完成它,并提供了一个额外的验证功能。
>> require 'coupon_code'
>> code = CouponCode.generate
=> "1K7Q-CTFM-LMTC"
>> CouponCode.validate(code)
=> "1K7Q-CTFM-LMTC"
>> CouponCode.validate('1K7Q-CTFM-LMTO') # Invalid code
=> nil
发布于 2015-12-04 14:44:14
创建不可猜测的优惠券码的关键是大空间的可能代码,其中只有一小部分实际上是有效的。让我们以8个字符长字母数字字符串为例:
字母数字= 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
- 63字符
在这种情况下,有63^8 = 248155780267521
可能的代码。这意味着,如果你发布10亿个代码,那么猜测一个代码的概率将是百万分之一的10^9/63^8 = 0.000004...
-4。
但是,它并不阻止运行一个脚本,直到找到一个有效的代码为止。为了阻止这样的蛮力攻击,您需要计算每个用户的尝试次数,并禁止超过某些限制。
如果您正在寻找一个允许对输出优惠券代码(长度、字符集、前缀、后缀和模式)进行完全定制的库,请查看凭单-代码生成器-js --一个用JavaScript编写的库。示例用法:
voucher_codes.generate({
length: 8,
count: 1000,
});
它将产生1000个随机唯一的代码,每个8个字符长。
另一个例子是:
voucher_codes.generate({
pattern: "###-###-###",
count: 1000,
});
它将根据给定的模式生成1000个随机唯一码。
源代码相对简单。我敢打赌,如果JS不是您最喜欢的语言,您可以轻松地将它重写为任何其他语言;)
如果您需要一个完整的凭证代码管理解决方案(包括暴力攻击预防),您可能对武彻化感兴趣。
https://stackoverflow.com/questions/22333237
复制相似问题