Шифрование / хэширование простых текстовых паролей в базе данных

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

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

любая помощь оцененный.

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

просто:

  1. сделайте резервную копию DB
  2. обновить логин/пароль Обновить код
  3. после нескольких часов, пройти через все записи в таблице пользователей хэширования пароля и замены каждого

16 ответов


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

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

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

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

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

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


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


когда вы хэш-пароли использовать НЕ ИСПОЛЬЗУЙТЕ ПРОСТОЙ 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

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

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

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

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

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


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

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

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

см. Томаса Птачека Достаточно Радужных Таблиц: Что Вам Нужно Знать О Безопасных Схемах Паролей для отдельные детали.


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

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

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

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

мы назвали его довольно безопасным хэшированные пароли или "ФШП" короче. Реализовал его в Python, Ruby, PHP5 и выпустил в общественное достояние. Можно съеденный, раздвоенный, пылающий или плюющий на GitHub в http://github.com/bdd/fshp

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

принцип конструкции схож с PBKDF1 спецификация в RFC 2898 (a.к. 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 (w/ 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==

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

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


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

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

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


  • шифровать, используя что-то вроде MD5, кодировать его как шестнадцатеричную строку
  • вам нужна соль; в вашем случае имя пользователя может использоваться как соль (оно должно быть уникальным, имя пользователя должно быть самым уникальным значением; -)
  • используйте старое поле пароля для хранения MD5, но пометьте MD5 (i.e.g " MD5: 687A878....") так что старый (обычный текст) и новый (MD5) пароли могут сосуществовать
  • измените процедуру входа для проверки на MD5, если есть MD5, и против простого пароля в противном случае
  • изменить "изменить пароль" и "новый пользователь" функции, чтобы создать пароли MD5 объед только
  • теперь вы можете запустить пакетное задание преобразования, которое может занять столько времени, сколько необходимо
  • после выполнения преобразования удалите устаревшую поддержку

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

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

Шаг 3: запустите скрипт для заполнения всех новых полей.

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

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

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

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

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


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

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

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


Я не эксперт по безопасности, но я думаю, что Текущая рекомендация-использовать bcrypt/blowfish или вариант SHA-2, а не MD5 / SHA1.

вероятно, вам нужно думать с точки зрения полного аудита безопасности, тоже


MD5 и SHA1 показали немного слабости (два слова могут привести к одному и тому же хэшу), поэтому рекомендуется использовать SHA256-SHA512 / итеративные хэши для хэширования пароля.

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

  1. не используйте MD5 / SHA1
  2. генерировать хорошую случайную соль (многие криптографические библиотеки имеют генератор соли)
  3. итеративный алгоритм хэша, как orip рекомендуется
  4. убедитесь, что пароли не передаются в обычном тексте по проводу

Я хотел бы предложить одно улучшение в великий пример python, опубликованный Orip. Я бы переопределил random_bytes функция должна быть:

def random_bytes(num_bytes):
    return os.urandom(num_bytes)

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


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


хэш их с md5. это то, что обычно делается с паролями.