SkillGridИзучай computer science на практике

Аутентификация: пароли, факторы и токены

Хранение паролей, которое переживает утечку базы, почему TOTP это не SMS, сколько на самом деле стоит JWT и почему OAuth — про делегирование, а не про вход.

15 мин · 50 XP

Ты уже знаешь разницу между хешированием и шифрованием: хеш нельзя превратить обратно во вход, шифрование можно. Именно поэтому сайт хранит хеш пароля, а не сам пароль. Чего тот урок не покрывал — той части, от которой зависит, защищает ли сохранённый хеш кого-нибудь на практике. Этот урок про неё и про всё, что происходит вокруг поля с паролем. Всё вышесказанное предполагает, что атакующий снаружи. Неудобная часть урока в том, что самый частый способ войти — действительный аккаунт, которым пользуется не тот, кто должен, или действительный пароль, введённый на странице, которая не была настоящей. Поэтому и меры, которые выглядят непарадно, — менеджер паролей, второй фактор, короткий срок жизни сессии, — на практике делают больше работы, чем любая остроумная криптографическая схема.

SHA-256 — неподходящий инструмент для паролей, и не потому, что он слабый. Он слаб здесь потому, что он быстрый. Современная видеокарта перебирает порядка 10^11 хешей SHA-256 в секунду, поэтому любой восьмисимвольный пароль падает за секунды. Хешу пароля нужно обратное свойство: он обязан быть медленным нарочно и медленным так, чтобы это нельзя было откупить дополнительным железом. Именно это делают scrypt и argon2id. Параметры стоимости здесь не украшение, а вся защита, и их приходится поднимать по мере роста скорости железа.

GPU, single card, offline guessing against a leaked dump

algorithm        cost setting        guesses/sec     one 8-char password
---------------  -----------------  --------------  ---------------------
SHA-256          plain               10^11           seconds
MD5              plain               10^11           seconds
bcrypt           cost 12             10^3            ~ 30 years
scrypt           N=2^15, r=8, p=1  10^2            ~ 300 years
argon2id         m=64 MiB, t=3       10^1            ~ 3000 yearsГрубые порядки величины для офлайн-перебора по утёкшему дампу на одной современной видеокарте. Считай эти числа формой, а не догмой: точные скорости меняются каждый год, разрывы между строками — нет.
UserAppDatabaseusername and passwordsalt = random 16 bytesstored = scrypt(password, salt, N=2^15)save username, salt, storedsalt is stored next to the hash, so it is not a secretsame password againread salt and stored valuerecompute with the same salt and compareone guess still costs the attacker 100 ms

Многофакторная аутентификация означает доказать две вещи разной природы: то, что ты знаешь, то, что у тебя есть, или то, чем ты являешься. Пароль плюс секретный вопрос — это один фактор, спрошенный дважды. На практике важнее более узкий вопрос, чем «сколько у тебя факторов»: может ли атакующий, разговаривающий с тобой прямо сейчас, передать твой второй фактор настоящему сайту, пока тот ещё действителен? Коды передать можно. Ключи — нельзя. Практическое различие — не абстрактная стойкость, а наличие короткоживущего секрета, который атакующий может переслать дальше.

После входа серверу нужно это помнить. Он может хранить непрозрачный случайный токен и искать его при каждом запросе, а может выдать тебе подписанный набор утверждений и проверять подпись вместо базы. Второе — это JWT, и он популярен по настоящей причине: любой сервер в парке может проверить его, не обращаясь к общему хранилищу. Цену называют реже. JWT действителен до истечения срока, потому что сервер умеет только проверять подпись, а подпись не знает, что ты нажал «выйти». Досрочный отзыв означает список отозванных, то есть ровно то состояние, от которого ты пытался уйти. Компромисс в одной таблице. JWT — не лучший токен сессии; это токен сессии, который купил отсутствие состояния ценой возможности отзыва. «Запомнить меня» — не галочка удобства, а решение о сроке жизни, принятое за пользователя. Долгоживущий токен, переживающий закрытие браузера, — это учётные данные на устройстве, которое ты не контролируешь, и обращаться с ним надо соответственно: привязать к устройству, дать возможность отозвать со страницы аккаунта и обнулять при смене пароля. Честная версия этой функции держит сессию долгой, но снова спрашивает пароль перед любым разрушительным действием. Нечестная версия держит вечный токен, который не убивает ничто, кроме сброса пароля, и о нём никто не вспоминает до продажи ноутбука. OAuth отвечает на вопрос, не имеющий отношения к тому, кто ты: как позволить одному сервису действовать от моего имени в другом сервисе, не отдавая ему свой пароль? Ты отправляешь пользователя к провайдеру, провайдер просит одобрить конкретный набор разрешений и возвращает код, который обменять на токен может только запрашивающее приложение. Токен несёт область действия и срок. Чего OAuth намеренно не определяет — как доказать личность, поэтому поверх него существует OpenID Connect как тонкий слой идентичности. Следствие стоит запомнить: сервис, который пускает тебя по голому токену OAuth и больше ни по чему, принимает решение об аутентификации на основе механизма авторизации.

Практика

  1. База хешей паролей утекает. Каждое сохранённое значение — это scrypt(пароль, соль) с параметрами, на которые уходит около 100 мс на одно вычисление. Какое утверждение верно?

    Подсказки
    • У атакующего уже есть сохранённые значения. Соль лежит прямо рядом с ними, поэтому секретом она быть не может.
    • Дорогим для атаки хеш пароля делает не секретность, а намеренная стоимость одного перебора.
  2. Сопоставь каждый механизм с тем, что он на самом деле даёт.

    Подсказки
    • Для каждого формата токена задай один вопрос: после выдачи сервер всё ещё может его забрать до истечения срока?
    • Для каждого второго фактора задай другой вопрос: есть ли короткоживущее значение, которое атакующий успеет переслать настоящему сайту, пока оно действительно?
  3. Расставь по порядку поток авторизационного кода OAuth.

    1. 1провайдер возвращает токен доступа с одобренной областью действия и сроком
    2. 2приложение обменивает код, предъявляя секрет клиента, и код одноразовый
    3. 3провайдер аутентифицирует пользователя и показывает, что именно запрашивается
    4. 4приложение вызывает API этим токеном, и пароль пользователя нигде не участвовал
    5. 5пользователь одобряет, и провайдер возвращает короткоживущий авторизационный код
    6. 6приложение отправляет пользователя к провайдеру с client_id и запрошенной областью действия
    Подсказки
    • Ничего нельзя выдать раньше, чем сервис решит, кто спрашивает и согласился ли пользователь.
    • Секрет клиента используется один раз, когда приложение меняет короткоживущий код на токен, а токен идёт последним.

Вопросы по этому уроку

Это бесплатно?

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

На какой возраст рассчитано?

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

Нужно ли уже уметь программировать?

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

Нужно что-то устанавливать?

Нет. Всё работает в браузере: теория, практика и интерактивные симуляции. Скачивать нечего и настраивать нечего.

Сколько времени занимает один предмет?

Урок — от 10 до 15 минут. Грейд «джун» это примерно 12–20 уроков, то есть несколько недель регулярных занятий, а не один вечер.

Обязательно идти по порядку?

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