WAF and injection protection
1) Why WAF in the API era
Even with rigorous validation and parameterization, injections occur due to:- "long tails" of integrations (legacy code, affiliate webhooks),
- parsing discrepancies (proxy ↔ framework),
- new protocol/obfuscation bypass techniques.
- WAF gives an early deny and "virtual patching" boundary before code release, but does not replace secure development.
2) Threat Model: API Injection Types
SQLi/ORMi: classic/boolean/time-based/stacked; blind through delays.
NoSQLi (Mongo/Elastic): operators' $ ne/$ gt ', JSON injection, regex-DoS.
Command Injection/RCE: shell metacharacters, argument substitution, unsafe deserialization → code exec.
XXE: external entities/DTDs in XML.
SSRF: access to '169. 254. 169. 254 '/internal services; DNS-rebinding.
Template Injection: Jinja/Thymeleaf/Handlebars; `{{77}}`.
LDAP/EL Injection, XPath, Header injection (CRLF), Path traversal.
GraphQL-specific: '__ schema' introspection, depth/complexity of queries.
JSON/JS-specific: Prototype Pollution ('__ proto __', 'constructor').
gRPC/Protobuf: oversized messages, field smuggling through schema mismatch.
3) WAF architectures
CDN-WAF perimeter: fast geo/ASN filtering, basic bot control, cache/anti-padding.
Perimeter L7 (NGINX/Envoy/APISIX/Kong): accurate parsing, deep rules, integration with PDP/limits.
Sidecar/mash (Envoy WASM/Filter): per-service, close to the data, less false positive for internal APIs.
Recommendation: dual layer model (CDN-WAF + L7 WAF).
4) Parsing, normalization and anti-bypass
WAF should see the same canonical representation as the application:- Path normalization ('/a/% 2e% 2e/b '→ failure),' UTF-8 '/Unicode confusables, NUL bytes.
- Single decoding: URL-/HTML-/Unicode-/Base64-layers, prohibition of double decoding.
- Restrictions: 'max _ headers', 'max _ header _ size', 'max _ body _ size', 'max _ args', JSON depth, multipart limit, 2x gzip/zip bombs forbidden.
- Content-Type policies: 'application/json' only on JSON endpoints; reject "polyglot."
5) Rule models
Negative (signatures): OWASP CRS (SQLi/XSS/SSRF/Java/Node RCE, etc.). Quick Start.
Positive (allow-list): strict schemes (JSON Schema/Protobuf), types and ranges; on routes.
Abnormal/scoring: summation of "suspicious" signs → blocking threshold.
Contextual: different profiles for 'POST/payments' and 'GET/status'; less FP.
6) Protection units (in a bundle)
1. Schemas and types: JSON Schema/Protobuf validation to business logic.
2. Parameterization: prepared expressions, ORM bindings, concatenation prohibition.
3. Output-escaping: HTML/JS/SQL contextual.
4. Body policies: Content-Type, size, multipart restrictions, prohibition of binaries on JSON handles.
5. WAF rules: CRS + custom negative/positive.
6. Rate/Quota/Concurrency: brute/turtle DDoS suppression, protective captchas/challenges for public forms.
7. Network isolation: egress policies for SSRF (deny RFC1918/metadata/Unix sockets).
8. Headers-hygiene: 'X-Content-Type-Options: nosniff', 'Content-Security-Policy' for front, 'Referrer-Policy'.
9. GraphQL guard: limits of depth/complexity, prohibition of introspection in the prod (or role-gate).
7) Configuration examples
7. 1 NGINX + ModSecurity (OWASP CRS)
nginx load_module modules/ngx_http_modsecurity_module.so;
modsecurity on;
modsecurity_rules_file /etc/modsecurity/modsecurity.conf;
modsecurity_rules '
SecRuleEngine On
Подключаем CRS
Include /etc/modsecurity/crs/crs-setup.conf
Include /etc/modsecurity/crs/rules/.conf
Позитивные правила: только JSON и ограничение размера
SecRule REQUEST_HEADERS:Content-Type "!@rx ^application/json($;)" "id:10001,phase:1,deny,status:415,msg:'Only JSON allowed'"
SecRequestBodyLimit 1048576
SecRequestBodyNoFilesLimit 1048576
Блок локальных адресов (SSRF)
SecRule REQUEST_HEADERS:Host "@ipmatch 127.0.0.0/8 10.0.0.0/8 169.254.0.0/16 192.168.0.0/16" \
"id:10002,phase:1,deny,status:403,msg:'Blocked private range'"
';
server {
listen 443 ssl http2;
server_name api.example.com;
client_max_body_size 1m;
proxy_request_buffering on; # защита от slow-POST proxy_read_timeout 300ms;
proxy_connect_timeout 100ms;
location /v1/ {
proxy_pass http://app_backends;
}
}
7. 2 Envoy HTTP WAF (WASM + JSON Schema + SSRF egress-deny)
yaml http_filters:
- name: envoy.filters.http.wasm typed_config:
config:
vm_config: { vm_id: waf, code: { local: { filename: /plugins/waf.wasm } } }
configuration:
"@type": type.googleapis.com/google.protobuf.Struct value:
crs_profile: "strict"
deny_patterns: ["(?i)union.select", "(?i)(sleep benchmark)\\s\\("]
json_schema:
"/v1/payments:create": "/schemas/payments_create.json"
- name: envoy.filters.http.router
Egress SSRF guard (L4): deny private ranges from gateway filter_chains:
- filters:
- name: envoy.filters.network.tcp_proxy typed_config:
stat_prefix: egress cluster: internet access_log: [...]
tunneling_config:
hostname: "%REQ(:authority)%"
transport_socket:
name: envoy.transport_sockets.tls
7. 3 APISIX: type restriction and anti-obfuscation
yaml routes:
- uri: /v1/
plugins:
cors: { allow_origins: "https://app.example.com" }
request-validation:
body_schema:
{"type":"object","properties":{"amount":{"type":"number","minimum":1}},"required":["amount"]}
uri-blocker:
block_rules: ["..","%2e%2e","%2f..","\\x00"] # traversal/NULL proxy-rewrite:
headers:
set:
X-Content-Type-Options: "nosniff"
8) Tuning and reducing false positives (FP)
Per-route profiles: strict rules only where appropriate (e.g. '/search 'allows '/'%').
Shadow/Report-Only: log responses before the block; A/B comparison of metrics.
Custom allow lists for "noisy" legitimate parameters.
Scoring: Block only when indicator sum> threshold.
Experiments: a small percentage of traffic to new rules → auto-rollback.
9) Observability and incidence
Метрики: `waf_block_total{rule}`, `waf_anomaly_score`, `request_body_rejected_total`, `schema_violation_total`, `ssrf_block_total`.
Logs (sampled): rule, part of the request (edited), 'trace _ id', 'tenant', 'route', reason. Disguise PII/secrets.
Dashboards: top rules/paths, FP clusters, dynamics after release.
Incidents: saving artifacts (payload, pcap if necessary), RCA products and "virtual patches."
10) Testing and chaos scenarios
WAF bypass enclosures (SQLi/XSS/SSRF), double/triple Unicode mixed encodings.
Parsing differences: send payload, where the proxy and framework can diverge (parameter duplicates, arrays, ';' vs' & ').
Slow-POST/oversize, zip bombs, multi-part forms, erroneous boundary.
GraphQL: depth/complexity generator, checking limits and timeouts.
11) Antipatterns
"Turned on CRS and forgot": without schemes, without tuning along routes.
Logs with raw request body and PII.
No normalization/size limits → bypasses, DoS for parsing.
Skipping'Content-Type '/charset checks → polyglot attacks.
No egress filters → SSRF to cloud metadata.
One common profile for external and internal APIs.
Uncontrolled exceptions "for the partner" → holes in the perimeter.
12) Specifics of iGaming/Finance
Reinforced profiles on payment/output handles: small body limits, strict schemes, deny lists for account/IBAN/PAN fields (masking, format checks).
Webhooks from PSP/KYC: HMAC signature/mutual TLS, individual WAF profiles, anti-replay.
Geo/ASN filters and behavioral limits to prevent bot registrations and bonus abuse.
Incident logs are immutable (audit), storage by jurisdiction.
13) Prod Readiness Checklist
- Dual layer WAF (CDN + L7), single normalization and size limits.
- OWASP CRS enabled, custom rules per-route; JSON Schema/Protobuf on write pens.
- Content-Type/charset policies; disallow double decoding/NULL/traversal.
- SSRF-egress block for private bands/metadata; DNS rebinding protection.
- Rate/Quota/Concurrency and anti-bot (challenges) on public forms.
- Shadow/Report-Only → canary → enforce; auto-rollback by SLO and FP.
- Metrics/logs/trails with masking; dashboards "top rules "/FP.
- Virtual patch and RCA playbooks; regular bypass tests.
- Separate profiles for PSP/KYC webhooks, payment pens, and internal APIs.
14) TL; DR
Build protection by layers: normalization and limits → schemes/types → parameterization → WAF (CRS + caste) → rate/bot filters → SSRF egress block. Tune per-route, run new rules in shadow → canary, monitor metrics/FP and make "virtual patches" before the code fix. For payment/webhook paths - separate strict profiles, HMAC/mTLS and minimal trust windows.