Шифрование / хеширование паролей в виде простого текста в базе данных - PullRequest
62 голосов
/ 13 ноября 2008

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

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

Любая помощь приветствуется.

Редактировать: Извините, если мне неясно, я хотел спросить, какова будет ваша процедура шифрования / хеширования паролей, а не конкретные методы шифрования / хеширования.

Должен ли я просто:

  1. Сделать резервную копию БД
  2. Обновить логин / обновить пароль
  3. Через несколько часов просмотрите все записи в таблице пользователей, хэширующие пароль и заменяющие каждую
  4. Проверка того, что пользователи все еще могут входить / обновлять пароли

Полагаю, меня больше беспокоит огромное количество пользователей, поэтому я хочу убедиться, что я делаю это правильно.

Ответы [ 16 ]

49 голосов
/ 13 ноября 2008

EDIT (2016): используйте Argon2 , scrypt , bcrypt или PBKDF2 в указанном порядке предпочтения. Используйте настолько большой коэффициент замедления, насколько это возможно для вашей ситуации. Использовать проверенную существующую реализацию. Убедитесь, что вы используете правильную соль (хотя библиотеки, которые вы используете, должны убедиться в этом за вас).


Когда вы хэшируете пароли, используйте НЕ ИСПОЛЬЗУЙТЕ PLAIN MD5 .

Используйте PBKDF2 , что в основном означает использование случайной соли для предотвращения радужного стола атак и повторение (повторное хеширование) достаточное количество раз для замедления хеширования - не так много, чтобы Ваше приложение занимает слишком много времени, но этого достаточно, чтобы злоумышленник, перебивший большое количество различных паролей, заметит

Из документа:

  • Выполните итерацию как минимум 1000 раз, предпочтительно больше - время вашей реализации, чтобы увидеть, сколько итераций для вас выполнимо.
  • 8 байт (64 бита) соли достаточно, и случайное число не обязательно должно быть защищено (соль не зашифрована, мы не беспокоимся, что кто-то ее угадает).
  • Хороший способ применить соль при хешировании - это использовать HMAC с вашим любимым алгоритмом хеширования, используя пароль в качестве ключа HMAC и соль в качестве текста для хеширования (см. этот раздел документа ).

Пример реализации на Python с использованием SHA-256 в качестве безопасного хэша:

РЕДАКТИРОВАТЬ : как упоминал Эли Коллинз, это не реализация PBKDF2. Вы должны предпочесть реализации, которые придерживаются стандарта, такие как PassLib .

from hashlib import sha256
from hmac import HMAC
import random

def random_bytes(num_bytes):
  return "".join(chr(random.randrange(256)) for i in xrange(num_bytes))

def pbkdf_sha256(password, salt, iterations):
  result = password
  for i in xrange(iterations):
    result = HMAC(result, salt, sha256).digest() # use HMAC to apply the salt
  return result

NUM_ITERATIONS = 5000
def hash_password(plain_password):
  salt = random_bytes(8) # 64 bits

  hashed_password = pbkdf_sha256(plain_password, salt, NUM_ITERATIONS)

  # return the salt and hashed password, encoded in base64 and split with ","
  return salt.encode("base64").strip() + "," + hashed_password.encode("base64").strip()

def check_password(saved_password_entry, plain_password):
  salt, hashed_password = saved_password_entry.split(",")
  salt = salt.decode("base64")
  hashed_password = hashed_password.decode("base64")

  return hashed_password == pbkdf_sha256(plain_password, salt, NUM_ITERATIONS)

password_entry = hash_password("mysecret")
print password_entry # will print, for example: 8Y1ZO8Y1pi4=,r7Acg5iRiZ/x4QwFLhPMjASESxesoIcdJRSDkqWYfaA=
check_password(password_entry, "mysecret") # returns True
38 голосов
/ 13 ноября 2008

Основная стратегия состоит в том, чтобы использовать функцию получения ключа для «хэширования» пароля с помощью соли. Соль и результат хеширования сохраняются в базе данных. Когда пользователь вводит пароль, соль и его ввод хэшируются одинаково и сравниваются с сохраненным значением. Если они совпадают, пользователь проходит проверку подлинности.

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

Атака предварительно вычисленного словаря использует предварительно вычисленный индекс, или словарь, от хеш-результатов до паролей. Хеширование выполняется медленно (или, как предполагается, в любом случае), поэтому злоумышленник хеширует все вероятные пароли один раз и сохраняет результат, проиндексированный таким образом, что при наличии хэша он может найти соответствующий пароль. Это классический обмен пространства на время. Поскольку списки паролей могут быть огромными, есть способы настроить компромисс (например, радужные таблицы), чтобы злоумышленник мог отказаться от небольшой скорости, чтобы сэкономить много места.

