Исправление границы ссылки сброса

КибербезопасностьBeginner
Практиковаться сейчас

Введение

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

Сначала вы восстановите тестовую запись обычным способом, увидите опасное поведение и исправите небольшой обработчик Python. Опыт Python не нужен: функции и условия объясняются до редактирования. Нужно уметь вводить команду в терминале и переключать вкладки.

Portal Web открывает приложение, Mailbox Web — имитацию почты, App Server — подготовленный код. Всё работает на одном независимом учебном экземпляре. Внешняя почта не отправляется. Токены, сравнение времени, транзакции, смена паролей и вход настоящие; маленькое приложение не является промышленной системой идентификации.

Восстановить запись обычным способом

На этом шаге вы перейдёте по письму и войдёте с новым паролем, определив нормальное поведение, которое должно сохраниться после исправления.

Учётная запись обозначает человека, а пароль подтверждает возможность входа. Если пароль забыт, ссылка временно разрешает его сменить. Токен ссылки — не сам пароль. В локальной почте можно действовать от имени владельцев вымышленных записей maya и noah. Обе начинают с StartPass42!. Используйте только учебные записи.

В Portal Web найдите Request a reset link. Введите maya в Recovery account и нажмите Send reset link. Появится Link requested. Open Local Mailbox. Обновите Mailbox Web: новое письмо сверху показывает получателя, Open reset link и строку токена.

Нажмите Open reset link в этом письме. В той же вкладке откроется форма с заполненными Token и Reset account. Не меняйте их; введите FirstPass42! в New password и нажмите Reset password. Ожидаются Reset accepted и HTTP 200.

HTTP — обмен запросами и ответами между браузером и сервером. Здесь 200 означает принятие, 403 — отказ, 500 — ошибку приложения. Ошибка соединения отличается от защитного отказа.

Откройте Portal Web, обновите при необходимости и заполните Sign in: Login account maya, Password FirstPass42!, затем Sign in. Ожидается Login accepted. Рабочее восстановление меняет настоящий пароль, а не только надпись на странице.

Потеряв ссылку, запросите новую той же формой и используйте последнее письмо. Срок действия — 30 минут. Опечатка или истечение срока не требуют перезапуска среды.

Наблюдать нарушение границы

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

Браузер отправляет поля с именами и значениями. Пользователь может менять их, поэтому сервер не должен доверять Reset account лишь потому, что письмо заполнило его. На сервере есть отдельная запись:

Поле Значение
account Запись, которой разрешён этот токен
expires_at Последнее допустимое время в секундах от фиксированного начала отсчёта
used 0 до использования, 1 после

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

Запросите новую ссылку для maya в Portal Web. Обновите Mailbox Web, откройте последнее письмо и задайте DemoPass42!. Снова откройте ссылку из того же письма и задайте ReplayDemo42!. Оба раза появляется Reset accepted. Второй запрос должен отклоняться: это повторное воспроизведение, применение уже исчерпанного разрешения.

Ещё раз откройте ту же ссылку. Замените Reset account на noah, New password на CrossDemo42! и нажмите Reset password. Запрос принят. В Portal Web войдите как noah с CrossDemo42!. Ссылка для maya изменила noah: сервер не связал токен с записью.

Наконец, в форме Portal Web укажите Token expired-demo, Reset account maya и New password ExpiredDemo42!, затем Reset password. Этот готовый токен истёк за час до подготовки среды, но тоже принят: сервер игнорирует часы. Ждать таймер не нужно.

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

Связать и погасить токен

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

Переключитесь в App Server, открытый в /home/labex/project/reset. cat показывает текстовый файл, имя которого передаётся аргументом. Прочитайте обработчик:

cat handler.py

Первая строка импортирует две готовые вспомогательные функции. def определяет функцию; имена в скобках — параметры, входные данные одного вызова. eligible(row, account, now) получает сохранённую запись токена, переданный аккаунт и время сервера. row["account"] читает поле. row is None означает отсутствие найденного токена. Блок if с отступом выполняется только при истинном условии; return False отказывает, а return True разрешает. Здесь блок Python задают четыре начальных пробела.

