混淆一个ID
我正在寻找一种方法来encryption/混淆一个整数ID到另一个整数。 更确切地说,我需要一个函数int F(int x)
,所以
- (x)是一一对应的(如果x!= y,F(x)!= F(y))
- 给定F(x),很容易找出x – 所以F不是散列函数
- 给定x和F(x)很难/不可能找出F(y),像
x ^ 0x1234
这样的东西是行不通的
为了清楚起见,我并不是在寻找一个强大的encryption解决scheme,这只是混淆。 想象一个像example.com/profile/1
等url的web应用程序。个人资料本身并不是秘密的,但是我想阻止偶然的voyeurs一个接一个地查看/获取所有configuration文件,所以我宁愿把它们隐藏在像example.com/profile/23423
东西上。虽然数据库存储的令牌可以很容易地完成这项工作,但我很好奇,如果有一些简单的math可用这个。
我不清楚的一个重要的要求是结果应该看起来是“随机的”,即给定序列x,x+1,...,x+n
, F(x),F(x+1)...F(x+n)
不应该形成任何forms的进展。
使用两种或三种简单方法的组合来混淆它:
- XOR
- 洗牌的个人比特
- 转换为模块化表示(D.Knuth,第2卷,第4.3.2章)
- 在每个子集中select32个(或64个)重叠的比特子集和XOR比特(子集的奇偶校验比特)
- 用可变长度数字系统和随机数字表示
- select一对彼此相乘的奇数整数
x
和y
(模2 2 32 ),然后乘以x
混淆并乘以y
以恢复,所有乘法都是模2 32 (来源: “乘法的实际应用反向“由Eric Lippert )
可变长度数字系统方法本身并不服从你的“进展”要求。 它总是产生短的算术进展。 但是当与其他方法结合使用时,效果会很好。
模块化表示方法也是如此。
以下是这些方法中的3个的C ++代码示例。 随机比特示例可以使用一些不同的掩码和距离来更加不可预测。 其他两个例子对于小数字是有好处的(只是为了给出这个想法)。 应该扩展它们以适当地混淆所有的整数值。
// *** Numberic system base: (4, 3, 5) -> (5, 3, 4) // In real life all the bases multiplied should be near 2^32 unsigned y = x/15 + ((x/5)%3)*4 + (x%5)*12; // obfuscate unsigned z = y/12 + ((y/4)%3)*5 + (y%4)*15; // restore // *** Shuffle bits (method used here is described in D.Knuth's vol.4a chapter 7.1.3) const unsigned mask1 = 0x00550055; const unsigned d1 = 7; const unsigned mask2 = 0x0000cccc; const unsigned d2 = 14; // Obfuscate unsigned t = (x ^ (x >> d1)) & mask1; unsigned u = x ^ t ^ (t << d1); t = (u ^ (u >> d2)) & mask2; y = u ^ t ^ (t << d2); // Restore t = (y ^ (y >> d2)) & mask2; u = y ^ t ^ (t << d2); t = (u ^ (u >> d1)) & mask1; z = u ^ t ^ (t << d1); // *** Subset parity t = (x ^ (x >> 1)) & 0x44444444; u = (x ^ (x << 2)) & 0xcccccccc; y = ((x & 0x88888888) >> 3) | (t >> 1) | u; // obfuscate t = ((y & 0x11111111) << 3) | (((y & 0x11111111) << 2) ^ ((y & 0x22222222) << 1)); z = t | ((t >> 2) ^ ((y >> 2) & 0x33333333)); // restore
你希望转型是可逆的,而不是明显的。 这听起来像一个encryption,在一个给定的范围内,并在同一范围内产生一个不同的数字。 如果您的范围是64位数字,那么使用DES。 如果你的范围是128位数字然后使用AES。 如果你想要一个不同的范围,那么你最好的select可能是Hasty Pudding cipher ,它被devise来处理不同的块大小,并且数量范围不能很好地整合到块中,例如100,000到999,999。
在安全性方面,混淆并不充分。
但是,如果你试图阻止偶然的旁观者,我会推荐两种方法的组合:
- 一个私钥,通过将它们结合在一起而与id相结合
- 在键被应用之前和之后旋转一定量的比特
这是一个例子(使用伪代码):
def F(x) x = x XOR 31415927 # XOR x with a secret key x = rotl(x, 5) # rotate the bits left 5 times x = x XOR 31415927 # XOR x with a secret key again x = rotr(x, 5) # rotate the bits right 5 times x = x XOR 31415927 # XOR x with a secret key again return x # return the value end
我没有testing过,但我认为这是可逆的,应该是快速的,而不是太容易梳理出来的方法。
我发现这个特定的Python / PHP代码非常有用:
我用分组密码写了一篇关于安全排列的文章,它应该满足你所说的要求。
但是,我build议,如果你想很难猜出标识符,你应该首先使用它们:生成UUID,首先将它们用作logging的主键 – 不需要能够转换成“真实”的ID。
做任何事情的身份证,不会摧毁他们。 例如:
- 旋转值
- 使用查找来replace值的某些部分
- 具有一定的价值
- 交换位
- 交换字节
- 反映整个价值
- 镜像价值的一部分
- … 动用你的想象力
对于解密,按照相反的顺序进行。
创build一个程序,将'encryption'给你一些有趣的值,并把它们放在一个表,你可以检查。 有相同的程序testing你的encryption/解密程序与您想要在系统中的所有设置的值。
将以上列表中的东西添加到例程中,直到您的数字看起来适合您。
对于其他任何事情,获得一本书的副本。
不知道你需要多么“困难”,有多快,或多less内存使用。 如果你没有内存限制,你可以列出所有的整数,洗牌,并使用该列表作为映射。 但是,即使是4字节的整数,你也需要大量的内存。
然而,这可能会变得更小,所以不是映射所有整数,而是只映射2(或最坏情况1)字节,并将其应用于整数中的每个组。 因此,使用2个字节的整数应该是(group1)(group2),您将通过随机映射映射每个组。 但是这意味着如果你只改变group2,那么group1的映射将保持不变。 这可以通过将不同的位映射到每个组来“固定”。
所以,*(group2)可能是(位14,12,10,8,6,4,2,0)所以,加1会改变group1和group2 。
不过,这只是安全的隐瞒,任何人都可以提供数字到你的function(即使你保持秘密function)可以很容易地弄清楚。
生成一个私有的对称密钥,以便在您的应用程序中使用,并使用它对您的整数进行encryption。 这将满足所有三个要求,包括最困难的#3:为了打破你的计划,你需要猜测你的钥匙。
这里描述的内容似乎与单向函数相反:很容易反转,但是很难应用。 一种select是使用标准的现成的公钥encryptionalgorithm,在该algorithm中,您可以修复您保密的(秘密的,随机select的)公钥以及您与世界共享的私钥。 这样,你的函数F(x)将是使用公钥的x的encryption。 然后,您可以使用私钥解密密钥轻松地将F(x)解密回x。 注意,公钥和私钥的作用在这里被颠倒过来 – 你把私钥交给每个人,这样他们就可以解密这个函数,但是把公钥保密在你的服务器上。 那样:
- 这个函数是一个双射,所以它是可逆的。
- 给定F(x),x是有效计算的。
- 给定x和F(x),从y计算F(y)是非常困难的,因为没有公钥(假设你使用密码强的encryptionscheme),没有可行的方法来encryption数据,即使私钥解密密钥是已知的。
这有很多优点。 首先,你可以放心,encryption系统是安全的,因为如果你使用像RSA这样的公认的algorithm,那么你不必担心意外的不安全感。 其次,现在已经有图书馆来做这件事了,所以你不需要编码太多,而且可以免受旁道攻击。 最后,你可以让任何人都可以去倒F(x)而没有任何人能够计算F(x)。
一个细节 – 你绝对不应该在这里使用标准的inttypes。 即使使用64位整数,攻击者也可能只有很less的组合才能尝试对所有事物进行反转,直到他们find某个y的encryption函数F(y),即使它们没有密钥。 我会build议使用类似于512位的值,因为即使是科幻小说的攻击也无法蛮力。
希望这可以帮助!
如果xor
对于所有的东西都是可以接受的,但是给出x
和F(x)
推导F(y)
,那么我认为你可以用盐来做到这一点。 首先select一个秘密的单向函数。 例如S(s) = MD5(secret ^ s)
。 那么F(x) = (s, S(s) ^ x)
,其中s
是随机select的。 我把它写成了一个元组,但是你可以把这两个部分组合成一个整数,例如F(x) = 10000 * s + S(s) ^ x
。 解密再次提取盐,并使用F'(F(x)) = S(extract s) ^ (extract S(s)^x)
。 给定x
和F(x)
你可以看到s
(虽然它有点混淆),你可以推断S(s)
但是对于其他用户y
用不同的随机salt t
知道F(x)
的用户找不到S(t)
。