码洞
首发于码洞
有趣的Python开源库之Hashids

有趣的Python开源库之Hashids

Hashids是一个非常小巧的开源库,它用来把数字编码成一个随机字符串。它不同于md5这种算法这种单向映射,Hashids除了编码还会解码。

拿论坛来说,一般帖子在数据库里的id都是顺序递增的,但是你可能不想在url上直接把id暴露出来,以免爬虫直接遍历id爬取你的内容,给你带来损失。那现在你就可以使用Hashids把这个id搞乱,让它失去顺序性,无法直接遍历,这样就可以直接提高了爬虫的门槛。著名的Youtube网站就是这么做的。

我们来看看它怎么使用 首先安装一下 pip install hashids

我们看到hashids不仅可以编码一个整数,还可以一次编码多个整数。解码的时候不需要对字符串进行分割,可以直接解码成多个整数。这在存储一个帖子的相关帖子时给我们多了一种选择,一般我们使用json打包多个帖子id放在帖子表的一个字段里,现在我们就可以使用hashids把它们编码成一个字符串塞进去了,可以节省一定的存储空间。不过,除此之外我想不到编码多个整数有什么其它的用途了。

hashids需要提供一个salt值,相当于编解码的私钥,别人不知道你的私钥,就无法编码出对应的帖子的展现key,也无法通过url上的展现key解码出对应的帖子id。所以想直接遍历你的帖子服务那就做不到了。

现在我们试试随便提供字符串,对它进行解码会怎样

我们看到这些字符串都是非法,所以hashids无法解码出对应的整数。

下面我们对一段连续整数进行编码

可以发现编码之后的值在直觉上是没有任何规律可言的。

鉴于hashids是如此的小巧,笔者随后对它的源代码进行了一番研究。首先看看它的构造器

我们发现可以设置编码后字符串的最小长度。如果你不设置这个最小长度,对于一个从0开始的自增id,编码出来的字符串长度一开始只有2位,但是随着id的增长,编码后的长度也越来越大,但是最终这个长度值越来越稳定,因为位数越大可以表达的数值就越多。如果我们设置了这个最小长度,在id没有恐怖的快速增长的情况下,那么编码出的长度一开始就是非常稳定的。

然后我们还可以设置映射字典。默认是base64[26+26+10]编码,如果你不喜欢大小写敏感,可以改成base36[26+10]编码,甚至可以改成火星文,如果你真这么无聊的话。

注意火星文的字典必须是unicode类型。不然你编码得到的不是火星文,而是乱码。具体实现算法我就跳过了,有点复杂,我就不讲了。

然后它还支持对16进制字符串进行编解码,看来mongodb的id也可以纳入进来了。

最后算法的实现原理,仔细研究了一下,有点复杂,很难三言两语讲清楚,有兴趣的话读者还是自己阅读这篇文章吧。算法的作者不保证安全性,不建议将hashids用在安全领域。

维基百科提到一个理想的hash算法需要满足下面3个特性

  1. 正向计算hash很容易
  2. 反向破解hash极其困难
  3. hash值碰撞概率极小

hashids满足第1和第3条,正向计算hash很快,hash值完全没有碰撞,保证了唯一性。如果知道salt值,还可以逆向通过hash值计算出原值。那第2条是否满足取决于你的salt秘钥有多容易被攻击者拿到。如果你的salt秘钥来自于常用字典单词,那攻击者可以通过彩虹字典快速将秘钥破解。你把秘钥好好保护,设置的随机一点长度长一些,安全性还是可以得到保障的。最后你可千万别把秘钥搞丢了,一旦搞丢了,就意味着所有帖子的展现id需要重新计算一边,变的跟以前不一样了,搜索引擎收录的所有网页就失效了,别人外链过来的地址全都打不开,这无疑会给你的网站带来很大的损失。

编辑于 2018-01-06

文章被以下专栏收录