Skip to content
Security & Trust

Coraza firewall could be shown a decoy filename while the app saw another

A silent parsing gap let file-upload rules miss the real name. The fix shows why picking one reading is not enough.

W
WebPulse Newsroom
AI-assisted · 4 min read
Share on X LinkedIn
Coraza firewall could be shown a decoy filename while the app saw another
In brief
  • Coraza's file-upload rules could be shown a decoy filename while some backends used the real one, according to the GitHub advisory for GHSA-3wr7-993q-jrff.
  • A standard-library parser silently dropped some filename encodings. The first fix only moved the gap; a later commit keeps both names.
  • 3.8.1 closes a further gap, RFC 2231 continuations, left open in 3.8.0. Teams running Coraza should upgrade to 3.8.1 and check how their upload backend resolves filenames.

A firewall and the application behind it must read a request the same way. When they do not, the firewall's checks can miss what the application acts on. A GitHub advisory about the Coraza firewall shows this in miniature: one upload, two filenames.

What the advisory describes

Coraza is a web application firewall. It checks web requests against rules before they reach an application. The advisory is GHSA-3wr7-993q-jrff, published October 8, 2026. It says Coraza's handling of file uploads could be given a different filename than the backend used.

An upload names its file in a Content-Disposition header. The header can hold a plain filename and an extended one, written filename*. RFC 7578 and RFC 6266 say the extended name wins if both appear.

Coraza read that header with a Go standard-library function, mime.ParseMediaType. That function decodes the extended name for only two character sets: us-ascii and utf-8. It drops any other, including iso-8859-1, which the standard allows. It gives no error and no warning.

How the decoy works

Picture a request with filename="safe.jpg" and filename*=iso-8859-1''shell.php. FILES is the list of uploaded names that rules can check. Before the fix, it held safe.jpg. A rule like SecRule FILES "\.php$" did not fire. A backend that follows the standard and accepts that charset saved the file as shell.php.

A part with only an extended name was handled differently. Coraza did not count it as a file. It treated the part as an ordinary form field. File rules and the upload size count did not apply to it.

4.0, Medium
CVSS v3.1 base score (current)
Source: GitHub Advisory Database, GHSA-3wr7-993q-jrff (October 8, 2026)

The limits matter

The advisory sets clear bounds. It calls the bypass deterministic against Coraza, and it needs no login. But it hides only the filename. Coraza kept checking the request body, arguments and headers.

It also depends on the backend. Backends in Go and PHP land on the same name Coraza did, so the gap does not touch them. Python apps on Werkzeug, which Flask uses, can land on a different name. So can Node.js apps built on the busboy library, such as Express with multer, or Fastify. This is why Attack Complexity is rated High.

5.8
CVSS v3.1 base score (earlier), when Attack Complexity was rated Low
Source: GitHub Advisory Database, GHSA-3wr7-993q-jrff (October 8, 2026)

Coraza does not store or serve uploads. Whether a missed rule mattered depends on what the application does with the file. The advisory adds that the application must itself be open to the hidden payload.

The fix that moved the problem

The first fix made the extended name always win, as the standard says. That closed the original example. But it moved the gap. Reverse the trick: put shell.php in the plain field and the decoy in the extended one. Now a backend that reads the plain field is missed.

The advisory credits @jptosso with showing this. It also says ModSecurity v3's fix for GHSA-5pww-8rfg-9crf made the same choice. The later fix, commit 89399e67, keeps both names. When they differ, both go into FILES for that part.

The lesson is that when two parsers can disagree, picking the correct one is a bet on the backend. Checking both readings removes the bet.

3
Further discrepancies found in post-fix review
Source: GitHub Advisory Database, GHSA-3wr7-993q-jrff (October 8, 2026)

Silent dropping was the core flaw

The deeper flaw was that nothing signalled the dropped value. Coraza now raises MULTIPART_STRICT_ERROR when a header cannot be parsed or a filename* is malformed. A new variable, MULTIPART_DUPLICATE_PART_HEADER, flags repeated headers or parameters. Two more, MULTIPART_FILENAME_CHARSET and MULTIPART_FILENAME_LANGUAGE, show what the part declared. Operators can use them to write their own allowlist.

One trap to know. A negated @within allowlist looks like an anchored regex but is not. An empty charset slips past it. The advisory says to use an anchored regex instead.

Separately from that commit, the 3.8.0 release was itself incomplete. Version 3.8.1 flags RFC 2231 numbered continuations placed next to a bare filename*. The advisory lists 3.8.0 as affected.

Questions for your team

First, do you run Coraza, directly or inside a product that embeds it? If so, is it on 3.8.1?

Second, which backend receives your uploads? Does it decode extended filenames in character sets other than UTF-8? Does it ever treat a part with only an extended name as a file?

Third, do your rules rely on FILES to block risky extensions? Is MULTIPART_STRICT_ERROR (rule 200003 in CRS) active and monitored?

A firewall is only as accurate as its reading of the request. When the filter and the application disagree, the application's reading is the one that counts.

Produced by the WebPulse Newsroom with AI assistance from the original reporting credited below, and checked against that source by our editorial review. How we use AI.
Original reporting: GitHub Advisory Database.

Share this insight