Перейти к содержимому

Как сделать аутентификацию на сайте

  • автор:

Как бы вы реализовали форму аутентификации на сайте? Вопрос для собеседования на Junior/Middle/Senior?

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

Тема с одной стороны в общем-то простая, а с другой — многогранная. Можно сделать “на коленке”, а можно и “по-взрослому” — зависит от знаний конкретного девелопера и технического задания. Ну и не привязывается к конкретному языку. Что nodejs, что .net, что PHP — на ответы это не влияет. Ну и отлично же! Давайте попробуем.

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

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

Восклицательным знаком ⚠ помечены вопросы, на которых можно «засыпаться» и оставить плохое впечатление о себе у интервьюера. Так же я позволил себе добавить еще пункты, которые подразумевают «Регистрацию», но по касательной. Многие ответы обрамил ссылками, которые помогут разобраться чуть глубже в конкретном вопросе, думаю будет полезно.

Junior level

Нужно ли скрывать вводимый пароль на странице?

Конечно. Никто не хочет, чтобы кто-то подсматривал пароли из-за плеча, верно? У input есть специальный атрибут password для этого. Это очевидно, но это и первый пункт. Дальше будет сложнее, обещаю 🙂

Должны ли валидироваться поля для ввода на клиентской стороне?

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

Аутентификация? Авторизация? Идентификация? В двух словах расскажите что за что отвечает.

Часто эти понятия путают, поэтому давайте кратко:

  • Идентификация — это проверка что такой пользователь/логин/email существует в системе.
  • Аутентификация — проверка связки логина и пароля, то есть проверка на то, не выдаёт ли пользователь себя за другого человека.
  • Авторизация — проверка прав доступа пользователя к внутренним ресурсам.

Запрос с данными формы должен идти через GET или POST?

Правильнее делать используя метод POST. Ничего вам не мешает сделать это любым другим методом, однако правильнее всего данные формы слать именно через POST. Изначально этот тип запроса был спроектирован не идемпотентным. Это значит, что отсылая его вы не можете гарантировать, что последствия его выполнения будут одинаковыми, если вызвать его несколько раз подряд. Поэтому в случае обрыва соединения браузер переспросит вас хотите ли вы заново отослать эту форму (вы наверняка видели такую хотя бы раз). Все GET запросы же перепосылаются браузерами без подтверждения.

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

Сохраните ли вы в пароль при регистрации «как есть» в текстовом виде (plain text)?

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

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

Данные статистики говорят о том, что 30-40% организаций хранят пароли в plain text. И в 2018-ом даже Twitter с его 330+млн пользователей смог. Ух.

Нужно ли проверять вводимые данные не только на UI стороне, но и на сервере? Является ли это ненужной/двойной работой?

Главное правило веб разработчика — не доверяй клиенту (браузеру). Все данные/поля могут быть отосланы/подменены другими инструментами, поэтому вам нужно обязательно делать полный цикл проверки на сервере.

Нужна ли валидация на сложность пароля или можно позволять пользователю иметь пароль любой сложности?

Заставлять пользователя придумать пароль в соответствии с правилами — безусловно здравая идея, т.к. слишком простые пароли легко подбираются путём перебора по словарю. Но тут важна грань, т.к. знаю реальный пример, когда правила пароля настолько сложные, что пользователи потом хранят эти пароли в текстовых файлах на рабочих столах, т.к. запомнить их нереально. А это еще хуже 🙂 Получается, заботились о безопасности, а по факту родили новый вектор атаки.

Хорошая статья про границы политики паролей есть на Хабре.

Middle level

Соединение защищено по https?

Если нет, то у меня для вас плохие новости — ваш логин и пароль может быть легко перехвачен по пути от вашего браузера к серверу. Это может быть как целенаправленная атака, так и “пассивный сниффинг данных“ публичными Wi-Fi, на уровне провайдера или даже на уровне уязвимого роутера, которым вы пользуетесь. Еще лет пять назад встречались сайты, где форма оплаты могла быть на https, а весь сайт — по http и в целом это работало безопасно (т.к. во время платежа на клиент не сохраняется никаких данных, которые впоследствии могут быть перехвачены по http каналу), однако сайты без https сегодня — это уже моветон и первый признак того, что такому сайту свои данные доверять нельзя.

