Рекомендации по использованию кодов подтверждения по электронной почте

StackOverflow https://stackoverflow.com/questions/2033315

Вопрос

Я создаю веб-сайт PHP, на котором пользователи регистрируются, и мне интересны лучшие практики для кодов «подтверждения по электронной почте».

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

md5("xxxxxxxxx".$username."xxxxxxxxxxx".$timestamp."xxxxxxxxx");

Где $timestamp относится к времени создания пользователя.В целом меня это вполне устроило, но потом я задумался, достаточно ли это безопасно?А как насчет возможности столкновений?А ещё мне нужно сгенерировать коды для сброса пароля и т.д.Если бы я использовал аналогичную методологию, коллизия могла бы привести к тому, что один пользователь случайно сбросил пароль другого пользователя.И это нехорошо.

Итак, как вы это делаете?Моими мыслями была таблица следующего формата:

codePK (int, a-I), userID (int), type (int), code (varchar 32), date (timestamp)

Где «тип» будет 1, 2 или 3, что означает «активация», «смена электронной почты» или «сброс пароля».Хороший ли это способ сделать это?У вас есть лучший способ?

Могу ли я, используя метод, аналогичный описанному выше, автоматически удалить что-либо старше двух дней без использования заданий cron?Мой хост (почтиfreespeech.net) их не поддерживает.Если это вообще возможно, я бы хотел избежать выполнения cron-задания на внешнем хосте, где wget представляет собой сценарий, удаляющий вещи, поскольку это просто беспорядок =P.

Спасибо!
Мала

Обновлять:
Чтобы уточнить:Я понял, что единственный способ безопасно и безопасно выполнить эту задачу — использовать базу данных, чего исходная функция пыталась избежать.Мой вопрос заключается в том, как должна быть структурирована таблица (или таблицы?).Кто-то предложил мне отказаться от codePK и просто сделать код PK.Итак, вкратце, мой вопрос:это то, что ты делаешь?

Это было полезно?

Решение

Когда мне нужны такие трюки, обычно это происходит по одной из двух причин, обе упомянутых вами:

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

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

Прежде всего, вы всегда должны использовать какую-то соль, которая скрыта и о которой знаете только вы.Обратите внимание, что эта соль должна быть разной для каждого пользователя.Например, соль можно рассчитать как sha256(something random).Затем эта соль должна быть сохранена в базе данных вместе с именем пользователя и паролем (хешированными с солью).

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

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

К чему это по сути сводится:

  • Хешируйте пароли пользователей, используя уникальную для пользователя соль (и возможно, глобальная секретная соль).
  • Сгенерируйте ключ путем хеширования ряда случайных или псевдослучайных источников, таких как временные метки, mt_rand() или даже random.org, если вам действительно нужны случайные вещи.
  • Никогда не используйте глобальную соль или соль, уникальную для пользователя, для хеширования всего, к чему пользователь имеет доступ, включая ключи сброса пароля, ключи активации и т. д.

Обратите внимание, что я ни в коем случае не являюсь экспертом по безопасности и, вероятно, забыл ряд вещей, и, возможно, я упомянул некоторые очень плохие практики.Просто мои 5 копеек ;-)

Другие советы

Зачем использовать какие-либо данные пользователя в качестве основы для ключа авторизации?

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

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

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

Почему бы не сделать поле кода уникальным индексом?То есть столкновений никогда не будет?

Кроме того, если вам не нужно создавать хэш на основе пользовательского ввода и сопоставлять его с хешем базы данных (подтверждение электронной почты, сброс пароля и т. д.) — вы можете добавить случайную строку в тело хеша, напримерmd5('xxx'.$username.'xxx'.time().'xxx'.rand())

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top