Глава 37. Web Application Firewall (WAF)¶
Жесты защиты веб-приложений: anti-CSRF, фильтрация загрузок по типу содержимого,
лимиты тела запроса, накопительный движок <Waf> и XML-firewall. Все они доступны
в слое <Proxy> (а <Waf> — задокументированное расширение Redcoat).
37.1. Anti-CSRF (http.csrf.*) — защита веб-приложений (WAF)¶
Семейство http.csrf.* защищает веб-приложения за прокси (режим WAF / reverse-proxy) от CSRF-атак (Cross-Site Request Forgery). Принцип: прокси вставляет в формы приложения подписанный анти-CSRF токен (token.insert), а затем проверяет его в последующих POST-запросах (detection); запрос без валидного токена отклоняется.
Применимость: это WAF-фича (reverse-proxy перед защищаемым приложением), не SWG (forward-proxy для клиентов) — в forward-режиме прокси не владеет формами чужих сайтов.
| Свойство | Синтаксис | Семантика | Статус |
|---|---|---|---|
http.csrf.detection |
(yes\|no) |
Проверка анти-CSRF токена в form-type POST (urlencoded/multipart); невалидный/отсутствующий → 403 (блокировка WAF) | |
http.csrf.token.name |
(<name>) |
Имя поля токена (по умолчанию CSRF-Token) |
|
http.csrf.token.insert |
(yes\|no) |
Вставка скрытого токена в HTML-формы ответа | |
http.csrf.authentication_link |
(user_id\|client_ip\|user_id,client_ip) |
Какая часть идентичности привязывается к токену |
Синтаксис:
http.csrf.detection(yes|no)
http.csrf.token.name(<name>)
http.csrf.token.insert(yes|no)
http.csrf.authentication_link(<link>)
Слои: <Proxy> (WAF-фаза). Транзакции: HTTP form-POST.
Пример:
<Proxy>
; защитить /app/* от CSRF: вставлять токен и валидировать POST
url.path=/app/ http.csrf.token.insert(yes) http.csrf.detection(yes)
Соответствие BlueCoat 7.3: «Used in WAF deployments» — токен зашифрован и подписан криптографически-стойкой подписью;
detectionпроверяет form-type POST;token.nameпереопределяет defaultCSRF-Token;authentication_linkзадаёт содержимое токена.
Поведение:
- Токен: имеет вид base64url(payload).base64url(HMAC-SHA256(payload)), где payload = привязка:nonce:время_выдачи — подписан (не зашифрован), привязан к идентичности, ограничен по времени (срок жизни 1 час).
- Проверка (detection): на form-type POST токен извлекается из тела (по token.name) и проверяется (привязка к пользователю или IP клиента); невалидный → 403.
- Вставка (token.insert): после каждого <form> в HTML-ответе вставляется скрытое поле <input name={token.name} value=...>. authentication_link задаёт привязку (user_id/client_ip/оба).
- Отличие от BlueCoat: токен подписан HMAC (целостность и подлинность), но не зашифрован (BlueCoat шифрует). По рекомендациям OWASP для защиты от CSRF подписи достаточно (токен не секрет — нужна защита от подделки, а не от чтения).
Наблюдаемость (WAF): заблокированный CSRF-POST попадает в тот же конвейер, что и блокировки WAF — увеличивается счётчик заблокированных запросов и фиксируется источник (IP) на дашборде WAF, пишется запись в журнал безопасности (event=csrf_blocked rule_id=csrf-token-invalid) и формируется событие блокировки WAF (которое отображается в счётчиках и таблице событий WAF). Счётчики наполняются при включённом WAF; журнал безопасности пишется всегда. Управление — целиком через CPL (отдельная страница интерфейса не нужна).
Поведение:
detection(без токена → 403, с валидным → 200, с поддельным → 403),token.insert(GET вставляет токен → POST с ним → 200, без него → 403), а такжеtoken.name,authentication_linkработают как описано.
37.2. Apparent Data Type — фильтрация загрузок по типу содержимого (http.request.apparent_data_type.*)¶
Назначение. Контроль HTTP POST-загрузок по фактическому типу данных тела запроса (Apparent Data Type, ADT). Тип определяется content-sniffing'ом — по магическим байтам первых байт тела, — а НЕ по заявленному Content-Type или расширению файла. Это делает фильтр устойчивым к подмене MIME/расширения. Главный кейс — Reverse Proxy: ограничить, какие файлы клиент может загрузить на back-end-сервер. Слой <Proxy>, только HTTP POST, без участия ICAP.
Синтаксис.
http.request.apparent_data_type.allow(<type>, ...) ; allow-list
http.request.apparent_data_type.deny(<type>, ...) ; deny-list
Семантика.
- allow(t, …) — проходят только перечисленные типы; всё остальное, включая неопознанные content-sniffer'ом тела, — deny (консервативно: «разрешено только то, что явно разрешено»).
- deny(t, …) — блокируются только перечисленные типы; всё остальное проходит (инверсия allow).
- Заблокированный POST → 403 (Error::AccessDenied), с записью в security-лог (event=adt_blocked detected_type=<label> mode=allow|deny).
Распознаваемые типы (label). ~40 форматов по магическим байтам: 7ZIP ACE ARJ BMP BZ2 CAB(MSCAB/ISCAB) COMPRESS CPIO DAA EGG EML EXE FLASH GIF GZIP HTML ICC JPG LHA LZIP MACH-O MSDOC PDF PNG RAR RTF TAR TIF TNEF TTF TXT UUE XAR XML XZ ZIP. Тип можно указывать лейблом или расширением (pdf ≡ PDF); CAB — зонтичный (матчит MSCAB и ISCAB).
Особенности классификации (как в BlueCoat 7.3):
- XLSX/DOCX/PPTX (OOXML) классифицируются как ZIP, не MSDOC. Чтобы политика сработала на XLSX — матчите ZIP. MSDOC = только legacy OLE2 (.doc/.xls/.ppt/.msi).
- EXE покрывает и MZ, и редкий ZM; TIF — classic и BigTIFF; универсальный (fat) Mach-O детектится (коллизия с Java .class разрешена по nfat_arch).
- MRAR/MZIP (многотомные) неотличимы по содержимому от RAR/ZIP — детектятся как RAR/ZIP.
Примеры.
; Reverse-proxy: разрешить загрузку только изображений и PDF
<Proxy>
url.host=upload.example.com http.request.apparent_data_type.allow(JPG, PNG, GIF, PDF)
; Запретить загрузку исполняемых файлов и архивов (даже если переименованы)
<Proxy>
http.request.apparent_data_type.deny(EXE, MACH-O, ZIP, 7ZIP, RAR)
upload.example.com → 403; PNG-картинка → проходит. Во втором — переименованный в photo.jpg Windows-исполняемый файл всё равно определяется как EXE по сигнатуре MZ и блокируется.
Поведение. Таблица магических байтов тщательно сверена с авторитетными источниками (это контроль безопасности: неверная сигнатура означала бы молча пропущенную загрузку). Тело POST анализируется по содержимому, и при совпадении возвращается 403. Тело буферизуется для любого POST с телом, поэтому распознаются и сырые (не form) загрузки.
Условие http.request.apparent_data_type=<type>. Помимо действий, можно матчить тип в условии правила:
<Proxy>
http.request.apparent_data_type=PDF deny ; запретить PDF-загрузки
http.request.apparent_data_type=(EXE, ZIP) exception(upload_blocked)
CAB (без учёта регистра), списки и продвинутые шаблоны (подстановочные знаки, регулярные выражения, префиксы). Для запроса без тела или с нераспознанным типом совпадения нет (правило не срабатывает и не начинает реагировать на любой трафик).
Наблюдаемость (WAF): заблокированный POST попадает в тот же конвейер, что и блокировки WAF — увеличивается счётчик заблокированных запросов и фиксируется источник (IP) на дашборде WAF, пишется запись в журнал безопасности (event=adt_blocked detected_type=<label> mode=allow|deny) и формируется событие блокировки WAF (которое отображается в счётчиках и таблице событий WAF). Счётчики наполняются при включённом WAF; журнал безопасности пишется всегда. Управление — целиком через CPL (отдельная страница интерфейса не нужна, как у CSRF); блокировки видны на дашборде WAF.
Поведение: deny(PDF) → PDF-тело = 403, текст = 200; allow(PDF) → PDF = 200, текст = 403; условие =PDF deny → PDF = 403, текст = 200, GET = 200.
Известные ограничения:
- Сторона ответа http.response.apparent_data_type= пока не работает: условие фактически не срабатывает (парсится, но никогда не совпадает) — нужна детекция в фазе ответа. Сторона запроса (http.request.apparent_data_type=) реализована.
- Вложенный файл в multipart: детектор применяется к телу POST как есть; для multipart/form-data это распознаёт конверт, а не вложенный файл. Извлечение вложения пока не реализовано.
- Синтаксические формы действий [t,..](yes|no) и .t(yes|no) пока не разбираются (только каноническая (t,…)).
37.3. Лимиты размера тела запроса для WAF (http.request.body.max_size / .inspection_size)¶
Два WAF-действия <Proxy> ограничивают объём тела запроса.
http.request.body.max_size(N). Если тело запроса больше N байт — запрос блокируется с 413 Payload Too Large (канонический код BlueCoat). Семантика BlueCoat: тело больше лимита не обрабатывается.
max_size(50) → тело 120 байт = 403, 20 байт = 200.
http.request.body.inspection_size(N). Ограничивает сканирование WAF первыми N байтами тела (содержимое за этой границей не инспектируется). WAF обрезает тело по лимиту перед сканированием.
Парные условия http.request.body.inspection_size_exceeded=yes|no и http.request.body.max_size_exceeded=yes|no — истинны, если тело превысило соответствующий лимит; читают реальное состояние. Особенность: флаг выставляется уже после оценки правил текущего прохода, поэтому в фазе запроса того же прохода условие видит false — оно полезно в фазе ответа; для блокировки используйте сам max_size() (он применяет лимит напрямую).
Ограничения: действие inspection_size пока проверено не до конца — в тестовом окружении WAF не детектирует тело запроса даже при полном сканировании, поэтому наблюдать эффект обрезки тела пока невозможно; http.request.body.data_type() (переопределение типа тела для WAF) применяет безопасный отказ (нет движка детекции).
37.4. Расширение Redcoat: слой <Waf> и профили защиты¶
Это расширение Redcoat, которого нет в BlueCoat ProxySG 7.3. Жесты BlueCoat
http.request.detection.*относятся к<Proxy>; профильный WAF Redcoat настраивается отдельно в<Waf>.
Правила слоя и решение WAF¶
| Что | Слой | Поведение |
|---|---|---|
Жесты BlueCoat — http.csrf.*, http.request.apparent_data_type.*, http.request.body.*, http.request.detection.* |
<Proxy> |
Условия и действия обычного CPL-правила; слой исполняется раньше <Waf> |
Выбор профиля и параметры WAF — waf.profile(...), waf.enabled(yes|no), waf.log_extra(detail=brief|full), anomaly_score+=(<level>) |
<Waf> |
Применяются действия всех совпавших правил слоя |
<Waf> выбирает профиль запроса и применяет его настройки, но не блокирует по сумме очков. Сигнатуры и встроенные детекторы действуют по настройкам своих классов и строк Syntax Based Detection в выбранном профиле: после проверок ограничений и упорядочения находок решение принимает первый блокирующий хит. Вес записанных находок и anomaly_score+=(<level>) увеличивают waf.score для журнала, а не порог блокировки. Если в CPL профиль не выбран, может действовать привязка профиля к VDOM.
Глобальное включение WAF и waf.enabled(yes|no) в CPL — разные переключатели: при отключённом глобальном WAF слой и инспектор не исполняются. Глобальный monitor-режим сохраняет настроенные действия находок в журнале, но не применяет блокировки. В главной политике waf.action(...) и waf.anomaly_threshold(...) отклоняются валидатором; waf.paranoia(...) и waf.ruleset(...) не входят в грамматику. Не используйте их как рецепт настройки защиты.
<Waf>
; имя должно существовать в реестре профилей
waf.profile("Inline Standard Protection") waf.enabled(yes) waf.log_extra(detail=full)
; дополнительный вес только для журнала, без самостоятельной блокировки
url.path=/upload anomaly_score+=(moderate)
</Waf>
Для сигнатурной инспекции нужен установленный WAF-пак. Состав сигнатур и действие каждого класса определяются действующим паком и профилем; пример не обещает срабатывания конкретного SID.
Межслойный детект: условия detection.result/rule/threat_risk¶
http.request.detection.result=, …rule= и …threat_risk= в <Proxy> и <Exception> получают предварительные данные, если политика использует эти условия. До прохода CPL вычисляются теги совпавших правил <Waf> и эвристики на настройках выбранного профиля. Этот пре-проход не запускает сигнатуры WAF-пака и XML-инспектор: его результат не равен полному вердикту последующей WAF-ступени. В частности, http.request.detection.rule=<SID сигнатуры> здесь не обнаруживает сработавший SID из пака.
<Proxy>
; пример по предварительному результату эвристики SQLi
http.request.detection.result=sqli deny
Распознаваемые имена результата включают sqli, xss, rce, path_traversal, ssrf, ssti, deserialization, known_exploits и другие классы. clean допустимо как имя, но не выставляется автоматически при отсутствии находок. Слой <Exception> исполняется для ожидающего отказа <Proxy>; сам по себе он не является способом получить полный сигнатурный вердикт до WAF.
Сводка для аудита¶
<Waf>— расширение Redcoat для выбора профиля и параметров запроса; действия всех совпавших CPL-правил слоя учитываются, но пороговой блокировки по score нет.- Решение сигнатурного WAF определяется настройками классов и детекторов профиля, а не устаревшими
waf.action/waf.anomaly_thresholdглавной политики. - Условия
detection.*получают предварительные эвристики и CPL-теги, не итоговый вердикт сигнатурного/XML-прохода.
37.5. XML-firewall: детект XXE / XInclude / глубины вложенности (http.request.detection.xml.*)¶
Свойства слоя <Proxy>, инспектирующие XML-тело запроса (типичный кейс — SOAP/XML-API) и блокирующие вредоносный XML. Это BlueCoat-7.3-нативные жесты (не расширение Redcoat). Они действия (синтаксис с ()-аргументом, без =), а не условия — режим реакции задаётся аргументом.
| Свойство | Что детектирует |
|---|---|
http.request.detection.xml.xxe(block\|monitor\|ignore) |
XML External Entity — <!ENTITY … SYSTEM/PUBLIC …> и параметрические сущности (<!ENTITY % …>); вектор чтения файлов сервера / SSRF |
http.request.detection.xml.xinclude(block\|monitor\|ignore) |
директиву XInclude (элемент в ns http://www.w3.org/2001/XInclude); вектор включения файлов / SSRF |
http.request.detection.xml.node_depth(1-99) |
превышение глубины вложенности XML (анти-DoS «billion laughs» / глубокая рекурсия); по умолчанию 99 |
http.request.detection.xml.invalid(block\|monitor\|ignore) |
не-well-formed (битый) XML; режим как у xxe/xinclude, по умолчанию ignore |
http.request.detection.xml.cdata(yes\|no) |
модификатор (не самостоятельное действие): при yes инспектор разворачивает CDATA-секции и сканирует их содержимое теми же детекторами (XXE/XInclude) — ловит атаку, спрятанную в CDATA. Сам ничего не блокирует — блок даёт сработавший xxe/xinclude |
Режимы: block — запретить запрос и залогировать (deny, резолвится в <Exception>); monitor — пропустить и залогировать; ignore — пропустить без лога.
Пример — блокировать XXE и XInclude, ограничить глубину 10 для SOAP-эндпоинта:
<Proxy>
url.domain=soap.example.com \
http.request.detection.xml.xxe(block) \
http.request.detection.xml.xinclude(block) \
http.request.detection.xml.node_depth(10) \
allow
Результат (проверено сквозным тестом на работающем прокси): запрос с <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>… → 403 (страница отказа «XML external entity (XXE) detected»); безобидный <foo>hello</foo> → 200; XML с 15 уровнями вложенности при node_depth(10) → 403, с 5 уровнями → 200.
Как это работает (важно для перфа и точности):
- Инспекция запускается только если тело — XML: Content-Type
application/xml/text/xml/ суффикс+xml; при пустом Content-Type — по magic-bytes (<?xmlили ведущий<). Явный не-XML Content-Type подавляет magic-byte fallback — обычный текст с литералами<!ENTITY/xi:includeНЕ триггерит детект (привязка к XML-телу, а не к подстроке). - Парсер потоковый (streaming): никогда не разворачивает и не резолвит внешние сущности — XXE невозможен в самом инспекторе; один проход; жёсткие лимиты (размер тела, глубина, число элементов/атрибутов/сущностей).
- Тело инспектируется в распакованном виде; превышение лимита размера/глубины → явный отказ (deny-able), а не молчаливый пропуск.
Нюанс 1. Жесты допустимы только в
<Proxy>(по 7.3).Нюанс 2.
blockиспользует обычныйdeny(откладывается и резолвится в<Exception>— можно подставить кастомную страницу), а неforce_deny.Нюанс 3.
cdata(yes)— модификатор, а не самостоятельное действие: он лишь включает скан внутри CDATA, а блокировку даёт сработавшийxxe/xinclude.cdata(yes)без них ничего не запретит.Нюанс 4. Реализованы
xxe/xinclude/node_depth/invalid/cdata. Жестыhttp.request.detection.xml.schema.schema_name(...)(валидация против XSD) иhttp.request.detection.xml.xpath_validation(...)пока не реализованы — политика с ними будет отклонена при загрузке, а не выполнится молча (безопасный отказ, а не тихий пропуск).