1-1 сопоставления для обфускации id - PullRequest
11 голосов
/ 11 января 2009

Я использую последовательные идентификаторы в качестве первичных ключей, и есть случаи, когда я не хочу, чтобы эти идентификаторы были видны пользователям, например, я мог бы хотеть избегать URL-адресов вроде? Invoice_id = 1234, которые позволяют пользователям угадывать, сколько Счета системы в целом выдают.

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

  1. Необходимо выделить дополнительное поле базы данных. Я знаю, что мог бы использовать GUID в качестве своего первичного ключа, но мои целочисленные PK с автоинкрементом подходят для большинства целей, и я не хочу их менять.

  2. Необходимо подумать о возможности коллизий хеша / GUID. Я полностью согласен со всеми доводами о том, что коллизии GUID столь же вероятны, как и самовозгорание, или что-то в этом роде, но не обращая внимания на исключительные случаи, потому что они исключительные, идут против всего, чему меня учили, и продолжают беспокоить меня, даже когда я знаю, Я должен быть более обеспокоен другими вещами.

  3. Я не знаю, как безопасно обрезать идентификаторы на основе хеш-функции, поэтому даже если мои личные идентификаторы имеют размер 16 или 32 бита, я застрял с сгенерированными 128-битными идентификаторами, которые создают неудобства в URL-адресах.

Я заинтересован в отображении 1-1 диапазона идентификаторов, растягиваемых или сжимаемых, чтобы, например, 16-разрядные идентификаторы были сопоставлены с 16-разрядными идентификаторами, 32-разрядные идентификаторы сопоставлены с 32-разрядными идентификаторами и т. Д., И это остановит кто-то попытался угадать общее количество выделенных идентификаторов или скорость распределения идентификаторов за период.

Например, если мои идентификаторы пользователя являются 16-битными целыми числами (0..65535), то примером преобразования, которое несколько запутывает распределение идентификаторов, является функция f (x) = (x mult 1001) mod 65536. внутренняя последовательность идентификаторов 1, 2, 3 становится общедоступной последовательностью идентификаторов 1001, 2002, 3003. С дополнительным уровнем запутывания от преобразования базы, например, к базе 36, последовательность становится 'rt', '1jm', '2bf ». Когда система получает запрос к url? Userid = 2bf, она конвертируется из базы 36 в 3003 и применяет обратное преобразование g (x) = (x mult 1113) mod 65536, чтобы вернуться к внутреннему id = 3.

Подобной схемы достаточно, чтобы остановить случайное наблюдение случайными пользователями, но ее легко решить кто-то, кто достаточно заинтересован, чтобы попытаться разгадать ее. Кто-нибудь может предложить что-то более сильное, но легко реализуемое в PHP без специальных библиотек? Это приближается к схеме шифрования по принципу «сворачивай свой собственный», так что, может быть, существует подходящий алгоритм шифрования, который широко доступен и имеет свойство растягиваемости, упомянутое выше?

РЕДАКТИРОВАТЬ: Отступив немного назад, некоторые обсуждения в codinghorror о выборе из трех видов ключей - суррогат (основанный на guid), суррогат (основанный на целых числах), натуральный. В этих терминах я пытаюсь скрыть целочисленный суррогатный ключ от пользователей, но я ищу что-то сжатое, что делает URL-адреса не слишком длинными, что я не знаю, как сделать со стандартным 128-битным GUID , Иногда, как рекомендует принцесса-комментатор ниже, проблему можно обойти естественным ключом.

РЕДАКТИРОВАТЬ 2 / РЕЗЮМЕ:

  • Учитывая ограничения вопроса, который я задал (растяжимость, обратимость, простота реализации), наиболее подходящим решением на данный момент представляется запутывание на основе XOR, предложенное Someone и Breton.
  • С моей стороны было бы безответственно предполагать, что я могу достичь чего-то большего, чем запутывание / безопасность за счет безвестности. Знание того, что это целочисленная последовательность, - это, вероятно, шпаргалка, которой любой компетентный злоумышленник сможет воспользоваться.
  • Я еще немного подумал над идеей дополнительного поля базы данных. Одним из преимуществ дополнительного поля является то, что оно делает его более простым для будущих программистов, которые пытаются ознакомиться с системой, просматривая базу данных. В противном случае им пришлось бы копаться в исходном коде (или документации, хм), чтобы выяснить, как запрос к заданному URL-адресу разрешается для данной записи в базе данных.
  • Если я разрешу дополнительное поле базы данных, то некоторые другие предположения в вопросе станут неактуальными (например, преобразование не должно быть обратимым). Это становится другим вопросом, поэтому я оставлю это там.

Ответы [ 8 ]

7 голосов
/ 11 января 2009

Я считаю, что простое шифрование XOR лучше всего подходит для обфускации URL. Вы можете продолжать использовать любой серийный номер без изменений. Дальнейшее шифрование XOR не увеличивает длину строки источника. Если ваш текст 22 байта, зашифрованная строка тоже будет 22 байта. Это не так просто, как угадать как гниль 13, но не так тяжело, как DSE / RSA.

Найдите в сети шифрование PHP XOR, чтобы найти некоторую реализацию. Первый найденный мной здесь .

3 голосов
/ 11 января 2009