Кстати, буквально на днях выпустил пост о «SSL/TLS/Асимметричном шифровании на пальцах» у себя на тг канале, поэтому если интересна тема — вэлкам!

На тему вопроса есть хороший ответ на стеке

Нужно ли при неудачной попытке регистрации писать, что такой пароль уже существует в системе?

Конечно нет. Вы должны выдавать МИНИМУМ информации всего, что касается безопасности. Есть шуточная версия в виде сообщения «такой пароль уже используется пользователем %username%». Шутки шутками, но возможно где-то и внедрили, за более чем тринадцать лет опыта работы и не такое встречал.

На aws к слову максимальный уровень — после ввода логина, сразу же запрашивают пароль + второй фактор (2fa), т.е. злоумышленник даже не сможет узнать какой именно параметр не подошёл.

Если попытка входа была неудачная — нужно ли логировать данные формы для дальнейшего изучения проблемы?

Нельзя. Максимум — это логин (а лучше хэш логина). Пароль — нельзя. Было уже множество громких инцидентов когда у крупных сервисов ломали системы логов и вытаскивали пароли, которые логировались в plain text (логировался весь реквест).

Если хэшировать в базе пароль, то как? Каким алгоритмом?

Хорошо, если мидл назовёт парочку, допустим md5 или sha-1/sha-256. Вопрос чем они плохи — уже скорее сеньорский левел, обсудим это ниже. Однако считаю, что для мидла и такой ответ пойдёт. Также плюсом для собеседующегося будет рассказ о том как именно нужно хэшировать — т.е. с солью. SALT — это что-то, что добавляется к паролю чтобы уменьшить риск его обратного преобразования в случае утечки данных. Многие популярные пароли уже находятся в базах (так называемые радужные таблицы или Rainbow tables) и хеши к ним подобраны, поэтому реверснуть какой-нибудь md5 без соли — дело несложное.

Вот тут хорошая статья с Хабра о хешировании паролей.

Senior level

Каким еще способом можно обезопасить форму? Что такое CSRF токен и имеет ли смысл его добавлять?

Это вопрос на самом деле в целом на понимание вида уязвимости CSRF (Cross-Site Request Forgery). Сеньор должен знать и понимать как это работает как со стороны имплементации, так и со стороны взламывающей стороны (хакера).

Безусловно, запрос должен посылаться с CSRF токеном. Это гарантирует отправку формы тем же клиентом, что её и отобразил.

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

update2 : Так же CSRF уязвимость не имеет силы при использовании JWT токенов, т.к. сам app ставит их при осуществлении запросов.

Что такое двухфакторная аутентификация (2fa) и нужна ли?

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

Для серьёзных сервисов — 2fa is a must. Существенно снижает риск зайти в вашу учётку злоумышленником даже зная ваши логин и пароль. 2fa — это «второй фактор», обычно некий токен, который выдаётся через ваш телефон (приложение или же смс), но не ограничивается им. Могут быть и более безопасные варианты в виде usb флешек, блютус девайсов, биометрических сканеров и тп.

Как бы вы хранили пароль в базе? Хешированным? Солёным? Поподробнее пожалуйста!

Пароль должен быть хеширован, притом важен алгоритм. md5 и sha-1 — уже не подходят в текущих реалиях и помечены как небезопасные (RFC 6151,RFC5246). Используйте bcrypt, PBKDF2 или Argon2i, но не SHA-*. Семейство SHA-* не были задизайнены для хэширования паролей, они слишком быстрые. bcrypt/scrypt и PBKDF2 — медленные.

Если взять ПК с AMD R9 290X, то он сможет сгенерить 172 миллиарда md5/сек, 11 миллиардов SHA-256/сек, 797 миллиона SHA-512/сек и 1.3 миллиона PBKDF2 (8192 итераций и соль, 20 байт на выходе).

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

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

Сможете ли сами написать алгоритм для хранения пароля?

Правильный ответ тут — не нужно изобретать криптовелосипеды, если вы не бородатый математик! Слишком просто допустить критическую ошибку в алгоритме и снизить стойкость. Хорошо, если вы сможете привести пример несложного шифрования через XOR или расскажете про AES256 и для чего нужен вектор инициализации (IV).

Ну и немного вопросов для Senior+

Что делать в случае коллизий паролей при хэшировании?