Атаки перед вычислением предотвращаются с помощью «криптографической соли». Это некоторые данные, которые хэшируются с паролем. Это не должно быть секретом, это просто должно быть непредсказуемо для данного пароля. Для каждого значения соли злоумышленнику потребуется новый словарь. Если вы используете один байт соли, злоумышленнику потребуется 256 копий своего словаря, каждый из которых будет создан с использованием другой соли. Сначала он использовал соль для поиска правильного словаря, затем он использовал вывод хеша для поиска пригодного для использования пароля. Но что, если вы добавите 4 байта? Теперь ему нужно 4 миллиарда копий словаря. При использовании достаточно большой соли атака по словарю исключается. На практике от 8 до 16 байтов данных из генератора случайных чисел криптографического качества дают хорошую соль.

С предварительным вычислением вне таблицы злоумышленник вычисляет хеш при каждой попытке. Время, необходимое для поиска пароля, теперь полностью зависит от того, сколько времени потребуется для хэширования кандидата. Это время увеличивается за счет итерации хэш-функции. Число итераций обычно является параметром функции вывода ключа; сегодня многие мобильные устройства используют от 10 000 до 20 000 итераций, а сервер может использовать 100 000 и более. (Алгоритм bcrypt использует термин «фактор стоимости», который является логарифмической мерой требуемого времени.)

19 голосов
/ 13 ноября 2008

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

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

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

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

Edit: И последнее: вы можете избежать ошибки, которую я совершил при первой попытке зайти на сайт безопасного входа в систему на испытательном стенде:

При обработке пароля пользователя учитывайте, где происходит хеширование. В моем случае хэш был рассчитан с помощью PHP-кода, запущенного на веб-сервере, но пароль был передан на страницу с компьютера пользователя в виде открытого текста! Это было нормально (иш) в среде, в которой я работал, так как это было внутри системы https в любом случае (уни сеть). Но в реальном мире, я думаю, вы захотите хешировать пароль до того, как он покинет пользовательскую систему, используя javascript и т. Д., А затем передать хеш на ваш сайт.

4 голосов
/ 13 ноября 2008

Следуйте Совету Ксана о том, чтобы некоторое время держать столбец с текущим паролем, поэтому, если что-то пойдет не так, вы можете откатить быстро-n-easy.

Что касается шифрования ваших паролей:

  • используйте соль
  • использовать алгоритм хеширования, предназначенный для паролей (т. Е. - это медленно )

См. Томас Птачек Достаточно с таблицами Радуга: что вам нужно знать о безопасных схемах паролей для некоторых деталей.

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

Это была моя проблема пару недель назад. Мы развертывали большой проект MIS в 975 различных географических точках, где наше собственное хранилище учетных данных будет использоваться в качестве средства проверки подлинности для различного набора уже реализованных и используемых приложений. Мы уже предоставили сервис аутентификации на основе REST и SOAP, но заказчик настаивал на том, чтобы иметь возможность получить доступ к хранилищу учетных данных пользователя из других приложений, просто подключившись к БД для просмотра только связанной таблицы или представления. Вздох ... (это очень связанное плохое проектное решение - предмет другого вопроса).

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

Мы назвали его «Защищенные хешированные пароли» или FSHP , короче говоря. Реализовал его в Python, Ruby, PHP5 и выпустил в Public Domain. Доступно для потребления, разветвления, воспламенения или плевки на GitHub на http://github.com/bdd/fshp

FSHP - это реализация хеширования пароля с итеративным хэшированием.

