Манипулирование учетными записями пользователя — шаблоны реализации

    image Тема этой публикации вряд ли заинтересует опытного разработчика веб-приложений.

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

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

    Все манипуляции с учетными записями пользователя сводятся к следующим:

    1. регистрация с подтверждением по электронной почте;
    2. смена некритичных настроек;
    3. смена критичных настроек — с подтверждением паролем;
    4. смена адреса электронной почты — с подтверждением по электронной почте;
    5. восстановление забытого пароля.


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

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


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

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

    image


    Для контроля правильности ввода пароль указывается дважды.

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

    image


    И пользователю сообщается об этом.

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

    Подытожим алгоритм регистрации пользователя:

    1. получить адрес электронной почты и пароль
    2. отправить письмо со строкой подтверждения регистрации
    3. сообщить пользователю об отправке письма
    4. обработать клик по строке подтверждения и зарегистрировать пользователя

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

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

    • чтобы не возникало проблем с многократными незаконченными регистрациями для одного электронного адреса;
    • чтобы был порядок в работе с различными сущностями.


    Смена некритичных настроек


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

    Чтобы пользователь мог, просто открыть необходимую форму:

    image

    И сохранить результат

    image


    Смена критически важных настроек


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

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

    image


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

    Важным моментом является способ хранения информации о пароле пользователя.

    Существует множество старых, успешных веб-сервисов, сохраняющих в базе данных сами пароли. Это неправильно, недопустимо и все современные веб-приложения хранят не пароли, а их хеши.

    Хеш это строка, сгенерированная из пароля и специальной секретной добавки (salt).

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

    Подытожим алгоритм смены пароля:

    1. получить старый и новый пароли;
    2. сравнить хеш старого пароля с хешом хранящимся в базе данных;
    3. если хеши совпали: вычислить хеш для нового пароля и сохранить его в базе данных.

    Смена электронной почты


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

    image


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

    image


    И пользователю сообщается об этом:

    image


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

    image


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

    image


    Пользователь указывает пароль, и подтверждает смену адреса.

    Подытожим алгоритм смены электронной почты пользователя:

    1. получить адрес электронной почты и пароль;
    2. сравнить хеш пароля и хешом хранящимся в базе данных;
    3. если хеши совпали: отправить письмо со строкой подтверждения смены адреса
    4. сообщить пользователю об отправке письма;
    5. обработать клик по строке подтверждения и отобразить страницу подтверждения владения учетной записью;
    6. получить специальную строку и пароль пользователя, по специальной строке определить пользователя и убедиться в правильности пароля (аналогично п.2);
    7. если пароль правильный: заменить в базе данных старый электронный адрес на новый, а если хеш пароля вычисляется с использованием адреса электронной почты – то, одновременно вычислить и сохранить в базе данных новый хеш пароля.

    Смена забытого пароля


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

    image


    После получения адреса электронной почты на указанный адрес отправляется письмо со ссылкой содержащей строку подтверждения. Строка подтверждения однозначно идентифицирует пользователя, который восстанавливает пароль.

    image


    И пользователю сообщается об этом:

    image


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

    image


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

    Подытожим алгоритм смены электронной почты пользователя:

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

    Специальная строка, идентифицирующая пользователя


    При регистрации, смене адреса и смене забытого пароля используется специальная строка (токен) идентифицирующая пользователя.

    Есть два способа генерации таких строк:

    Первый (с использованием базы данных):

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

    Второй (с шифрованием):

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

    Вот как может выглядеть работа со специальной строкой (токеном) на языке python, с использованием библиотеки itsdangerous

    При генерации токена

    from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
    # создаем сериализатор указав, используемый секретный ключ 
    # и время жизни (в секундах) зашифрованной информации
    serializer = Serializer('very_secreti_key', expires_in=60*60*24 )
    
    # получаем специальную строку содрежащую всю информацию
    id=1234567
    token = serializer.dumps( {'newemail':  id} ) }
    

    Получив специальную строку, можем извлечь из неё информацию

    # создаем сериализатор указав, используемый секретный ключ 
    serializer = Serializer('very_secreti_key')
    
    # получаем зашифрованные данные:
    # {'newemail':  1234567}
    try:
      data = serializer.loads( token )
    

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

    TheOnlyPage
    Компания
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 21

      –1
      Вы знаете, все, что есть в статье и даже больше элементарно гуглится (хотя даже начинающий веб-разработчик про все это слышал)? Особенно момент про хеширование пароля.
        +1
        Даже если и слышал, полезно иметь шпаргалку со всей информацией в одном месте.
        +5
        Кстати, требовать вводить пароль два раза при регистрации и смене пароля — это пережиток прошлого. Если человек опечатается — а такое случается редко-редко — всегда можно воспользоваться восстановлением пароля еще раз.

        Из то же оперы следующие сценариями, которые долгое время считались единственно правильными и возможными:

        1. Иметь длинную форму регистрации.
        2. Требовать вбить пароль при регистрации 2 раза.
        3. Иметь длинную форму регистрации и сбрасывать поля ввода пароля и подтверждения, если валидация других полей не прошла.
        4. Требовать вбить 2 раза адрес электронной почты.
        5. Не давать пользоваться сервисом, пока в письме на ссылку не нажмешь.

        И т.д, Сегодня большинство пунктов — дикость. Желательно иметь вообще 2 поля при регистрации и логине — почта и пароль. Плюс социальные кнопки по желанию.
          +1
          Дискуссия на тему два раза вводить пароль при регистрации или один раз может получиться действительно эпической. И главное все останутся при своем мнении.
            +2
            Желательно иметь вообще 2 поля при регистрации и логине — почта и пароль

            Или можно пойти еще дальше, и оставить 1 поле для регистрации — почта. А пароль генерировать и отправлять на почту. Я так в одном сервисе делаю.

            Требовать вбить 2 раза адрес электронной почты

            За такое вообще убивать хочется. Особенно, когда некоторые извращенцы яваскриптом Ctrl+c/Ctrl+v в этих полях блокируют.
              +2
              За такое вообще убивать хочется. Особенно, когда некоторые извращенцы яваскриптом Ctrl+c/Ctrl+v в этих полях блокируют.

              Мы ведь тоже так делали 2 года: пупблика на сайте была непродвинутая и ошибки в почте были часто. Причем мы сразу просили данные карты, и потом снимали с людей денег. И получалось: есть аккаунт, с которого идут деньги, но которому не удается отправить подтверждение. Часто люди платили месяцами и пользовались. Но иногда человек получал очередной платеж, видел его в банке, но не узнавал и заказывал chargeback.

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

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

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

                проверять популярные почтовые домены и сравнивать юзернейм

                Вот это уже хороший вариант. Но блокировать возможность копипасты — это зло. Если пользователь настолько непродвинут, что свой адрес ввести без ошибок не может, то он и Ctrl+c жать не будет. А остальных будет раздражать.
                  0
                  Тоже, на мой взгляд, антипаттерн. Сразу сообщать данные своей карты какой-то левой системе? Кроме того, если деньги не сразу снимаются, и есть, например, триальный период, то лучше позволить пользователю заплатить тогда, когда это будет нужно. И если ему это будет нужно.

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

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

                  Сейчас, насколько знаю — меня в той команде уже нет — остался только один косяк: на сайте указан номер службы техподдержки, работающей 24/7, но за номером робот, который регистрирует звонок. Поддержка потом переходит в режим общения по email и звонит клиенту только если он грозит чарджбеком.
                  0
                  А как насчёт варианта при клике на «Зарегистрироваться» возвращать фокус на поле с адресом электронной почты и просьбой подтвердить адрес?
              0
              Генерировать псевдослучайную последовательность зашифровав информацию об id пользователя, типе сообщения и времени жизни сообщения в самом сообщении.

              Мне кажется, и в данной логике мы имеем нехилую такую дыру в безопасности?
              Меня всю жизнь учили, что за пределы сервера не должна выходить никакая информация, даже так или иначе зашифрованная. К тому же не думаю, что там будет, что то серьезное по шифрованию…
                0
                За пределы сервера постоянной выходит зашифрованная информация, например при обмене данными по протоколу TLS (SSL).
                  0
                  В статье не нашел ни одного упоминания, о том, что используется, какой либо протокол кроме умолчательного http
                  И не надо мои слова передергивать. Сервер не должна покидать информация о пользователе, естественно картинки и HTML это не касается…
                  А то встречались мне «сервисы» где в куках хранились пользовательские логин с паролем… при этом логин был не шифрованный, а пароль md5 один раз, что по радужным таблицам раскалывается на раз, два, три…
                  Или когда в адресной строке передается как элемент урла что то вида \ТРИ СИМВОЛА\ID\ПЯТЬ СИМВОЛОВ\логин пользователя\ЕЩЕ ПЯТЬ СИМВОЛОВ\
                  А зная идентификатор пользователя, и его пароль, попытаться пропихнуть SQL иньекцию пусть и слепую на таблицу users… так руки и чешутся… особенно если отсутствует привязка хотя бы к сессиям…
                  В общем надеюсь я объяснил какую информацию и в каких случаях я имел в виду.
                0
                Вот честно — ненавижу когда логин и эмейл это одно и то же. Эмейлов у меня штук 5, а логинов всего 2-3 (третий — небольшая модификация первого).

                Да и к тому же особенно касается сайтов с публичными данными (форумы, комментарии) — помещать там эмейл ну просто глупо.

                Минусуйте. :)
                  0
                  Ну тут слегка неоднозначно. С одной стороны — да, иногда удобнее иметь логин. Привычнее как-то, что ли. С другой стороны, зачем плодить лишние сущности? Если email нужен все равно, а однозначно идентифицировать можно и по логину, и по почте, то зачем требовать их оба? Да, тот же джанго по умолчанию предполагает, что может быть несколько пользователей с разными логинами, но одной почтой, но на практике я что-то такого не припомню.

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

                  А email можно и не раскрывать другим посетителям. Если предполагается функциональность комментариев и пр., то можно в профиль добавить возможность придумать себе ник.
                    0
                    Ну, достаточно «логин» назвать «именем пользователя», чтобы обычные пользователи начали понимать о чём речь, ну и сбоку написать небольшое пояснение о том, что это такое и зачем нужно :)

                    Просто дело в том, что именно входить по длинному эмейлу куда как менее удобно чем по (обычно) не в пример короткому логину. Ну и не говоря о том, что эмейлов таки может быть несколько, о чём я и сказал ранее. :)
                      0
                      Сначала так и было написано: «Имя пользователя». Туда пытались прилежно ввести что-нибудь вроде «Иванов Петр Михайлович». И искренне удивлялись, что с таким логином зарегистрироваться нельзя. И не читали сообщение об ошибке, а начинали заваливать письмами техподдержку. То же самое касается и «написать небольшое пояснение о том, что это такое и зачем нужно» — не читают даже то, что написано большими буквами в красной рамке (вот правда, для меня это абсурд — почему человеку проще написать 20 предложений, чем прочитать одно?). Поэтому решено было идти по пути наименьшего сопротивления и выкинуть это поле.

                      Кому лень вводить (как мне, например) длинные email и пароль, тот может запомнить в браузере или пользоваться keypass'ом.
                        0
                        Всякие запоминалки в браузерах и Keepass-ы хороши, пока не нужно заходить в свою учётку с другого компа/мобильного устройства/микроволновки/you say it.

                        А так с вами солидарен, что бред, про не читающих сообщения пользователей. Хотя по мне так стоило таки разрешить логин формата «Иванов Пётр Михайлович». В чём собственно проблема-то? Ну и показывать «красные рамки» только если есть повторки, да.
                          0
                          В чём собственно проблема-то?

                          Например, проблема возникнет, когда этот пользователь поставит 2 пробела между словами. Или напишет «Пётр» вместо «Петр». Или напишет «Петр Михайлович Иванов». А он что-нибудь из этого точно сделает, зуб даю.

                          Да, с другого браузера/компа — немного неудобно будет. Но, во-первых, мы же говорим не о пароле (он-то и так супер-сложный и мега-криптостойкий), а о разнице между логином и почтой, это не так уж и существенно.
                  0
                  **Веткой промахнулся**
                    0
                    А зачем в качестве иллюстрации к статье вы используете покореженную иконку давнего продукта компании Elcomsoft?
                      0
                      Эта чтоль?
                      Скрытый текст
                      image

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

                    Самое читаемое