Я сам поиграл с подобными вещами, по-своему любительски, и пришел к некоему странному алгоритму шифрования чисел, включающему смешанные радиусы. В основном у меня есть функция, которая отображает число от 0-N до другого числа в диапазоне 0-N. Для URL я затем сопоставлю это число с парой английских слов. (слова легче запомнить).

Упрощенная версия того, что я делаю, без смешанных радикалов: у вас есть число, которое составляет 32 бита, поэтому заранее, у вас есть пароль длиной 32 бита, и XOR с вашим входным номером. Затем перемешайте биты в определенном порядке. (возможно, на основании вашего пароля).

Хорошая вещь об этом

  1. Никаких столкновений, если вы каждый раз перетасовываете и перемешиваете одинаково
  2. Нет необходимости хранить обфусцированные ключи в базе данных
  3. Все еще пользуйтесь заказанным IDS для внутренних целей, поскольку вы можете полностью изменить запутывание
  4. Вы можете повторить операцию несколько раз, чтобы получить более запутанные результаты.

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

2 голосов
/ 11 января 2009

Я видел этот вопрос вчера: как reddit генерирует идентификатор алфавита

Я думаю, что это достаточно хороший метод (и особенно умный)

он использует Python

def to_base(q, alphabet):
    if q < 0: raise ValueError, "must supply a positive integer"
    l = len(alphabet)
    converted = []
    while q != 0:
        q, r = divmod(q, l)
        converted.insert(0, alphabet[r])
    return "".join(converted) or '0'

def to36(q):
    return to_base(q, '0123456789abcdefghijklmnopqrstuvwxyz')
2 голосов
/ 11 января 2009

Возможно, вам будет полезно вернуться к идее использования GUID, поскольку вы можете создавать идентификаторы GUID таким образом, чтобы не было конфликтов.

Посетите страницу Википедии по GUID - алгоритм «Тип 1» использует в качестве входных данных как MAC-адрес ПК, так и текущую дату / время. Это гарантирует, что столкновения просто невозможны.

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

1 голос
/ 01 июня 2011

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

prandmap(x) return x * nextPrime(N) % N

это создаст функцию, которая повторяет (или имеет точку) каждый N, число не создается дважды, пока x = N + 1 Он всегда начинается с 0, но после этого является псевдослучайным.

1 голос
/ 11 января 2009

Добавьте поле char (10) в таблицу заказов ... назовите его order_number.

После создания нового заказа случайным образом сгенерируйте целое число от 1 ... 9999999999. Проверьте, существует ли он в базе данных по номеру order_number. Если нет, обновите свою последнюю строку с этим значением. Если он существует, выберите другое число наугад.

Используйте 'order_number' для общедоступных URL-адресов, возможно, всегда с нулями.

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

Провёл от здесь .

ОБНОВЛЕНО Как и в случае использования подхода GUID, описанного Беваном, если столбец ограничен как уникальный, вам не нужно его использовать. Я полагаю, что это не отличается от использования GUID, за исключением того, что клиенту и отделу обслуживания клиентов будет проще обращаться к заказу.

0 голосов
/ 11 января 2009

Из вашего описания лично я бы начал работать с любой доступной стандартной библиотекой шифрования (я программист на Java, но я предполагаю, скажем, базовая библиотека шифрования AES для PHP):

  • в базе данных, просто ключевые вещи, как обычно
  • всякий раз, когда вам нужно передать ключ клиенту или из него, используйте довольно надежную стандартную систему шифрования (например, AES) для преобразования ключа в / из цепочки мусора. В качестве обычного текста используйте (скажем) 128-байтовый буфер, содержащий: (скажем) 4-байтовый ключ, 60 случайных байтов, а затем 64-байтовый хэш среднего качества из предыдущих 64 байтов (см. Числовые рецепты для пример) - очевидно, когда вы получаете такую ​​строку, вы дешифруете ее, а затем проверяете, совпадает ли хеш, прежде чем попасть в БД. Если вы немного более параноидальны, отправьте зашифрованный AES буфер случайных байтов с вашим ключом в произвольной позиции, плюс безопасный хэш этого буфера в качестве отдельного параметра. Первый вариант - это, вероятно, разумный компромисс между производительностью и безопасностью для ваших целей, особенно в сочетании с другими мерами безопасности.
  • в день, когда вы обрабатываете так много счетов в секунду, что AES-шифрование их в пути слишком дорого, выйдите и купите себе большой толстый сервер с большим количеством процессоров для празднования.

Кроме того, если вы хотите скрыть, что переменная является идентификатором счета-фактуры, вы можете назвать ее как-то иначе, чем «invoice_id».

0 голосов
/ 11 января 2009

Честно говоря, шифрование / дешифрование данных строки запроса - плохой подход к этой проблеме. Самым простым решением является отправка данных с использованием POST вместо GET. Если пользователи нажимают на ссылки с данными строки запроса, вам придется прибегнуть к некоторым javascript hacks для отправки данных по POST (помните о доступности для пользователей с отключенным Javascript). Это не мешает пользователям просматривать исходный код, но, по крайней мере, сохраняет чувствительность от индексации поисковыми системами, если предположить, что данные, которые вы пытаетесь скрыть, действительно очень чувствительны.

Другой подход заключается в использовании естественного уникального ключа. Например, если вы выставляете счета клиентам ежемесячно, то «yyyyMM [customerID]» однозначно идентифицирует конкретный счет для конкретного пользователя.

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