X-API-Key compared as bytes (401 instead of 500 on non-ASCII), strict parsing of the diff "since" parameter (400 instead of 500), FQDN resolution keeps only global addresses (allow_non_global_ips to opt out). Adds the code review report and the plan/summary for this change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.6 KiB
4.6 KiB
План: безопасность ввода и данных (п. 2-4 ревью)
Источник: docs/review-2026-09-21.md, находки 2, 3 и 4.
Цель
Некорректный ввод не приводит к ответу 500, а адреса, которые не должны попадать в правила файрвола (loopback, частные, 0.0.0.0), не попадают в выдачу.
Дизайн
Находка 2: заголовок X-API-Key
- Сравнение токенов выполняется по байтам (
encode("utf-8")обеих сторон):secrets.compare_digestдляstrпринимает только ASCII и падает на других символах. - Результат: любой неверный ключ, в том числе нелатинский, даёт
401; поведение с верным ключом и без токена в окружении (503) не меняется.
Находка 3: параметр since (GET /addresses/diff)
- Курсором считается строка из ASCII-цифр длиной до 30 символов (
isascii() and isdigit()); более длинная или с юникод-цифрами трактуется как некорректное значение. - Разбор времени защищён от
OverflowError(крайние даты с поясом) вместе сValueError. - Все некорректные значения дают
400с прежним текстом;410остаётся для корректных, но вышедших за журнал значений (в том числе очень больших курсоров до 30 цифр).
Находка 4: фильтрация DNS-адресов
FQDNCollector.resolve_fqdnоставляет только глобальные адреса (ipaddress.ip_address(...).is_global; зона IPv6%ifотбрасывается перед разбором). Отфильтрованные значения пишутся в лог (WARNING, с перечнем).- Если после фильтрации адресов не осталось, поведение как при ошибке DNS: предупреждение, TTL не применяется, данные не удаляются.
- Обходной путь для внутренних имён: ключ
allow_non_global_ipsвconfig.json(по умолчаниюfalse;trueотключает фильтр). - Ранее собранные неглобальные адреса не удаляются сразу: они истекают по
ttl_days(для немедленной очистки -DELETE /fqdns/<имя>?purge=trueи повторное добавление).
Изменения
api_server.py: сравнение токена по байтам;parse_since(ограничение длины, ASCII-цифры, перехватOverflowError).cidr_collector.py: фильтр вresolve_fqdn, чтениеallow_non_global_ips.README.md: ограниченияsince(400), фильтрация адресов и ключallow_non_global_ips, ключ в описанииconfig.json.docs/review-2026-09-21.md: статусы находок; итоги вdocs/summary-input-hardening.md.- Тесты (2, всего 22): API (нелатинский ключ даёт 401;
sinceс юникод-цифрой, длинным числом и крайней датой даёт 400); сборщик (смесь глобальных, частных, loopback и зональных адресов фильтруется, сallow_non_global_ipsостаётся всё, если ничего не осталось - пустой результат).
Не входит
Находка 1 (пустая выдача после порчи без копий) - отдельное решение по поведению; находки 5-10 - отдельные доработки.
Проверка
Тесты в контейнере; вручную в контейнерном окружении повторить запросы из ревью (since=², длинное число, 0001-01-01T00:00:00+05:00, нелатинский X-API-Key), для DNS - сбор на копии с подставленными ответами getaddrinfo.
Откат
Изменения локальны (две функции и одно чтение конфигурации): возврат к предыдущему коммиту; схема данных не затрагивается.