Traefik как WAF
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 /plugins-local /plugins-local
COPY /coraza.conf /coraza/coraza.conf
COPY /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