Лучшие практики для кодов подтверждения по электронной почте - PullRequest
15 голосов
/ 09 января 2010

Я создаю веб-сайт 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-jobs? Мой хост (nearfreespeech.net) не поддерживает их. Если это вообще возможно, я бы хотел избежать работы cron на внешнем хосте, где wget - скрипт, который удаляет вещи, так как это просто грязно = P.

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

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

Ответы [ 5 ]

10 голосов
/ 09 января 2010

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

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

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

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

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

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

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

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

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

2 голосов
/ 09 января 2010

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

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

1 голос
/ 09 января 2010

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

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

0 голосов
/ 09 января 2010

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

0 голосов
/ 09 января 2010

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

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

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