Секреты, ошибки и журналы
Защитные механизмы зависят от конфигурации, а работа сервиса оставляет сведения в ответах и журналах. Если ключ подписи попал в исходники или пароль отражён в ошибке, приложение теряет важную границу без изменения маршрута заметки.
Результатом урока станет договор конфигурации и наблюдения security-lab. Секреты остаются вне публичных файлов, посетитель получает короткую ошибку, а журнал позволяет связать запрос с результатом без записи его чувствительного содержания.
Источник конфигурации
LAB_SECRET передаётся через окружение процесса. Приложение не выбирает его из HTTP-параметра и не использует общее значение «на случай отсутствия». Ошибка настройки должна остановить запуск до обслуживания сессий.
import os
secret = os.environ.get("LAB_SECRET", "")
if len(secret) < 32:
raise RuntimeError("Настройте случайный LAB_SECRET")
https_mode = os.environ.get("LAB_HTTPS", "0") == "1"
Проверка длины замечает часть явных ошибок. Она не измеряет энтропию: тридцать две одинаковые буквы проходят условие, но не становятся хорошим ключом. Читатель генерирует случайное значение для собственного стенда и хранит его по политике окружения.
Переменные окружения тоже не являются магическим сейфом. Они могут быть доступны процессу и инструментам администрирования, а диагностическая выгрузка способна их раскрыть. Разделение с исходниками уменьшает случайное распространение, но не заменяет права окружения.
Локальный .env, если он используется читателем, исключается из Git и публичного дерева. При этом .gitignore не отзывает уже опубликованный секрет. Если значение попадало в историю или артефакт, нужно рассматривать утечку и менять его, а не только удалять текущую строку.
Явные режимы
Учебный HTTP и внешний HTTPS имеют разные настройки cookie. Флаг LAB_HTTPS задаёт известный режим; он не меняется по непроверенному заголовку браузера. Это помогает отделить конфигурацию окружения от сведений текущего запроса.
Аналогично список доверенных хостов происходит из настройки приложения. Посетитель не должен включить новый host параметром формы. В заключительном исходнике локальный список ограничен localhost и 127.0.0.1; будущая внешняя площадка потребует собственного согласованного значения.
Не смешивайте режим разработчика с успешным входом администратора. Debug относится к процессу и обработке ошибок, а не к правам заметки. В локальных примерах запуск не включает интерактивный debugger; внешний процесс требует отдельного способа обслуживания.
Идентификатор запроса
Для диагностики создадим случайный requestId на сервере. Он не является сессией и не даёт права читать заметку. Его задача — связать видимую ошибку с одной записью наблюдения.
import time
import uuid
g.request_id = uuid.uuid4().hex
g.started = time.monotonic()
Эти значения создаются в начале before_request. В ответ добавляется X-Request-ID, а API также получает requestId в коротком объекте ошибки. Можно передать эту строку сопровождающему без передачи пароля или cookie.
Если внешняя инфраструктура использует свой идентификатор, понадобится договор доверия и ограничение формата. В нашем примере браузер не выбирает произвольную строку журнала. Это уменьшает неожиданное содержание и сохраняет связь с серверным обращением.
Выбранный журнал
Записываем известное имя endpoint, код ответа и длительность. Не используем full_path или body: они могут содержать параметры формы, токен или текст личной заметки.
print(json.dumps({
"requestId": g.request_id,
"route": request.endpoint,
"status": response.status_code,
"durationMs": round((time.monotonic() - g.started) * 1000),
}, ensure_ascii=False), flush=True)
JSON представляет поля структурированно. Но это не разрешение сохранять любые значения: корректный формат строки и допустимый состав сведений являются разными условиями. Мы заранее выбрали небольшие поля.
Запись 403 на edit помогает увидеть отказ формы; запись 503 на reference — проблему интеграции. Без полного текста запроса не всегда можно восстановить всё содержание ситуации. Это осознанный предел: для диагностики сначала используем категорию и контролируемый повтор на стенде.
Принципы исключения паролей, токенов и других чувствительных значений описаны в OWASP Logging. В нашей реализации они применяются к конкретным полям приложения.
Другие слои журнала
Сервер разработки и обратный прокси могут писать путь независимо от нашего JSON. Поэтому политика приложения не охватывает весь журнал окружения автоматически. Нужно отдельно проверить формат доступа, ошибки библиотеки и внешний сборщик.
В заключительном локальном запуске обычный журнал доступа Werkzeug отключается, чтобы он не дублировал адресную строку. Это учебная настройка наблюдения, а не предложение отключить все ошибки процесса. Для промышленного окружения нужен согласованный набор журналов и доступ к ним.
Не публикуйте файл журнала рядом со static. Даже выбранные короткие сведения могут раскрывать структуру использования и внутренние маршруты. Хранение, сроки и права на чтение относятся к активам системы так же, как база.
Ошибка посетителю
Для известного HTTP-отказа возвращается выбранная короткая фраза. Для неожиданного исключения — 500 и requestId. Трассировка, значение LAB_SECRET и сообщение внешнего сервиса не отправляются в браузер.
При этом одинаковая фраза не означает одинаковую причину. Сопровождающий смотрит endpoint, код, время и состояние зависимости. Если нужны подробности, их нужно получать контролируемым способом с фильтрацией, а не добавлять repr(error) в общий ответ.
Проверьте известный отказ, отсутствие зависимости и неожиданную локальную ошибку. В каждом случае тело ответа должно соблюдать договор. Отдельно посмотрите stdout: корректная страница не доказывает, что пароль не оказался в другом канале.
Связь журнала и данных
Представим запрос с неверным токеном и длинным текстом заметки. Журнал сообщает 403 и endpoint, но не сохраняет этот текст. Для воспроизведения достаточно отдельно заданного учебного значения и состояния сессии, а не копии личного содержания.
При внутреннем исключении финальный обработчик записывает класс ошибки с requestId. Он не вставляет str(error) в публичный ответ. Сведения внешней библиотеки могут содержать путь или ввод, поэтому их состав также нужно анализировать.
Наблюдение health показывает доступность маршрута процесса, но не подтверждает готовность базы и справочного stub. Для каждого ресурса нужна соответствующая проверка. Не следует поднимать общую тревогу «всё сломалось», когда отказ относится только к необязательной справке.
При передаче журнала сопровождающему определите, какой период и какие поля нужны. Полная выгрузка всего окружения часто содержит больше данных, чем требуется для одной категории отказа. Минимальный полезный снимок проще защитить и сопоставить с конкретным выпуском.
Теперь конфигурация и наблюдение имеют понятные источники и ограничения. Следующий урок рассмотрит код библиотек, от которых зависят хеширование, шаблоны и обработка изображений.