Принцип конструкции аналогичен спецификации PBKDF1 в RFC 2898 (a.k.a: PKCS # 5: спецификация шифрования на основе пароля, версия 2.0.) FSHP позволяет выбрать длину соли, количество итераций и лежащая в основе криптографическая хеш-функция среди SHA-1 и SHA-2 (256, 384, 512). Самоопределение мета-префикса в начале каждого вывода делает его переносимым, позволяя потребителю выбирать собственный базовый уровень безопасности хранения пароля.

БЕЗОПАСНОСТЬ

По умолчанию FSHP1 использует 8-байтовые соли с 4096 итерациями хеширования SHA-256. - 8-байтовая соль делает атаки на радужные таблицы нецелесообразными, умножая необходимое место с 2 ^ 64. - 4096 итераций приводят к довольно дорогостоящим атакам методом перебора. - Нет известных атак против SHA-256, чтобы найти столкновения с вычислительное усилие менее чем 2 ^ 128 операций во время этот выпуск.

реализации:

  • Python: протестировано с 2.3.5 (с hashlib), 2.5.1, 2.6.1
  • Ruby: протестировано с 1.8.6
  • PHP5: протестировано с 5.2.6

Каждый может создать недостающие языковые реализации или отполировать текущие.

ОСНОВНЫЕ ОПЕРАЦИИ (с Python) :

>>> fsh = fshp.crypt('OrpheanBeholderScryDoubt')
>>> print fsh
{FSHP1|8|4096}GVSUFDAjdh0vBosn1GUhzGLHP7BmkbCZVH/3TQqGIjADXpc+6NCg3g==
>>> fshp.validate('OrpheanBeholderScryDoubt', fsh)
True

НАСТРОЙКА КРИПТА:

Давайте ослабим нашу схему хеширования паролей. - Уменьшить длину соли с 8 до 2. - Уменьшить цикл итерации с 4096 до 10. - Выберите FSHP0 с SHA-1 в качестве основного алгоритма хеширования.

>>> fsh = fshp.crypt('ExecuteOrder66', saltlen=2, rounds=10, variant=0)
>>> print fsh
{FSHP0|2|10}Nge7yRT/vueEGVFPIxcDjiaHQGFQaQ==
3 голосов
/ 13 ноября 2008

Я думаю, вы должны сделать следующее:

  1. Создайте новый столбец с именем HASHED_PASSWORD или что-то подобное.
  2. Измените код, чтобы он проверял оба столбца.
  3. Постепенно переносите пароли из нехешированной таблицы в хешированную. Например, когда пользователь входит в систему, автоматически перенесите его или ее пароль в столбец хэширования и удалите версию без хэширования. Все вновь зарегистрированные пользователи будут иметь хешированные пароли.
  4. В нерабочее время вы можете запустить скрипт, который мигрирует n пользователей за раз
  5. Если у вас больше не осталось хэшированных паролей, вы можете удалить столбец старого пароля (возможно, вы не сможете этого сделать, это зависит от используемой вами базы данных). Также вы можете удалить код для обработки старых паролей.
  6. Готово!
2 голосов
/ 13 ноября 2008

В целях аутентификации вам следует избегать хранения паролей с использованием обратимого шифрования, т. Е. Хранить только хэш пароля и проверять хэш пароля, предоставленного пользователем, против сохраненного вами хэша. Однако у этого подхода есть недостаток: он уязвим для радужных таблиц атак, если злоумышленник овладеет вашей базой данных хранилища паролей.

Что вам нужно сделать, это сохранить хэши предварительно выбранного (и секретного) значения соли + пароль. Т.е. объединить соль и пароль, хэшировать результат и сохранить этот хеш. При аутентификации сделайте то же самое - объедините ваши солт-значение и предоставленный пользователем пароль, хеш, затем проверьте на равенство. Это делает атаки радужного стола невозможными.

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

2 голосов
/ 13 ноября 2008

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

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

1 голос
/ 14 ноября 2008

Шаг 1: Добавить зашифрованное поле в базу данных

Шаг 2. Измените код, чтобы при смене пароля обновлялись оба поля, но при входе в систему все еще использовалось старое поле.

Шаг 3: Запустите скрипт, чтобы заполнить все новые поля.

Шаг 4. Измените код, чтобы при входе в систему использовалось новое поле, а при смене паролей перестало обновляться старое поле.

Шаг 5: Удалить незашифрованные пароли из базы данных.

Это должно позволить вам выполнить переход без прерывания для конечного пользователя.

Также: Что-то, что я хотел бы сделать, это назвать поле новой базы данных чем-то, что совершенно не связано с паролем, таким как «LastSessionID» или что-то похожее скучное. Затем вместо удаления поля пароля просто заполните хэши случайных данных. Затем, если ваша база данных когда-либо будет взломана, они могут потратить все свое время, пытаясь расшифровать поле «пароль».

Это может на самом деле ничего не достигать, но интересно думать, что кто-то сидит там и пытается выяснить бесполезную информацию

1 голос
/ 13 ноября 2008
  • Шифрование с использованием чего-то вроде MD5, кодирование его в виде шестнадцатеричной строки
  • Тебе нужна соль; в вашем случае имя пользователя может быть использовано в качестве соли (оно должно быть уникальным, имя пользователя должно быть самым уникальным из доступных значений; -)
  • используйте поле старого пароля для хранения MD5, но пометьте MD5 (например, "MD5: 687A878 ...."), чтобы старые (обычный текст) и новые (MD5) пароли могли сосуществовать
  • изменить процедуру входа в систему, чтобы проверить, есть ли MD5, если есть MD5, а в противном случае - простой пароль
  • изменить функции «изменить пароль» и «новый пользователь», чтобы создавать только пароли MD5
  • теперь вы можете запустить пакетное задание преобразования, которое может занять столько времени, сколько необходимо
  • после запуска преобразования удалите legacy-support
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...