Skip to content

Instantly share code, notes, and snippets.

@megayu
Last active May 30, 2023 03:11
Show Gist options
  • Save megayu/50a6741875148613e38dd58a0a6a0972 to your computer and use it in GitHub Desktop.
Save megayu/50a6741875148613e38dd58a0a6a0972 to your computer and use it in GitHub Desktop.
id混淆算法

id混淆算法

在业务系统中,通常都存在着根据id查询详细信息的场景,比如GET /item/100,获取id为100的商品,这是最常规的做法,但不适用于对外服务,因为数字id泄露了内部信息,通过更改id可以访问其他数据,如果使用程序脚本还可以把所有数据爬下来,另外通常id是递增的,id较小通常代表创建时间早。

因而对外服务中我们需要对id做混淆,合格的算法混淆后的id通常需要达到以下几点:

  • 随机数字或字符串

  • 无特征,相邻id混淆后非递增、非相近数字或字符串

  • 双向,可逆

  • 原始id和混淆id为一对一关系

从算法实现角度来说,主要考虑:

  • 最终混淆结果无法用常规的算法解密,比如迅雷的url明显就是base64,解码后就能找到规律

  • 支持加盐,同一算法添加不同的随机数据后混淆的结果能有巨大差异,保证即使算法公开也无风险

  • 支持long类型,不少数据库尤其是分布式系统的id很可能是超出int范围的

  • 不论id长短一律输出固定长度,比如YouTube中的视频id都是11个字符

常用的混淆方式包括:

  • XOR

  • 位、字节、字符换位

  • 部分值替换

  • 乘法逆

一般要达到理想的效果需要几种方式叠加使用。

分享一种实现方式供参考 an id obfuscation algorithm to generate a string of length 25 · GitHub

使用方式如下:

IDObfuscation ob = new IDObfuscation(0x1234);
String obfuscatedId = ob.obfuscate(1);
ob.restore(obfuscatedId);

构造函数内的key为随机数,不同参数生成的同一id的结果都会不同。

obfuscate是混淆方法,restore是恢复方法。

以下介绍一下大致的实现方式:

在混淆时,首先通过swap方法对id的高低位的各32个字节进行替换,恢复时同样使用swap方法,这是从数学上通过XOR确保2次调用swap后返回结果一致。

opaque方法对数字进行混淆,即使相邻数字的结果也非雷同,采用了XOR加法乘法和移位,构造函数内的key就在这里用到,为了避免巧合正好XOR后为0,添加一个数字,方法内的几个数字并非magic number,纯属随意填写,实际该方法只要数字返回前& 0xFFFFFFFFL都能确保有效的,直接返回参数也可以,以上做法只是为了增强混淆的效果以及引入key造成算法使用上的差异性。

执行opaque方法后返回的是数字长度不定的long,并且因为java没有无符号数字,返回的数字也有可能是负数,接下来的toStr方法就是为了将long转成定长的包含a-zA-Z0-9的字符串。

一个long用字符串形式表示最多是20个字符,1个减号加上19个数字,为了定长就会涉及到padding,又需要将有效信息和padding区分,此处采用了将对应数字转成ASCII码的方式,0-9分别对应0x30-0x39,代表负数的减号对应0x2D,ASCII码内只要非这10个值都可以作为padding,这里采用0x29开始递减的19个值,19是理论上会用到的padding个数,实际swap方法返回的long值不会需要那么多padding。

目标结果的字符串的字符一共有62种可能,也就是62进制的计算,为了避免使用BigInteger,将每4个字符的ASCII码叠加后的数字做62进制运算,数字范围在0x2D313030和0x39393939之间,而62进制的4次方是0xE17810,5次方是0x369B13E0,为了统一长度避免此处再做padding,统一将每个字符减去3,确保在62的5次方的涵盖范围内,也就是说每4个数字转成5个字符,20个数字依次分5组转换完成后的字符串reverse后返回25个字符做为最终的混淆id。

恢复时就是反向操作,没什么特殊的地方。

整个实现尽可能的采用数字移位,因为这是对于计算机来说最高效的执行逻辑。

最后看一下执行效果:

原始id key=0x1233 混淆后id key=0x1234 混淆后id
1 5Ap1f8YNc65AYqW7PQoL7O9pS 8X6JV6JEoY8XpCG1lebEv4ZfX
2 32Rlv7PQoN49B8O9fw0G7QXCm 31LRX6HaWO2rqvf6Igqq8Yvi5
3 49BGY48u5H6H8Dj9h8zd1kpec 30Eyy30Euq7O91p0bslfv4ZXI
4 8XojG3zxrr5AYmK7O9lI7QW0O 9gloV2tUo47QFpEzTlIT6HaOB
5 1kYO12r7yh8ZCNJ6IPGd30nHW 9hsPU31LRb2tVDo8XYX7v4IYS
6 49BW58ZkPK0amVS8XYLi2rqzm 1kHcV2rqSn8YNUm1lNoi2r7uX
7 32Rp97Q4pz9h9F87QW8lv5OcL 3zxvr7QFkzzSNu52rZLc2rqe6
8 48Min30m9E48dhc1kpidv4Iot 1kpSDzTDOp6GkuQ30WY8v3kWa
9 5AY2l5AZJO9hJ611j0I70ciFY 7RdGZ0bst09fwbL9h8rT32RVa
10 6IxpX1kHcb8Z1zQ31K65v4IYN 5AH3132RNG9hbEG30WY9v3km3

参考

https://github.com/marekweb/opaque-id

@pfchongqing
Copy link

123

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment