Перейти к основному содержимому

Traefik как WAF

· 5 мин. чтения

WAF (Web Application Firewall) — это инструмент, который защищает веб-приложения от различных видов атак, анализируя и фильтруя HTTP-запросы. Вот основные преимущества и случаи, когда WAF может быть полезен:

Отдельным плюсом является возможность использования WAF без изменения кода приложения. WAF работает как промежуточное звено между пользователем и сервером, поэтому разработчикам не нужно вносить изменения в код приложения. Это экономит время и снижает вероятность ошибок.

Однако, библиотека про которую пойдет речь в этой статье, Coraza, также может быть использована внутри приложения.

Настройка WAF

Вы можете писать правила для WAF самостоятельно. Однако, это может быть сложно и требует определенных знаний в области безопасности. Поэтому, в большинстве случаев, проще использовать готовые правила, которые предоставляются поставщиком WAF. Вместе с Coraza, вы можете использовать правила из OWASP Core Rule Set. Он содержит набор правил для защиты от распространенных угроз.

  • SQL Injection (SQLi) — передача в запросе SQL-код, выполняющийся впоследствии на сервере.
  • Cross Site Scripting (XSS) — внедрение вредоносного кода на страницу, который выполнится в браузере пользователя.
  • Local File Inclusion (LFI) — внедрение вредоносного кода в файлы на сервере.
  • Remote File Inclusion (RFI) — внедрение вредоносного кода из внешнего источника.
  • PHP Code Injection — внедрение PHP-кода в запрос.
  • Java Code Injection — внедрение Java-кода в запрос.
  • HTTPoxy — атака на сервер, использующая переменные окружения.
  • Shellshock — атака на сервер, использующая уязвимость в bash.
  • Unix/Windows Shell Injection — внедрение команд в запрос.
  • Session Fixation — атака на сессию пользователя.
  • Scanner/Bot Detection — обнаружение сканеров и ботов.
  • Metadata/Error Leakages — обнаружение утечек метаданных и ошибок.

Установка Coraza

Coraza — это плагин для Traefik, который добавляет WAF-функционал.

Плагины в Traefik можно устанавливать динамически, просто указав их в конфигурации. А можно предварительно скачать плагин и указать локальный путь к нему. В этой статье мы рассмотрим второй вариант.

FROM alpine:3.21 AS build
ARG NAME=jcchavezs/coraza-http-wasm-traefik
ARG PLUGIN_MODULE=github.com/${NAME}
ARG ARTIFACT_NAME=coraza-http-wasm
ARG PLUGIN_VERSION=v0.3.0
RUN apk update && \
apk add zip wget git && \
mkdir -p /plugins-local/src/${PLUGIN_MODULE} && \
wget https://${PLUGIN_MODULE}/releases/download/${PLUGIN_VERSION}/${ARTIFACT_NAME}-${PLUGIN_VERSION}.zip -O /tmp/plugin.zip && \
unzip /tmp/plugin.zip -d /plugins-local/src/${PLUGIN_MODULE} && \
wget https://raw.githubusercontent.com/${NAME}/refs/tags/${PLUGIN_VERSION}/.traefik.yml -O /plugins-local/src/${PLUGIN_MODULE}/.traefik.yml
RUN wget https://raw.githubusercontent.com/corazawaf/coraza/v3/dev/coraza.conf-recommended -O coraza.conf && \
git clone https://github.com/coreruleset/coreruleset

FROM traefik:v3.3
COPY --from=build /plugins-local /plugins-local
COPY --from=build /coraza.conf /coraza/coraza.conf
COPY --from=build /coreruleset /coreruleset

Настройка Coraza

Теперь, когда плагин установлен, нужно настроить его. Для этого создадим два файла конфигурации: статический и динамический.

api:
insecure: true

entryPoints:
web:
address: :80

providers:
file:
filename: /etc/traefik/config-dynamic.yaml

log:
level: ERROR

experimental:
localPlugins:
coraza:
moduleName: github.com/jcchavezs/coraza-http-wasm-traefik
settings:
mounts:
- /coraza:/coraza
- /coreruleset:/coreruleset

Здесь, стоит обратить внимание на настройку settings.mounts в строках 19-22. Traefik ограничивает для плагина доступ к файловой системе. Поэтому, если плагин должен читать файлы, их нужно предварительно примонтировать.

http:
routers:
httpbin:
rule: PathPrefix(`/`)
service: httpbin
entryPoints:
- web
middlewares:
- waf

services:
httpbin:
loadBalancer:
servers:
- url: http://httpbin:8000

