人类可读的 GUID

14

我正在编写一个小系统,可以生成代金券,在演出现场出售我们乐队的音乐,用户可以通过兑换代金券在我们的网站上下载MP3文件。

代金券需要有一个代码,用户需要输入该代码。 代码需要具备以下特点:

  1. 在长度和内容方面具有某种人类可读性,以防止用户感到沮丧和输入错误。
  2. 给定一个代金券代码,不容易猜测到另一个代金券代码。

如果我使用GUIDs,我会担心点1。 如果我使用递增整数,我会担心点2。 必须在两者之间找到一种理想的解决方案,对吧? 我想也许这项工作已经完成,并且有一个理想的解决方案等待着我。 在缺乏这个方案的情况下,我考虑采用随机的字母数字字符串,或仅包含字母(为了清晰起见,I和O除外),并让应用程序阻止失败X次的IP地址,这将表明可能有暴力攻击。 如果我采用这种方法,多长的字符串和什么值的X会起作用,为什么呢?

谢谢你的帮助!


更新:我没有完全明确方法:我将生成代金券代码列表以供打印,然后在演出后输入“已售出”的代码。 因此,像校验和这样的元素就不像使用验证服务器的软件密钥那样必要了。


关于阻止暴力攻击,我不会费心去开始。就你和你的团队而言,并不像是在保护什么非常重要的东西。这对我来说似乎有点不成比例。 - Tom Duckering
你说得完全正确,我在设计这个系统时太过于兴奋了。但是没办法,我就是一个程序员。而且,如果一切都顺利的话,我可能会托管其他乐队的专辑。 - James Orr
他们正在保护自己的工作。请注意问题中的“出售”一词。 - R. Martinho Fernandes
13个回答

9
你可以使用基于英语音节训练的马尔可夫链来创建由可发音的无意义单词组成的句子。只需在打印优惠券时将生成的句子添加到有效优惠券的数据库中(当然,在兑换后要使其失效)。

你也可以使用某种形式的N-gram分析:这可能更容易理解和实现。http://en.wikipedia.org/wiki/N-gram - R. Martinho Fernandes
我的最后一条评论可能有些令人困惑:N-gram分析可用于执行“训练”部分。 - R. Martinho Fernandes
1
如果你想避免“自动咒语生成器”问题,可以训练它识别单词而非音节。我上周用C#写了这样一个程序,将一本书输入进行分析后,它会输出像“有多少人现在面临着教育的问题”、“如果你不理解情况,那么继续下去就很困难”的“句子”。 - R. Martinho Fernandes

5

好的,如果您真的想要人类可读性,可以使用BubbleBabble。创建一个像下面这样的Perl脚本:

#!/usr/bin/perl
use Digest::BubbleBabble qw(bubblebabble);
use Digest::SHA1 qw(sha1);
print bubblebabble(Digest => sha1(join(' ', @ARGV))), "\n";

然后输入任何命令行参数,您将获得以下输出:

xogan-nydut-zogiv-kotyn-ledah-taseb-gyhib-tucel-vudul-mykom-mexax

如果您不喜欢Perl,您可以使用PWGen也可在线使用)来获得类似以下输出的结果:
aiCee5om Ohxai2is tae3Gael Gaeth7ei ooCh0ish
说实话,这种人类可读性有点过度设计;RickNZ的答案应该可以很好地工作(并且与我们为某些软件密钥所做的工作非常接近)。但BubbleBabble有点有趣。

5

AOL曾经在发送CD时使用两个随机单词的组合。您可以采用同样的方法,只需增加单词数量即可获得所需的概率。


我喜欢这个!从此列表中选出三个3或4个字母的单词,它们的密钥空间将为128,405,466,125...非常可接受。 - James Orr

