Перейти к содержанию

Секреты, ошибки и журналы

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

Результатом урока станет договор конфигурации и наблюдения 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. Для каждого ресурса нужна соответствующая проверка. Не следует поднимать общую тревогу «всё сломалось», когда отказ относится только к необязательной справке.

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

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