Files
ripe-cidr-collector/docs/plan-input-hardening.md
ayurishchevandClaude Sonnet 5 46d56654d8 Harden input handling and filter non-global DNS addresses
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>
2026-09-21 09:42:51 +03:00

40 lines
4.6 KiB
Markdown

# План: безопасность ввода и данных (п. 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` и повторное добавление).
## Изменения
1. `api_server.py`: сравнение токена по байтам; `parse_since` (ограничение длины, ASCII-цифры, перехват `OverflowError`).
2. `cidr_collector.py`: фильтр в `resolve_fqdn`, чтение `allow_non_global_ips`.
3. `README.md`: ограничения `since` (`400`), фильтрация адресов и ключ `allow_non_global_ips`, ключ в описании `config.json`.
4. `docs/review-2026-09-21.md`: статусы находок; итоги в `docs/summary-input-hardening.md`.
5. Тесты (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`.
## Откат
Изменения локальны (две функции и одно чтение конфигурации): возврат к предыдущему коммиту; схема данных не затрагивается.