Коллизии существуют для большинства хеш-функций, но для «хороших» функций частота их возникновения близка к теоретическому минимуму. В целом — чем длиннее хэш, тем меньше шанс этой коллизии. Именно md5 и sha-1 не обеспечивают этой длины в сегодняшнем мире. Так же можно упомянуть про двойное хеширование и метод цепочек — внутрянку знать не обязательно, просто знать, что методы есть.

Если принято решение шифровать пароль, то какой тип шифрования выберете? Почему?

Вопрос на знание отличий симметричного vs асимметричного типа. Хорошо бы понимать разницу. Ответом тут на самом деле будет “it depends”, зависит от системы взаимодействия. Обычно, используют симметричное, т.к. асимметричное подразумевает валидацию второй стороной, которой скорее всего не будет. Плюс симметричное шифрование быстрее. Неплохо бы так же дополнить, что шифрование не замена хэшированию и должно применяться только в крайних случаях для кейсов подобно обсуждаемому нами.

Рекомендации по теме «Hashing vs Encryption» можно найти прямо в cheetsheets OWASP’a.

В связи с предыдущим вопросом — одинаков ли по стойкости 128-битный ключ для симметричного и асимметричного способа?

Нет, не одинаков. Природа асимметричного шифрования подразумевает, что ключ должен быть намного длиннее. Эквивалент 128 битного симметричного ключа равен 2048 асимметричного. Хорошо бы тут еще затронуть про плюсы и минусы каждого из подходов и паттерны применения (расскажите про SSL/TLS и как он устроен)

В дополнение — хороший ответ на qna Хабра тут.

Хорошая ли идея вынести аутентификацию на openid?

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

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

Что есть Rate Limiter и каким боком оно нам к форме аутентификации?

Это уже немного вне темы сабжа, но и уровень всё же со звёздочкой — добавим в тред 🙂 Rate Limit (или Throttling) — это механизм ограничения запросов по какому-то сценарию. В случае брутфорса или же ддоса — позволит отсекать вредных клиентов и не аффектать «хороших». Все большие ребята следят за этим. Часто авторизацию прячут (как и весь сайт) за тем же Cloudflare, который берёт на себя эту функцию. Rate Limiter может быть и кастомным и быть реализован на уровне самого приложения. К примеру в виде “не принимать больше n запросов per period от такого-то пользователя с такой-то ролью”. Есть такие опции и у тех же Lambda functions от AWS, там прямо в интерфейсе можно прописать нужные цифры.

Тут можно найти общая инфу и про алгоритмы. Ну и классная статья от Яндекса об их пути рейт лимитера.

Где хранить соль и как её формировать?

Обычно, соль хранится где-то рядом с паролем. Однако в случае, если злоумышленник сможет слить базу — соль станет ему доступна. Поэтому к соли еще хорошо бы добавлять либо какую-то константу, которой нет в бд, либо проводить данные через какой-то “black-box” алгоритм, который так же не будет известен хакеру — этим вы ещё больше снижаете риск взлома хэшей, т.к. соль будет храниться в двух местах. Усложняя — можно (и нужно) комбинировать эти два метода и переменную брать, допустим из переменных окружения.

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

Дополнительные материалы

1) Годные советы по функционалу окна аутентификации

Выводы

Как мы видим — одно и то же задание может быть сделано как джуниором, так и высококлассным специалистом и результаты будут весьма разные. Позволю себе вставить пять копеек к статье об исследовании в начале сабжа : глупо ожидать серьёзного уровня реализации за 200 евро (а уж тем более за 100) на фрилансе, однако и хранить в Base64 определенно точно не стоит, если вас попросили обеспечить безопасность паролей. Нужно объяснять заказчику тезис трёх основополагающих свойств результата работы : Быстро/Качественно/Дёшево — выбрать можно только два из них.

Пожалуй, это основные моменты, которые пришли на ум. Если что-то пропустил или где-то не прав, вэлкам в комментарии 😉

ps. Если вы так же любите айтишечку как и я — буду рад видеть вас на своём Telegram канале.

  • аутентификация
  • собеседование вопросы
  • computer science
  • криптография
  • разработка web-сайтов

Настройка авторизации через Google

Как настроить авторизацию посетителей сайта через Google в личном кабинете и для оформления заказа

Если у вас несколько сайтов, то авторизацию нужно настроить отдельно для каждого сайта.

  1. Зарегистрируйтесь в «Гугле».
  2. Откройте страницу https://console.cloud.google.com/cloud-resource-manager.
  3. Создайте новый проект кнопкой «CREATE PROJECT».
  4. Введите любое название и нажмите на кнопку «CREATE».
  5. Перейдите к настройкам созданного проекта.
  6. Откройте раздел «Explore and enable APIs».
  7. Откройте раздел « OAuth consent screen».
  8. Выберите «User Type → Externanl» и нажмите на кнопку «CREATE».
  9. На шаге «1. OAuth consent screen» введите название приложения, которое будет видно посетителям во время авторизации на вашем сайте через Google, и email-адрес, по которому пользователи смогут обращаться к вам с вопросами.
  10. Внизу страницы в секции «Authorized domains» добавьте доменное имя своего сайта.
  11. В секции «Developer contact information» введите email-адрес, на который Google будет отправлять вам информацию об изменениях в работе этого проекта.
  12. Нажмите на кнопку «SAVE AND CONTINUE».
  13. На шагах «2. Scopes» и «3. Test users» нажмите на кнопку «SAVE AND CONTINUE».
  14. Откройте раздел « Credentials ».
  15. Нажмите на кнопку «CREATE CREDENTIALS» и выберите «OAuth client ID».
  16. Выберите тип приложения «Web application».

