Перейти до змісту

9.2 Захисні модулі — що насправді перевіряє кожен

[!IMPORTANT] 9.1 завершилася одним правилом: фільтруй дешево і рано, автентифікуй решту. Це модулі, що фільтрують, і кожен перевіряє рівно одну річ — sanity доводить, що повідомлення є легальним SIP, secfilter зіставляє рядки зі списками, pike рахує пакети від джерела, auth встановлює, хто їх надіслав. Жоден не інспектує вільнотекстовий вміст заголовків на shell- чи SQL-безпеку. Розуміння того, хто є хто, — це і є вся гра.

sanity — коректність форми, не безпека

sanity_check([msg_checks[, uri_checks]]), що викликається першою в request_route як route("sanity"), відповідає на одне питання: чи це легальний SIP? На провал sanity_reply() видає відповідний 4xx. Задокументовані перевірки — суто структурні: версія і схема R-URI, обов'язкові заголовки (To/From/CSeq/Call-ID/Via), збіг методу в CSeq із методом запиту та валідний integer, Content-Length рівний фактичному розміру тіла (та, що ловить усікання і request smuggling), значення Expires, Proxy-Require, парсовані URI, форма digest-credentials, дублікати To/From tags, наявний-парсований-з-branch перший Via та відповідність RFC 3261 (magic-cookie / lr).

Кожна перевірка — про форму, ніколи — про сенс. І саме ця відмінність є опорною точкою для 9.4:

Підрядок $(...) усередині User-Agent чи ' усередині From — абсолютно легальний SIP-токен. sanity_check() пропускає його незмінним.

sanity відхилить пакет, чий Content-Length бреше про своє тіло; він не відхилить — і не може — пакет, що протягує shell-метасимвол чи SQL-фрагмент, бо цей пакет — валідний SIP. Це синтаксичний шлюз — дешевий triage проти malformed-мотлоху, і нічого — проти ворожого-але-коректного вмісту.

secfilter — фільтр вмісту

secfilter — найближче в стоковому наборі до фільтрації вмісту: блек- і вайт-листи в пам'яті (завантажені з БД), що зіставляються родиною secf_check_*.

Функція Що зіставляє
secf_check_ip() source-IP проти блек-листа
secf_check_ua() User-Agent проти UA-списку
secf_check_from_hdr() / secf_check_to_hdr() user, domain, display-name з From/To
secf_check_contact_hdr() user / domain з Contact
secf_check_dst(string) destination-номер
secf_check_country(string) код країни (потрібен geoip)
secf_check_sqli_hdr(string) / secf_check_sqli_all() заборонені символи в одному значенні / по UA, From, To, Contact

Це ловить friendly-scanner-UA'и, відомі погані домени, toll-fraud-префікси й очевидні SQLi-проби. Але це статичне зіставлення патернів — рядки і фіксований набір заборонених символів. Воно підвищує вартість наївної атаки; воно не знає, що ваш downstream — БД, shell чи скрипт — зробить зі значенням. Фільтр, а не гарантія — і хибний шар для безпеки в стилі 9.4, що має відбуватися там, де ви використовуєте значення.

pike — детекція флуду

pike відповідає на питання: чи одне джерело шле надто швидко? Він трекає rate per-source-IP у shm через дерево з ключем побайтово по адресі (одна структура покриває IPv4 і IPv6). pike_check_req() — або pike_check_ip(ipaddr) для довільної адреси — зараховує кожен hit. Три параметри задають політику: sampling_time_unit (вікно в секундах, default 2), reqs_density_per_unit (дозволено за вікно, default 30), remove_latency (утримання неактивного джерела, default 120).

Повертане значення і є вся суть: 1 — не заблоковано (а також внутрішня помилка — fail-open), -1 — відомий флудер, -2новий флудер, перша детекція прямо зараз. Розщеплення -2/-1 дозволяє діяти один раз на першій детекції (залогувати, забанити), а далі дешево short-circuit'ити кожен наступний пакет від того ж джерела. Цей вердикт зазвичай іде прямо в патерн ipban з 9.3: pike детектує, htable — пам'ятає.

topoh / topos — обрізати recon

Не фільтри — topology hiding, прочитане тут як відмова в розвідці. Сканер читає Via, Record-Route, Contact і Route, щоб скласти карту вашої внутрішньої адресації; topoh (кодує або зрізає ці заголовки) і topos (ховає стан на боці сервера, повертає непрозорий токен) зменшують те, що витікає у відповідях, які ви таки надсилаєте. Механіка — в 8.1 topology hiding; вони тут, бо загартування edge — це настільки ж про що ви зливаєте у відповідях, наскільки й про що приймаєте в запитах.

auth — єдиний справжній trust boundary на UDP

Усе вище — евристика: жодне не встановлює, хто надіслав пакет, а на UDP source-IP не є ідентичністю (9.1). auth проводить межу ідентичності через SIP digest. Challenge-функції — www_challenge(realm, flags[, algorithms]) (401, registrar/UAS), proxy_challenge(...) (407, проксі), auth_challenge(...) (обирає за контекстом) — видають заголовок WWW-/Proxy-Authenticate; сторона перевірки (pv_www_authenticate / pv_proxy_authenticate / pv_auth_check) перераховує digest проти збереженого секрету й приймає лише за збігу. Захист від replay живе в challenge'і: обмежений час життя nonce (nonce_expire, default 300с), а qop додає клієнтський nonce-count, тож перехоплений Authorization не можна просто переграти. Саме це — а не якийсь блек-лист — зупиняє кейси REGISTER-hijack і toll-fraud. Фільтрація купує дешевше відхилення мотлоху; лише auth дозволяє довіряти трафіку, що лишився.

Підсумок

Модуль Що перевіряє Де Чого не робить
sanity SIP-синтаксис / коректність форми першим у request_route інспектувати вміст на shell/SQL-безпеку
secfilter поля проти списків; SQLi-набір символів рано, per request парсити намір; ловити те, чого нема в списку
pike rate запитів per source-IP per request, edge дбати про вміст чи ідентичність
topoh/topos (переписує вихідний стан) per message, edge відхиляти запити; автентифікувати
auth digest-ідентичність відправника після дешевих фільтрів фільтрувати флуди чи malformed-вхід

Зверху донизу — це доктрина 9.1, зроблена конкретною: відхиляй malformed-мотлох майже задарма (sanity), дропай відомі погані рядки й флуди (secfilter, pike), розкривай мало у відповідях (topoh/topos), витрачай digest-challenge (auth) лише на трафік, що пережив усе це. Прогалина, що стає очевидною: pike забуває флудера після remove_latency, а secf_check_ip() зіставляє лише список, який ви наповнюєте. Перетворення разової детекції на тривалий, поширюваний, кластерний бан — це 9.3.


← Зміст · ← 9.1 Поверхня атаки · Далі: 9.3 Динамічні блок-листи →