【问题标题】:Integer ID obfuscation techniques整数 ID 混淆技术
【发布时间】:2011-02-03 15:52:26
【问题描述】:

我正在寻找一种简单且可逆的方法来混淆整数 ID。理想情况下,我希望得到的混淆长度最多为八个字符且不连续,这意味着“1”的混淆看起来与“2”的混淆完全不同,依此类推。

这并不意味着以任何方式安全,所以这不是一个大问题。此外,我要混淆的整数并不大 - 介于 1 到 10,000 之间 - 但我也不想要任何冲突。

有人对符合此标准的东西有任何想法吗?

【问题讨论】:

  • 一种非顺序映射(想不出一个好的映射)和 base64 编码/解码(对于 32 位 int 产生 6 个字符,忽略 = 字符)将是一种不使用查找表的解决方案。

标签: c# encryption obfuscation


【解决方案1】:

如果您只有大约 10,000 个整数,那么最简单和最可靠的方法可能是整数和随机生成的字符串之间的映射表。要么预先生成一堆与每个整数对应的随机标识符,要么按需填写。

这样您可以保证没有冲突,并且不必担心加密,因为没有什么可以解密,因为字符串不是从整数本身派生的。

您可以根据需要在数据库表或内存(例如双向字典)中实现它。

【讨论】:

  • 我最终按照约翰·莱德格伦在下面提出的方式进行了操作,这也是同样的想法,只是更加充实了一点。谢谢!
【解决方案2】:

只需获取整数字节表示的 MD5/SHA1 哈希。保证不会发生碰撞。

【讨论】:

  • 他希望数字的长度最多为 8 个字符,对于这两个字符来说都太短了。
  • 另外:用于非加密需求的加密哈希是多余的,它们的处理时间太长了。
  • @Cosmin:您的评论可能有点过于笼统。一些加密哈希非常快。据报道,在某些架构上,某些加密哈希实际上比简单的 CRC32 更快。
  • 这也不可逆,afaik。
【解决方案3】:

我从 Pearson 散列中得出了一个想法,它也适用于任意输入,而不仅仅是 32 位整数。我不知道这是否与格雷格的答案完全相同,但我无法理解他的意思。但我所知道的是这里的内存需求是不变的。不管输入有多大,这仍然是一个可靠的混淆/加密技巧。

郑重声明,此方法不是散列,也没有冲突。这是混淆字节字符串的完美方法。

为此,您需要一个密钥 _encryptionTable,它是包含范围 0..255 的随机排列。您可以使用它来随机播放字节。为了使它很难反转,它使用 XOR 来混合字节字符串。

public byte[] Encrypt(byte[] plaintext)
{
    if (plaintext == null)
    {
        throw new ArgumentNullException("plaintext");
    }
    byte[] ciphertext = new byte[plaintext.Length];
    int c = 0;
    for (int i = 0; i < plaintext.Length; i++)
    {
        c = _encryptionTable[plaintext[i] ^ c];
        ciphertext[i] = (byte)c;
    }
    return ciphertext;
}

然后您可以使用 BitConverter 在值和字节数组之间进行转换,或者将一些转换为 base 64 或 32 以获得文本表示。如果这很重要,Base 32 编码可以是 URL 友好的。解密就像通过计算 _encryptionTable 的倒数来反转操作一样简单。

    public byte[] Decrypt(byte[] ciphertext)
    {
        if (ciphertext == null)
        {
            throw new ArgumentNullException("ciphertext");
        }
        byte[] plaintext = new byte[ciphertext.Length];
        int c = 0;
        for (int i = 0; i < ciphertext.Length; i++)
        {
            plaintext[i] = (byte)(_decryptionTable[ciphertext[i]] ^ c);
            c = ciphertext[i];
        }
        return plaintext;
    }

如果您正在处理一个 32 位整数并且只关心大于或等于 0 的数字,那么您还可以做其他有趣的事情,这使得猜测混淆的数字变得更加困难。

我还使用一个秘密词来植入一个伪数生成器,并使用它来设置初始排列。这就是为什么我可以通过知道我用什么秘密词来创造每一件事来简单地获得价值。

var mt = new MersenneTwister(secretKey.ToUpperInvariant());
var mr = new byte[256];
for (int i = 0; i < 256; i++)
{
    mr[i] = (byte)i;
}
var encryptionTable = mt.NextPermutation(mr);
var decryptionTable = new byte[256];
for (int i = 0; i < 256; i++)
{
    decryptionTable[encryptionTable[i]] = (byte)i;
}
this._encryptionTable = encryptionTable;
this._decryptionTable = decryptionTable;

这有点安全,这里最大的缺陷是加密,与 0 的异或,恰好是异或的标识,并且不会改变值(a ^ 0 == a)。因此,第一个加密字节表示该字节的随机位置。要解决此问题,您可以根据密钥选择c 的初始值,该值不是恒定的,只需向 PRNG(使用种子初始化后)询问随机字节即可。这样一来,只要您无法观察输入和输出,即使是大样本也很难破解加密。

【讨论】:

    【解决方案4】:

    您可以使用数字的位模式 - 例如旋转和交换位。这将为您提供一种在多个 26 位和另一个 26 位之间移动的方法,这对于人类观察者来说不会立即显而易见。虽然它绝不是“安全的”。

    【讨论】:

    • 我也尝试过解决这个问题,但无法想出使结果不连续的方法。
    【解决方案5】:

    XOR 是一种很好且快速的整数混淆方法:

    1 xor 1234 = 1235
    2 xor 1234 = 1232
    3 xor 1234 = 1233
    100 xor 1234 = 1206
    120 xor 1234 = 1194
    

    速度很快,再次用相同的数字进行异或运算可以返回原始数据!唯一的麻烦是,如果“攻击者”知道任何数字,他们可以通过对已知原始结果进行异或运算来轻松找出异或掩码!

    例如,我(“攻击者”)现在该列表中的第 4 个数字是经过混淆的“100”。所以我会这样做:

    100 xor 1206 = 1234
    

    ...现在我有了 XOR 掩码,我可以对任何数字进行非混淆处理。令人高兴的是,这个问题有简单的解决方案。以算法方式更改 XOR 掩码。例如,如果您需要对数组中的 1000 个整数进行混淆处理,请从“1234”的 XOR 掩码开始,并为数组中的每个数字增加 4 的 MASK。

    【讨论】:

    • 我想过这个想法,但因为在你的例子中 1235(1)和 1232(2)非常接近,所以把它扔掉了。还是谢谢!
    • 请注意,如果攻击者有一些生成新 ID 的方法,这可能不是一个好主意。例如,如果创建一条新消息提供了一个 ID,他们可以快速创建两条消息并获得几乎(或完全)连续的 ID,并轻松确定 XOR 常数。
    【解决方案6】:

    如果其他人感兴趣,几年前有人采用了一种 32 位分组密码,这对这项任务特别有用。

    上面还有 Perl 和 Ruby 端口可用:

    如果您需要 8 个字符或更少的结果,您可以使用 hex 或 base64 表示。

    【讨论】:

      【解决方案7】:

      我意识到这是 7 个月前提出的问题,所以您现在已经找到了解决方案,但我遇到的解决方案是 Skip32/Skipjack 密码 + base32 编码的组合。 perl 示例(因为这是我所知道的)显示:

      use Crypt::Skip32::Base32Crockford;
      my $key    = pack( 'H20', "112233445566778899AA" ); # Always 10 bytes!
      my $cipher = Crypt::Skip32::Base32Crockford->new($key);
      my $b32    = $cipher->encrypt_number_b32_crockford(3493209676); # 1PT4W80
      my $number = $cipher->decrypt_number_b32_crockford('1PT4W80'); # 3493209676
      

      我不知道 c# 实现,但是 perl 的实现是 http://search.cpan.org/perldoc?Crypt::Skip32::Base32Crockford,而 ruby​​ 的两个组成部分是 https://github.com/levinalex/base32https://github.com/patdeegan/integer-obfuscator。在他们两个之间,您应该能够将其移植到您需要的任何语言。

      【讨论】:

        【解决方案8】:

        2017 年 5 月更新

        随意使用(或修改)我开发的库,可通过 Nuget 安装:

        安装包 Kent.Cryptography.Obfuscation

        这会将诸如 127 之类的非负 id 转换为 8 个字符的字符串,例如xVrAndNb,然后返回(有一些可用选项可以在每次生成序列时随机化序列)。

        示例用法

        var obfuscator = new Obfuscator();
        string maskedID = obfuscator.Obfuscate(15);
        

        完整文档位于:Github


        旧答案

        只是为旧答案增加多样性。也许有人会需要它。这是我以前做的一个混淆类。

        Obfuscation.cs - Github

        您可以通过以下方式使用它:

        Obfuscation obfuscation = new Obfuscation();
        string maskedValue = obfuscation.Obfuscate(5);
        int? value = obfuscation.DeObfuscate(maskedValue);
        

        干杯,希望它可以有用。

        【讨论】:

        • 这里的链接坏了。
        • 很抱歉回复晚了。链接已更新。
        【解决方案9】:

        我意识到这是一篇旧帖子,但我认为发布我的混淆整数 id 的技术可能会有所帮助

        缺点:确实使用超过 8 个字符,仅适用于 3300 万以下的 id 值

        优点:不需要密钥去混淆,URL/cookie 友好,每次生成不同的值,这使得破解更加困难,没有冲突,包括校验和功能,以消除随机/蛮力破解尝试(上述帖子未解决的一个问题是人们试图“抓取”您的网站。如果我看到一个以 id=123 结尾的 url,我知道我可以尝试 id=124 等...以获取更多数据,这就是为什么一些 XOR 示例可能不是一个好主意)

        我建议稍微调整一下(我已经为我做过),因为我认为您不应该使用公开发布的混淆技术,但这是一个很好的起点。

        编码愉快!

            public static string ObfuscateId(int id)
            {
                try
                {
                    string rtn;
                    int sid = id + 279;
                    int xm = sid * 3;
                    int xl = xm.ToString().Length + 10;
                    string sc = xl.ToString().Substring(1, 1);
                    string fc = xl.ToString().Substring(0, 1);
                    string csum = sid.ToString().Substring(sid.ToString().Length - 3);
                    rtn = Guid.NewGuid().ToString().Replace("-", "").ToLower();
                    rtn = sc + rtn.Substring(2, 26) + fc;
                    rtn = rtn.Remove(4, 3).Insert(4, csum);
                    rtn = rtn.Remove(xl, (xl - 10)).Insert(xl, xm.ToString());
                    rtn = rtn.Replace('1', 'g');
                    rtn = rtn.Replace('2', 'h');
                    rtn = rtn.Replace('3', 'i');
                    rtn = rtn.Replace('4', 'w');
                    rtn = rtn.Replace('5', 'y');
                    rtn = rtn.Replace('6', 'u');
                    rtn = rtn.Replace('7', 'z');
                    rtn = rtn.Replace('8', 'l');
                    rtn = rtn.Replace('9', 'v');
                    rtn = rtn.Replace('0', 'n');
                    rtn = rtn.Replace('c', 'j');
                    rtn = rtn.Replace('d', 'p');
                    rtn = rtn.Replace('f', 'q');
        
                    return rtn.ToUpper();
                }
                catch
                {
                    return "ERROR BAD ID";
                }
            }
        
            public static int DeObfuscateId(string obtxt)
            {
                try
                {
                    string rtn;
                    int id;
        
                    rtn = obtxt.ToLower();
                    rtn = rtn.Replace('g', '1');
                    rtn = rtn.Replace('h', '2');
                    rtn = rtn.Replace('i', '3');
                    rtn = rtn.Replace('w', '4');
                    rtn = rtn.Replace('y', '5');
                    rtn = rtn.Replace('u', '6');
                    rtn = rtn.Replace('z', '7');
                    rtn = rtn.Replace('l', '8');
                    rtn = rtn.Replace('v', '9');
                    rtn = rtn.Replace('n', '0');
                    rtn = rtn.Replace('j', 'c');
                    rtn = rtn.Replace('p', 'd');
                    rtn = rtn.Replace('q', 'f');
        
                    string sc = rtn.Substring(0, 1);
                    string fc = rtn.Substring(rtn.Length - 1);
                    int xl = int.Parse(fc + sc);
                    int mv = int.Parse(rtn.Substring(xl, (xl - 10)));
                    int sid = mv / 3;
                    id = sid - 279;
                    string csum = sid.ToString().Substring(sid.ToString().Length - 3);
                    string xsum = rtn.Substring(4, 3);
        
                    if (csum!=xsum)
                    {
                        return -99999;
                    }
        
                    return id;
                }
                catch
                {
                    return -99999;
                }
            }
        
        }
        

        【讨论】:

          【解决方案10】:

          用大多数语言的库来处理这个问题的好项目:http://hashids.org/

          【讨论】:

            猜你喜欢
            • 2018-07-05
            • 2011-01-24
            • 2012-10-04
            • 1970-01-01
            • 2012-07-18
            • 2015-03-12
            • 1970-01-01
            • 2019-02-19
            • 2023-03-29
            相关资源
            最近更新 更多