В поле «Authorized redirect URIs » введите адрес вида https://mydomain.com/oauth.php?provider=google. Замените mydomain.com на домен своего сайта. Нажмите на кнопку «CREATE».

  • Скопируйте значения «Your Client ID» и «Your Client Secret».
  • Откройте приложение «Сайт» в своей панели управления Webasyst.
  • В списке сайтов слева вверху выберите тот, для которого вы настраиваете авторизацию через Google.
  • Откройте раздел «Личный кабинет».
  • Если авторизация для выбранного сайта не включена, включите её с помощью переключателя.
  • В секции «Способы входа и регистрации » включите переключатель для «Социальные сети (дополнительный способ входа)».
  • Включите флажок напротив Google.
  • Вставьте скопированные значения «Your Client ID» и «Your Client Secret» в поля «Идентификатор клиента» и «Секрет клиента».
  • Сохраните настройки авторизации внизу страницы.
  • Проверьте, как работает авторизация:
    • Откройте свой сайт в режиме браузера «инкогнито». Или в другом браузере, где вы не авторизованы ни в своем Вебасисте, ни в «Гугле».
    • Перейдите по ссылке «Вход».
    • Щелкните по иконке «Гугла».
    • Введите данные для входа в сервис.
    • После обновления страницы откроется личный кабинет зарегистрированного посетителя вашего сайта.
  • Готово! Авторизация через Google настроена.

    4 комментария

    +2

    Павел Горелов Разработчик 18 апреля 2022 18:26 #

    Сейчас всех заставляют использовать HTTPS иначе работать не будет

    https://console.cloud.google.com/apis/credentials?project=parserposter

    Ну хоть ключ каждый год не заставляют обновлять как у Yandex

    Аутентификация пользователя

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

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

    К счастью HTTP и все браузеры предоставляют возможность сохранения информации о пользователе.

    Cookies

    Cookies (в дальнейшем просто «куки») — небольшие фрагменты данных, которые веб-сервер отправляет браузеру. Браузер сохраняет их у себя, а при следующем посещении веб-страницы отправляет обратно. Благодаря этому, веб-сервер сможет узнать своего «старого» посетителя.

    �� С технической стороны, куки — это обычные HTTP-заголовки.

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

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

    Как установить куки: функция setcookie

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

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

    Чтобы добавить новую куку, необходимо вначале определиться со следующими критериями:

    • Название куки (может состоять только из символов латинского алфавита и цифр);
    • Значение, которое предполагается хранить;
    • Срок жизни куки — это обязательное условие.

    За установку куки в PHP отвечает функция setcookie , ей нужно передать как минимум три параметра, описанных выше. Пример:

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

    Как прочитать куки

    В PHP максимально упрощён процесс чтения информации из cookies. Поэтому все переданные сервером куки доступны в специальном глобальном массиве $_COOKIE .

    Так, чтобы получить содержимое куки с именем visit_count , достаточно обратиться к одноимённому элементу массива $_COOKIE , например, вот так:

    Обратите внимание: установив в сценарии куку через setcookie , прочитать её можно будет только при следующем посещении страницы.

    Собираем всё вместе

    Теперь, научившись устанавливать и читать куки, напишем полноценный сценарий, который будет считать и выводить количество посещений страницы пользователем:

     setcookie("visit_count", $visit_count, strtotime("+30 days")); print("Количество посещений: " . $visit_count); ?> 

    Сессии

    Мы уже умеем сохранять информацию для пользователя между посещениями страницы с помощью кук. Но зачем нам ещё и сессии?

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

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

    Запись и чтение информации при использовании сессий выглядит просто как работа со специальным массивом $_SESSION .

    Как устроены сессии

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

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

    Перепишем сценарий для подсчета посещений, но теперь используем сессии:

     $_SESSION["visit_count"] = $visit_count; print("Количество посещений: " . $visit_count); ?> 

    Аутентификация

    Представим интернет-магазин. Все его страницы можно разделить на две половины: публичные и приватные.

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

    Проверка доступа пользователей к сайту и называется аутентификацией. Весь процесс аутентификации всегда состоит из нескольких шагов:

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

    Отличие аутентификации и авторизации

    Следует различать два термина: аутентификация и авторизация.

    �� Аутентификация — проверка подлинности предоставленного пользователем идентификатора (пара логин-пароль).

    Авторизация — процесс проверки и предоставления прав пользователю на выполнение определённого действия.

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

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

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

    Использование сессии для контроля доступа

    Сессии чаще всего используются для хранения информации о залогиненном пользователе.

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

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

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

    Выход с сайта. Как закрыть сессию

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

    Чтобы очистить сессию, достаточно очистить массив $_SESSION : $_SESSION = []

    «Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.

    Система авторизации с помощью cookie на PHP

    loader

    В прошлом уроке мы изучили механизм взаимодействия с cookie в языке PHP.

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

    Техническое задание

    Начнём мы это дело с описания будущей системы. Пусть у нас будут следующие компоненты:

    1. Главная страница сайта с каким-либо содержимым. Вверху страницы выводится:
      • если пользователь авторизован: Добро пожаловать, %username%.
      • если пользователь неавторизован: Авторизация — слово является ссылкой, которая ведёт на форму авторизации.
        Авторизован пользователь или нет, определяется с помощью cookie.
    2. Страница с формой авторизации. Два инпута для логина и пароля и кнопкой «Вход». Если введены правильные логин и пароль, устанавливаются cookie со значениями переданных данных, а затем пользователя автоматически редиректит (перенаправляет) на главную страницу.
    3. Страница для разлогинивания — при переходе на неё cookie будут удаляться из браузера пользователя, а затем выполняется редирект на главную страницу.

    Решение

    Продумываем архитектуру

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

    • Привет, мир!
    • Развернуть строку
    • Чётные числа
    • Числа Фибоначчи

    Начнем с простого — для начала у нас должно получиться 3 странички, которые мы описали в ТЗ.

    Ещё нам потребуется функционал, который будет проверять, авторизован ли пользователь. Если мы перечитаем ТЗ, то поймём, что он используется в двух местах — и на главной странице и на странице авторизации. Значит, стоит вынести этот механизм в отдельный файл, и использовать его в двух местах сразу.

    Ну и наконец, нам где-то нужно хранить самих пользователей, а именно — их логины и пароли. Создадим для этого простенькую «базу данных» — массив, с набором пар логин-пароль. Это ещё один файл.

    • Курс HTML для начинающих
    • Курс PHP для начинающих
    • Курс MySQL для начинающих
    • Курс ООП в PHP

    Пишем код

    Все исходники по данному заданию доступны здесь.

    База данных

    Ну вот, всё продумали, осталось только написать. Предлагаю начать с нашей базы данных. Создадим файл usersDB.php и запишем в него несколько пользователей.

     'admin', 'password' => 'P@ssw0rd'], ['login' => 'moderator', 'password' => 'password'], ['login' => 'user', 'password' => '123'], ];
    Функции проверки авторизации

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

     > return false; >

    В цикле мы пробегаемся по базе данных пользователей и пытаемся найти пользователя с переданными логином и паролем. Если такой пользователь в массиве найден — возвращаем true. Иначе — false.

    Давайте теперь ещё напишем функцию, которая будет возвращать логин текущего пользователя. Эта функция будет проверять текущие значения cookie с ключами login и password с помощью уже существующей функции checkAuth. При этом если пользователь найдётся, то она вернёт его login, а иначе — null. Назовём эту нашу новую функцию getUserLogin.

    //продолжение файла auth.php function getUserLogin(): ?string < $loginFromCookie = $_COOKIE['login'] ?? ''; $passwordFromCookie = $_COOKIE['password'] ?? ''; if (checkAuth($loginFromCookie, $passwordFromCookie)) < return $loginFromCookie; >return null; >

    На этом всю логику проверки логина мы описали. Теперь займёмся непосредственно страничками.

    Главная страница

    Создадим файл index.php. Для простоты примера мы будем использовать только строку с приветствием авторизованного пользователя, либо ссылкой на авторизацию. В этой странице нам потребуется функция проверки авторизации через cookie, поэтому здесь нужно подключить файл auth.php.

       Главная страница   Авторизуйтесь Добро пожаловать,  
    Выйти

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

    Главная страничка с призывом авторизации

    Форма авторизации

    Давайте теперь сделаем форму авторизации — создаём файл login.php и для начала набрасываем саму HTML-форму. Шаблон получился следующим.

      Форма авторизации     

    Давайте теперь добавим логику проверки переданных данных.

     else < $error = 'Ошибка авторизации'; >> ?>  Форма авторизации        

    Логика простейшая — если был отправлен POST-запрос, проверяем правильные ли логин и пароль были переданы.

    Если нет — то создаём переменную $error, в которой пишем об ошибке авторизации. Позже в шаблоне выводим эту ошибку, если эта переменная объявлена.

    Если же авторизация прошла успешно, мы устанавливаем cookie с ключами login и password, в которые помещаем значения из POST-запроса. После этого выполняем редирект на главную страницу.

    Редирект делается с помощью заголовка в HTTP-ответе. Этот заголовок называется Location и выглядит следующим образом:

    Location: адрес_на_который_нужно_перейти

    Для формирования заголовков в PHP используется функция header – ознакомиться с ней более детально вы можете здесь.

    Теперь можно попробовать нашу страничку в действии. Давайте для начала введём несуществующую пару логина и пароля. Например, 123:123.

    Ошибка при авторизации

    Мы увидим соответствующую ошибку.

    Теперь давайте зайдем под пользователем user. В нашей БД для него указан пароль 123. Пробуем.

    Успешная авторизация

    Успех! Нас автоматически перекинуло на главную страницу, где мы видим приветствие для данного пользователя!

    Безопасная система авторизации

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

    Хеширование паролей

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

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

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

    Для чего это делается? Да просто потому, что если сайт будет каким-то образом взломан, то злоумышленник в базе данных не найдёт паролей в открытом виде — только хеши. А так как из хеша получить пароль довольно сложно (при условии, что хеш-функция надежна и используется надёжный пароль), то пароль он не узнает. Следовательно:

    1. злоумышленник не сможет использовать пароль для входа на взломанный сайт;
    2. он также не сможет использовать этот пароль для входа под тем же логином и паролем в другие места (ведь довольно часто люди используют одинаковые пароли для всего).

    Вычисляются хеши с помощью хеш-функции. Хеш-функции при этом вычисляют хеши следуя алгоритмам хеширования. Сейчас в PHP для хеширования следует использовать функцию password_hash(), а для проверки хеша — password_verify(). Если вы в каком-то уроке увидите, что для хеширования паролей используется md5 — бегите оттуда, такие хеши вскрываются за несколько минут, она устарела ещё лет 10 назад.

    Авторизационные токены

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

    И потом пользователь с помощью cookie передает этот токен на сервер, где он сравнивается со значением в базе данных. Если они равны, то считаем пользователя авторизованным. Для чего? Дело в том, что куки могут быть похищены злоумышленниками (очень многими способами, не будем об этом в этой статье, кому интересно — погуглите). И если злоумышленнику попадет в руки токен — он не сможет получить исходный пароль. О том, почему это так важно, я уже объяснил.

    Заключение

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

    Ах да, чуть не забыл, все исходники к каждому уроку я для вашего удобства буду выкладывать на github – вот тут.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *