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

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

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

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

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

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

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

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

Ответ 1

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

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

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

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

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

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

Ответ 2

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


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

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

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

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

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

EDIT: как упоминал Эли Коллинз, это не реализация 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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

  • Создайте новый столбец HASHED_PASSWORD или что-то подобное.
  • Измените свой код так, чтобы он проверял оба столбца.
  • Постепенно переносите пароли из не хэшированной таблицы в хэшированную. Например, когда пользователь входит в систему, автоматически переносите свой пароль в хеш-столбец и удаляйте неповрежденную версию. Все зарегистрированные пользователи будут иметь хешированные пароли.
  • Через несколько часов вы можете запустить script, который ежемесячно переносит n пользователей
  • Если у вас больше не осталось лишних паролей, вы можете удалить свой старый столбец паролей (возможно, вы не сможете этого сделать, зависит от используемой вами базы данных). Кроме того, вы можете удалить код для обработки старых паролей.
  • Все готово!

Ответ 6

Это была проблема моей пары недель назад. Мы развертывали большой проект 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 (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==

Ответ 7

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

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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

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

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

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

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

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

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

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

Ответ 11

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

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

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

Ответ 12

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

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

Ответ 13

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

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

  • Не используйте MD5/SHA1
  • Создайте хорошую случайную соль (многие библиотеки криптографии имеют генератор соли).
  • Итеративный алгоритм хеширования, рекомендованный orip
  • Убедитесь, что пароли не передаются обычным текстом по кабелю.

Ответ 14

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

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

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

Ответ 15

Чтобы хешировать пароль, вы можете использовать функцию HashBytes. Возвращает varbinary, поэтому вам нужно будет создать новый столбец, а затем удалить старый varchar.

Как

ALTER TABLE users ADD COLUMN hashedPassword varbinary(max);
ALTER TABLE users ADD COLUMN salt char(10);
--Generate random salts and update the column, after that
UPDATE users SET hashedPassword = HashBytes('SHA1',salt + '|' + password);

Затем вы модифицируете код для проверки пароля, используя запрос типа

SELECT count(*) from users WHERE hashedPassword = 
HashBytes('SHA1',salt + '|' + <password>)

где < пароль > - это значение, введенное пользователем.

Ответ 16

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