Сейчас функция возвращает True для любого существующего токена, что объясняет три уязвимости. Кроме того, reset вызывает unsafe_reset, меняющую пароль без погашения токена.

Исправление требует всех условий одновременно. == сравнивает значения, > означает больше, а and требует истинности обеих сторон. or разрешило бы любую сторону, позволяя верному свойству скрыть неверное. Будущий срок больше now; равенство уже означает истечение. used == 0 допускает только неиспользованный токен.

Готовая consume_and_reset начинает транзакцию SQLite, читает актуальный токен, вызывает вашу eligible и совместно фиксирует пароль и used = 1. Это атомарность: изменения успешно выполняются вместе. Блокировка ставится до чтения, чтобы два конкурирующих запроса не увидели одновременно неиспользованный токен. Реализовывать блокировки и хеширование паролей не нужно.

Откройте файл терминальным редактором nano:

nano handler.py

Замените содержимое следующим кодом. Стрелками и Backspace/Delete можно удалить старое и вставить полную замену. Точно сохраняйте пробелы и знаки; скобки позволяют разделить условие на строки.

from framework import unsafe_reset, consume_and_reset


def eligible(row, account, now):
    if row is None:
        return False
    return (row["account"] == account
            and row["expires_at"] > now
            and row["used"] == 0)


def reset(db, token, account, password):
    return consume_and_reset(db, token, account, password, eligible)

Сохраните через Ctrl+O, подтвердите handler.py клавишей Enter и выйдите через Ctrl+X. Каждый Web-запрос читает сохранённый код, поэтому перезапуск не нужен. При HTTP 500 снова откройте файл и сравните отступы, скобки и кавычки.

python3 запускает программу Python. Проверяющая программа принимает путь обработчика и запускает отдельное HTTP-приложение с временными данными SQLite. Она проверяет настоящий код, не исправляет его и не меняет действующие записи:

python3 /opt/reset-lab/check.py handler.py

Ожидаются одиннадцать строк PASS, в том числе valid reset accepted, replay denied и competing use has exactly one winner. FAIL называет нарушенное свойство. Отказ на все запросы провалит нормальный сброс и вход. Изменение браузерных полей или действующей базы не исправляет код, проверяемый на свежих данных. При необходимости снова откройте nano handler.py, исправьте, сохраните и повторите ту же проверку.

Доказать работоспособность восстановления

На этом шаге вы используете исправленный действующий сервис и сопоставите его результаты с изолированными проверками безопасности.

В Portal Web запросите новую ссылку для maya. Обновите Mailbox Web, откройте последнее письмо и задайте ResetSafe42!. Ожидаются Reset accepted и HTTP 200. Снова откройте ссылку из того же письма и попробуйте ReplayAgain42! для maya: ожидаются Reset denied и HTTP 403.

Запросите ещё одну новую ссылку maya. Откройте её, замените Reset account на noah и попробуйте BlockedCross42!. Ожидается отказ. Он не должен погасить неиспользованную ссылку владельца: снова откройте то же письмо, оставьте maya и задайте ResetSafe42!. Это должно сработать.

В форме попробуйте Token expired-demo, Reset account maya, New password BlockedExpired42! — ожидается отказ. Затем войдите в Portal Web как maya с ResetSafe42!. Login accepted доказывает, что неудачные попытки не меняли пароль. Также можно войти как noah с демонстрационным CrossDemo42!: другие записи сохраняются.

В App Server повторите регрессионные проверки на временных данных:

python3 /opt/reset-lab/check.py handler.py

Все одиннадцать должны пройти, включая два одновременных использования с одним успешным сбросом. Повторение не меняет действующие записи. Нужно сохранить разрешённое восстановление и запретить злоупотребление; отключённая страница или постоянный отказ не являются исправлением.

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

Резюме

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