Методы обфускации целочисленных идентификаторов - PullRequest
8 голосов
/ 02 апреля 2010

Я ищу простой и обратимый способ запутывания целочисленных идентификаторов. В идеале, я бы хотел, чтобы полученная в результате запутанность была не более восьми символов в длину и не была бы последовательной, а это означает, что запутывание «1» не должно выглядеть как запутывание для «2» и т. Д.

Это не означает, что оно должно быть безопасным, поэтому это не большая проблема. Кроме того, целые числа, которые я буду обфусцировать, невелики - от одного до 10000 - но я также не хочу никаких коллизий.

У кого-нибудь есть идеи для чего-то, что соответствовало бы этим критериям?

Ответы [ 10 ]

11 голосов
/ 02 апреля 2010

Если у вас есть только около 10000 целых чисел, то самым простым и надежным способом, вероятно, будет таблица соответствия между целым числом и случайно сгенерированной строкой. Либо сгенерируйте несколько случайных идентификаторов, которые соответствуют каждому целому числу, либо просто заполните их по требованию.

Таким образом, вы можете гарантировать отсутствие коллизий и не беспокоиться о шифровании, потому что расшифровывать нечего, поскольку строки не являются производными от самих целых чисел.

Вы можете реализовать его в таблице базы данных или в памяти (например, двухстороннем словаре) в зависимости от ваших потребностей.

6 голосов
/ 17 ноября 2010

Я понимаю, что об этом спрашивали 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

Я не знаю о реализации ac #, но perl равен http://search.cpan.org/perldoc?Crypt::Skip32::Base32Crockford, а две составные части для ruby ​​- https://github.com/levinalex/base32 и https://github.com/patdeegan/integer-obfuscator. Между двумя из них вы сможете перенести его на любой язык, который вам нужен.

5 голосов
/ 02 апреля 2010

Я получил идею хеширования Пирсона, которая будет работать и для произвольных входных данных, а не только для 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 для перехода между значениями и байтовыми массивами или преобразовать их в основание 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;

Это в некоторой степени безопасно, самый большой недостаток в том, что шифрование, XOR с 0, оказывается идентичностью XOR и не меняет значение (a ^ 0 == a). Таким образом, первый зашифрованный байт представляет случайную позицию этого байта. Чтобы обойти это, вы можете выбрать начальное значение для c, которое не является постоянным, на основе секретного ключа, просто запросив PRNG (после init с seed) случайный байт. Таким образом, даже с большой выборкой намного сложнее взломать шифрование, если вы не можете наблюдать за вводом и выводом.

4 голосов
/ 02 апреля 2010

XOR - хороший и быстрый способ запутывания целых чисел:

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

Это быстро, и повторное копирование с тем же номером возвращает вам оригинал! Единственная проблема в том, что если «злоумышленник» знает какое-либо из чисел, он может тривиально выяснить маску xor ..., скопировав результат с известным оригиналом!

Например, я («нападающий») теперь, когда четвертое число в этом списке - это запутанная цифра «100». Так и сделаю:

100 xor 1206 = 1234

... и теперь у меня есть маска XOR, и я могу не запутывать любое из чисел. К счастью, есть тривиальное решение этой проблемы. Алгоритмически изменить маску XOR. Например, если вам необходимо скрыть 1000 целых чисел в массиве, начните с маски XOR «1234» и увеличьте MASK на 4 для каждого числа в массиве.

2 голосов
/ 13 июня 2010

Если кому-то интересно, кто-то несколько лет назад адаптировал 32-битный блочный шифр, что особенно полезно для этой задачи.

Доступны также порты Perl и Ruby:

Если вам нужен результат в 8 символов или менее, вы можете использовать шестнадцатеричное или base64 представление.

1 голос
/ 29 июня 2016

Хороший проект для работы с библиотеками на большинстве языков: http://hashids.org/

1 голос
/ 05 апреля 2015

Обновление май 2017

Не стесняйтесь использовать (или изменять) разработанную мной библиотеку, которую можно установить через Nuget с помощью:

Install-Package Kent.Cryptography.Obfuscation

Преобразует неотрицательный идентификатор, такой как 127, в 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);

Ура, надеюсь, это может быть полезным.

0 голосов
/ 06 октября 2015

Я понимаю, что это старый пост, но я подумал, что может быть полезно опубликовать мою технику для запутывания целочисленных идентификаторов

Минусы: использует более 8 символов, подходит только для значений идентификаторов до 33 миллионов

Плюсы: не требуется ключ для удаления запутывания, дружественный URL / cookie, генерирует разные значения каждый раз, что затрудняет его взлом, отсутствие коллизий, включает функцию контрольной суммы для устранения случайных / грубых попыток взлома Одна из проблем, которые не рассматриваются в этом посте, это люди, пытающиеся "почистить" ваш сайт. Если я вижу URL, заканчивающийся на id = 123, я знаю, что могу попробовать 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;
        }
    }

}
0 голосов
/ 02 апреля 2010

Вы можете поиграть с битовыми шаблонами числа - например, вращаться и менять биты. Это даст вам возможность перемещаться между числом, скажем, 26 бит, и другим числом из 26 бит, что не будет сразу очевидно для человека-наблюдателя. Хотя это ни в коем случае не "безопасно".

0 голосов
/ 02 апреля 2010

Просто получите MD5 / SHA1-хэш целочисленного байтового представления. Вы гарантированно не получите столкновения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...