5
我建议使用自己的编码方案。除了省略I和O外,为了最佳可读性,还应该省略几乎同音字集(C/E,M/N)和多音节字母(例如W),当然要保持一种大小写。
至于长度,您可以使用60位加上4位校验和。64位足以存储数千年的毫秒粒度时间,因此在实际上是无法猜测的。假设每个字母使用4位,那么长度为16个字母。即使长度减半也可能已经足够。
另一种思考方式是汽车牌照的形式:3个字母和3个数字足以覆盖一个相当大的州,并且往往非常易读。除非您提供一种高速破解代码的方法,否则它们肯定不会在人类时间尺度内被猜测到。

@RickNZ:64位时间戳被Windows NTFS和OpenVMS使用:两者每秒计数一千万个滴答。NTFS的年份范围为1601年至60055年,而VMS的年份范围为1858年至31084年。(VMS将“负”范围保留给相对时间用途。) - wallyk

4

仅有8个字母数字字符(除了I和O)有1785793904896种可能的组合。只要您没有50亿张凭证,这对于所有意图而言都是无法猜测的。


4

背景

  • 易于阅读的UUID
  • 与语言无关的算法

问题

  • 设计一个生成“人类可读”的UUID (HR-UUID)的算法
  • HR-UUID应该能够抵御暴力破解攻击
  • 人类可以直接输入和记忆,不易出错
  • 拥有1个或多个已知的有效HR-UUID不应该对猜测其他有效的HR-UUID有统计学意义

解决方案

  • Use the DiceWare password algorithm.
  • In contrast to the other solutions offered in this thread, this approach solves the human-readable UUID problem by re-casting the problem to that of password generation.
  • In contrast to the BubbleBabble solution offered elsewhere in this thread, Diceware allows you to choose how many elements are included in each UUID, depending on how many times you wish to "roll the die" ... this means you get to choose the entropy per UUID.
  • DiceWare password algorithm solves the problem of generating high-entropy passphrases that are nonetheless easy for humans to both enter and remember.
  • Below is a sampling of Diceware "UUIDs" consisting of six elements each:

    crabmeat-coach-properly-driving-yoga-ferret
    edition-mousy-fabric-budding-book-mortuary
    rickety-uncrown-earful-majority-sublet-evade
    

参见


2

嗯,我不知道大多数系统是如何工作的,但我认为定义一个静态数字并将该数字乘以另一个随机数字会更加简洁明了。然后,如果大GUID是您的静态数字的倍数,那么就可以使用它。

易于生成,但很难猜测新的数字(仅限短期使用)

int i = 61234;
int j = rand()%99999
long GUID = i * j;

将为您提供一个电话号码长度的GUID

只能使用99999次!糟糕


1
嗯,如果我們無法達到10萬張的銷售量,我們的專輯就無法獲得白金認證! - James Orr

2

使用5个由5个字符组成的块应该足够了——前四个块作为“密钥”,第五个块作为校验和以确保有效性。当然,不要使用整个密钥空间。

这大概就是软件序列号的布局方式。


有趣,我从未知道!然而,对于我的系统来说,这种算法并不直接适用,因为我将预先生成这些数字,然后在演出后“验证”我出售的代码。 - James Orr
它仍然适用 - 毕竟你不需要公开所有的代码。 - Anon.

2

最好避免所有元音字母[*],这样就可以避免所有脏话。

[*] 包括W,如果你是威尔士人!


1
W也是唯一一个多音节的字母,所以它发音要花费更长的时间(这就是我对于网站中“www”的强烈反感的原因!)。 - RickNZ
还记得“trip dub”吗?或者更糟糕的是,在九十年代的广播中你会听到“aitch tee tee pee,冒号,斜杠,斜杠,…” - James Orr
Rick:完全同意,有很多理由要避免使用它!当你删掉所有的元音和易错字母后,你只剩下大约16个字符,这正好适合每个字符4位的情况。 - NickZoic

1
一个简单的解决方案是调用大多数语言中字符串类型上具有的getHashCode方法。将字符串设置为批准单词列表中的某个单词。然后调用gethashcode,这将是您的键。要验证它,请将其与现有单词哈希列表进行比较,并可能从列表中删除它,以便不能再次使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接