You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
OpenIG routes often sit in front of services that would benefit from a Web Application Firewall enforcing well-maintained open-source rules — OWASP Core Rule Set (CRS) — against common attacks (SQLi, XSS, path traversal, RCE, protocol violations), without deploying a separate proxy tier (nginx+ModSecurity, Envoy, etc.). The idea: a first-class WAFFilter heap object that can be dropped into any route's filter chain.
Why not a Java SecLang engine
There is no maintained pure-Java engine for the ModSecurity SecLang rule language that CRS is written in: ModSecurity for Java (Trustwave JNI bridge), jwall webapp-firewall and OWASP ESAPI WAF are all abandoned (~2013), and libmodsecurity v3 has no maintained Java bindings and would drag native dependencies into the WAR.
Proposed approach
Run OWASP Coraza — an actively maintained WAF engine, 100% SecLang- and CRS v4-compatible — inside the JVM via WebAssembly:
coraza-proxy-wasm 0.6.0 — official Coraza v3.3.3 build with embedded CRS v4.14.0 (same artifact used by Envoy/Istio deployments).
proxy-wasm-java-host (io.roastedroot:proxy-wasm-java-host:0.0.5, Maven Central) — proxy-wasm ABI host on top of Chicory 1.5.1, a pure-Java Wasm runtime that compiles modules to JVM bytecode. Zero JNI/native dependencies, Java 11 — matches the OpenIG baseline.
New Maven module openig-waf, filter registered as WAFFilter (alias CorazaFilter). Blocked requests are rejected (default: engine-provided 403) without reaching the downstream handler; interruption details go into attributes.wafInterruption for audit/custom error handling. A pool of pre-warmed Wasm instances (CRS is parsed at route load, not per request) handles concurrency; request bodies are inspected via the re-readable entity buffer, response inspection (phases 3/4) is optional.
All keys optional — the default is CRS in blocking mode.
Where rules come from and how they are updated
Base CRS is embedded in the coraza-proxy-wasm.wasm release (the Wasm sandbox has no filesystem, so CRS .conf + .data files are baked in and referenced via Include @owasp_crs/*.conf). Updates: bump the pinned release version/sha256 at build time (the ~18 MB binary is fetched by download-maven-plugin, sha256-pinned, never committed to git), or — at runtime without rebuilding — point the wasm config option at a newer downloaded file and hot-reload the route.
Custom rules and tuning are plain SecLang strings in the route's directives: own SecRules, false-positive exclusions (SecRuleRemoveById, ctl:ruleRemoveTargetById), paranoia level, and official CRS rule-exclusion plugins (WordPress/Nextcloud — pure SecLang, no data files). Updated by editing the route JSON with hot reload.
Recommended rollout for new CRS versions: SecRuleEngine DetectionOnly first, then blocking.
Known trade-offs
proxy-wasm-java-host is 0.0.x; the integration pins the version and confines its API to three adapter classes.
Upstream warns about TinyGo GC memory behaviour in long-lived instances — mitigated by a bounded pool recreated on route reload.
Cold start: CRS parse per pool slot at route load (seconds, thanks to Chicory bytecode compilation — not per request).
Request bodies are buffered for inspection (same trade-off as the existing OpenApiValidationFilter).
Feedback welcome — especially interest in response-phase inspection, per-route CRS tuning presets, and whether a DetectionOnly-by-default rollout would be preferable.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Motivation
OpenIG routes often sit in front of services that would benefit from a Web Application Firewall enforcing well-maintained open-source rules — OWASP Core Rule Set (CRS) — against common attacks (SQLi, XSS, path traversal, RCE, protocol violations), without deploying a separate proxy tier (nginx+ModSecurity, Envoy, etc.). The idea: a first-class
WAFFilterheap object that can be dropped into any route's filter chain.Why not a Java SecLang engine
There is no maintained pure-Java engine for the ModSecurity SecLang rule language that CRS is written in: ModSecurity for Java (Trustwave JNI bridge), jwall webapp-firewall and OWASP ESAPI WAF are all abandoned (~2013), and libmodsecurity v3 has no maintained Java bindings and would drag native dependencies into the WAR.
Proposed approach
Run OWASP Coraza — an actively maintained WAF engine, 100% SecLang- and CRS v4-compatible — inside the JVM via WebAssembly:
io.roastedroot:proxy-wasm-java-host:0.0.5, Maven Central) — proxy-wasm ABI host on top of Chicory 1.5.1, a pure-Java Wasm runtime that compiles modules to JVM bytecode. Zero JNI/native dependencies, Java 11 — matches the OpenIG baseline.New Maven module
openig-waf, filter registered asWAFFilter(aliasCorazaFilter). Blocked requests are rejected (default: engine-provided 403) without reaching the downstream handler; interruption details go intoattributes.wafInterruptionfor audit/custom error handling. A pool of pre-warmed Wasm instances (CRS is parsed at route load, not per request) handles concurrency; request bodies are inspected via the re-readable entity buffer, response inspection (phases 3/4) is optional.Example route config:
{ "type": "WAFFilter", "config": { "directives": [ "Include @recommended-conf", "SecRuleEngine On", "Include @crs-setup-conf", "Include @owasp_crs/*.conf", "SecRule REQUEST_URI \"@streq /admin\" \"id:101,phase:1,deny,status:403\"" ], "failureHandler": { "type": "StaticResponseHandler", "config": { "status": 403 } }, "inspectResponse": false, "concurrency": 4 } }All keys optional — the default is CRS in blocking mode.
Where rules come from and how they are updated
coraza-proxy-wasm.wasmrelease (the Wasm sandbox has no filesystem, so CRS.conf+.datafiles are baked in and referenced viaInclude @owasp_crs/*.conf). Updates: bump the pinned release version/sha256 at build time (the ~18 MB binary is fetched bydownload-maven-plugin, sha256-pinned, never committed to git), or — at runtime without rebuilding — point thewasmconfig option at a newer downloaded file and hot-reload the route.directives: ownSecRules, false-positive exclusions (SecRuleRemoveById,ctl:ruleRemoveTargetById), paranoia level, and official CRS rule-exclusion plugins (WordPress/Nextcloud — pure SecLang, no data files). Updated by editing the route JSON with hot reload.SecRuleEngine DetectionOnlyfirst, then blocking.Known trade-offs
proxy-wasm-java-hostis 0.0.x; the integration pins the version and confines its API to three adapter classes.OpenApiValidationFilter).Feedback welcome — especially interest in response-phase inspection, per-route CRS tuning presets, and whether a
DetectionOnly-by-default rollout would be preferable.Vote to raise the priority 🖐️
All reactions