middlewares:
waf:
plugin:
coraza:
directives:
- Include /coraza/coraza.conf
- Include /coreruleset/crs-setup.conf.example
- Include /coreruleset/rules/*.conf
- SecRuleUpdateTargetById 932130 "REQUEST_HEADERS"
- SecRuleEngine On

В динамическом конфиге мы указываем правила, которые должен применить WAF. В данном случае, мы используем правила из OWASP Core Rule Set.

Запуск

Теперь, когда все настроено, можно запустить Traefik с плагином Coraza.

Для этого, я предлагаю использовать Docker-compose. Создайте файл docker-compose.yml и добавьте следующий код:

version: '3.7'

services:
traefik:
build:
context: .
dockerfile: Dockerfile
ports:
- 8080:80
command:
- "--configFile=/etc/traefik/config-static.yaml"
volumes:
- ./:/etc/traefik
depends_on:
- httpbin

httpbin:
image: mccutchen/go-httpbin:v2.9.0
environment:
- APP_NAME=httpbin
- MAX_BODY_SIZE=15728640 # 15 MiB
command: [ "/bin/go-httpbin", "-port", "8000" ]
ports:
- 8000:8000

Обратите внимание, что доступны оба контейнера и Traefik, и httpbin. Таким образом мы можем проверить, как работает WAF.

Для этого мы можем отправить запрос на httpbin, который содержит SQL-инъекцию. Например:

curl "http://localhost:8000/post" -i -XPOST -d"id=1%3B Drop TABLE users"

В ответ мы получим:

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Type: application/json; encoding=utf-8
Date: Fri, 24 Jan 2025 17:02:06 GMT
Content-Length: 501

{
"args": {},
"headers": {
"Accept": [
"*/*"
],
"Content-Length": [
"24"
],
"Content-Type": [
"application/x-www-form-urlencoded"
],
"Host": [
"localhost:8000"
],
"User-Agent": [
"curl/8.7.1"
]
},
"method": "POST",
"origin": "192.168.97.1:50006",
"url": "http://localhost:8000/post",
"data": "id=1%3B Drop TABLE users",
"files": null,
"form": {
"id": [
"1; Drop TABLE users"
]
},
"json": null
}

Видно, что данные с SQL-инъекцией были обработаны HTTPbin. Т.е., если бы мы пробросили данные из запроса в базу данных, то таблица users была бы удалена.

Теперь давайте отправим такой же запрос, но через Traefik:

curl "http://localhost:8080/post" -i -XPOST -d"id=1%3B Drop TABLE users"

В ответ мы получим:

HTTP/1.1 403 Forbidden
Date: Fri, 24 Jan 2025 17:05:22 GMT
Content-Length: 0

А в логах Traefik:

ERR [client "192.168.97.1"] Coraza: Warning. SQL Injection Attack Detected via libinjection [file "/coreruleset/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "8813"] [id "942100"] [rev ""] [msg "SQL Injection Attack Detected via libinjection"] [data "Matched Data: 1;Tnn found within ARGS:id: 1; Drop TABLE users"] [severity "critical"] [ver "OWASP_CRS/4.11.0-dev"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-sqli"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/152/248/66"] [tag "PCI/6.5.2"] [hostname ""] [uri "/post"] [unique_id "JieKvlSfwSgKEuMGxSH"] entryPointName=web middlewareName=waf@file middlewareType=wasm routerName=httpbin@file
ERR [client "192.168.97.1"] Coraza: Warning. Detects MySQL UDF injection and other data/structure manipulation attempts [file "/coreruleset/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "9197"] [id "942350"] [rev ""] [msg "Detects MySQL UDF injection and other data/structure manipulation attempts"] [data "Matched Data: ; Drop TABLE found within ARGS:id: 1; Drop TABLE users"] [severity "critical"] [ver "OWASP_CRS/4.11.0-dev"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-sqli"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/152/248/66"] [tag "PCI/6.5.2"] [hostname ""] [uri "/post"] [unique_id "JieKvlSfwSgKEuMGxSH"] entryPointName=web middlewareName=waf@file middlewareType=wasm routerName=httpbin@file
ERR [client "192.168.97.1"] Coraza: Warning. Detects concatenated basic SQL injection and SQLLFI attempts [file "/coreruleset/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "9236"] [id "942360"] [rev ""] [msg "Detects concatenated basic SQL injection and SQLLFI attempts"] [data "Matched Data: 1; Drop TABLE found within ARGS:id: 1; Drop TABLE users"] [severity "critical"] [ver "OWASP_CRS/4.11.0-dev"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-sqli"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/152/248/66"] [tag "PCI/6.5.2"] [hostname ""] [uri "/post"] [unique_id "JieKvlSfwSgKEuMGxSH"] entryPointName=web middlewareName=waf@file middlewareType=wasm routerName=httpbin@file
ERR [client "192.168.97.1"] Coraza: Access denied (phase 2). Inbound Anomaly Score Exceeded (Total Score: 15) [file "/coreruleset/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "11553"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 15)"] [data ""] [severity "emergency"] [ver "OWASP_CRS/4.11.0-dev"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [tag "OWASP_CRS"] [hostname ""] [uri "/post"] [unique_id "JieKvlSfwSgKEuMGxSH"] entryPointName=web middlewareName=waf@file middlewareType=wasm routerName=httpbin@file

Таким образом, WAF успешно обнаружил и заблокировал SQL-инъекцию.

Вот еще несколько примеров запросов, которые можно отправить через Traefik, чтобы проверить работу WAF:

  • Remote Command Execution: Unix Shell Code Found

    curl "http://localhost:8080/anything?foo=/etc/passwd&bar=/bin/sh" -i
  • Path Traversal Attack

    curl "http://localhost:8080/anything?foo=../../../../etc/passwd" -i
  • SQL Injection Attack Detected via libinjection

    curl "http://localhost:8080/anything?foo=1%27%20OR%20%271%27%3D%271" -i