Skip to content

Fix Content-Disposition parameter injection via download filenames#1309

Merged
lovasoa merged 2 commits into
mainfrom
ophir.lojkine/fix-content-disposition-injection
Jun 10, 2026
Merged

Fix Content-Disposition parameter injection via download filenames#1309
lovasoa merged 2 commits into
mainfrom
ophir.lojkine/fix-content-disposition-injection

Conversation

@lovasoa

@lovasoa lovasoa commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Fix Content-Disposition parameter injection via download filenames

The csv and download components built the Content-Disposition header by string-interpolating the user-supplied filename (render.rs#L304-L307 and #L348-L351). A filename containing ;, " or = could inject a second header parameter that some browsers prefer over the intended one.

For example:

select 'csv' as component, 'report.csv; filename*=UTF-8''''evil.html' as filename;
select 1 as a;

produced two parameters, the injected one being agent-preferred:

content-disposition: attachment; filename=report.csv; filename*=UTF-8''evil.html

Fix: build the header with actix-web's structured ContentDisposition type, so the filename is always a single, properly quoted/escaped value:

content-disposition: attachment; filename="report.csv; filename*=UTF-8''evil.html"

A compliant parser (and actix's own ContentDisposition::from_raw) now sees exactly one inert filename value.

Both components share the new attachment_with_filename helper, so the fix is in one place.

Proof

New test test_csv_filename_header_injection parses the response header with ContentDisposition::from_raw (same logic a browser uses) and asserts there is no injected filename* and the payload stays inside a single filename.

Fails on main:

attacker injected a separate filename* parameter: "attachment; filename=report.csv; filename*=UTF-8''evil.html"
test result: FAILED. 0 passed; 1 failed

Passes with the fix, and the full tests/mod.rs suite stays green (64 passed; 0 failed), including the existing test_download_data_url which still asserts attachment; filename="my text file.txt".

cargo fmt --all and cargo clippy --all-targets --all-features -- -D warnings are clean.

Scope / severity The filename is normally chosen by trusted SQL, so this matters only when an app interpolates untrusted data into a `csv`/`download` `filename`. No SQL change is required by users; CHANGELOG updated.

lovasoa and others added 2 commits June 10, 2026 16:03
The csv and download components built the Content-Disposition header by
string-interpolating the user-supplied filename. A filename containing
characters such as ';', '"' or '=' could inject an additional header
parameter (e.g. a second, agent-preferred filename*=...), letting an app
that interpolates untrusted data into the filename smuggle a different
download name past the intended one.

Build the header with actix-web's structured ContentDisposition type so
the filename is always a single, properly quoted/escaped value and cannot
create new parameters.
@lovasoa lovasoa merged commit c37ce26 into main Jun 10, 2026
@lovasoa lovasoa deleted the ophir.lojkine/fix-content-disposition-injection branch June 10, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant