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
The download endpoint derives the served Content-Type from three sources (spec §Attachments, "Download"), and the dangerous-type policy is specified for exactly one of them:
Source
Forcing specified?
?contentType param
Yes — "Dangerous types (HTML, SVG, JS, XML) are forced to application/octet-stream regardless"
Extension inference from ?filename (when ?contentType omitted)
No — spec is silent
Stored _attachment@1mimeType (no params)
No — spec is silent
So the hardened path is the one the attacker doesn't have to use: GET /attachments/<fileId>?filename=x.html invites the server to infertext/html with no forcing language covering the result, and a stored metadata record with a scripty type (see #65's escalation scenario) flows to the header equally unforced. XSS-shaped, on the stack's origin — where a web client's bearer token lives.
A server author reading the spec today can implement all three paths "correctly" and ship the hole; that's a spec defect, not an implementation one.
Decided direction
Forcing applies to the result, not the source. Normative spec language: compute the candidate type (?contentType → else extension inference → else stored metadata → else application/octet-stream), then apply the dangerous-type policy to whatever came out. One policy, one application point, impossible to route around by choosing a different source.
Define the policy once, and prefer a safe-list. The current deny-list ("HTML, SVG, JS, XML") is informal and incomplete (e.g. application/xhtml+xml, text/xsl, MIME parameter tricks). Recommended: safe-list — image/* (minus image/svg+xml), video/*, audio/*, application/pdf, text/plain, application/octet-stream; everything else is served as application/octet-stream with Content-Disposition: attachment. At minimum, the deny-list becomes normative and exhaustive-by-category rather than four examples in a table cell.
X-Content-Type-Options: nosniff on every attachment response, mandated by the spec. Without it, browsers may sniff octet-stream responses back into the very types the forcing removed — nosniff is what makes the downgrade stick.
Relationship to #65: that issue makes the served type deterministic (first-recorded wins, conflicts rejected); this one makes even a deterministic lie inert. Both are needed — determinism without forcing serves the first uploader's text/html faithfully; forcing without determinism leaves caches flapping between safe answers.
Work items
Spec §Attachments: result-not-source forcing rule; the safe-list (or normative deny-list) as a named policy section; nosniff requirement
Problem
The download endpoint derives the served
Content-Typefrom three sources (spec §Attachments, "Download"), and the dangerous-type policy is specified for exactly one of them:?contentTypeparamapplication/octet-streamregardless"?filename(when?contentTypeomitted)_attachment@1mimeType(no params)So the hardened path is the one the attacker doesn't have to use:
GET /attachments/<fileId>?filename=x.htmlinvites the server to infertext/htmlwith no forcing language covering the result, and a stored metadata record with a scripty type (see #65's escalation scenario) flows to the header equally unforced. XSS-shaped, on the stack's origin — where a web client's bearer token lives.A server author reading the spec today can implement all three paths "correctly" and ship the hole; that's a spec defect, not an implementation one.
Decided direction
?contentType→ else extension inference → else stored metadata → elseapplication/octet-stream), then apply the dangerous-type policy to whatever came out. One policy, one application point, impossible to route around by choosing a different source.application/xhtml+xml,text/xsl, MIME parameter tricks). Recommended: safe-list —image/*(minusimage/svg+xml),video/*,audio/*,application/pdf,text/plain,application/octet-stream; everything else is served asapplication/octet-streamwithContent-Disposition: attachment. At minimum, the deny-list becomes normative and exhaustive-by-category rather than four examples in a table cell.X-Content-Type-Options: nosniffon every attachment response, mandated by the spec. Without it, browsers may sniff octet-stream responses back into the very types the forcing removed — nosniff is what makes the downgrade stick.Relationship to #65: that issue makes the served type deterministic (first-recorded wins, conflicts rejected); this one makes even a deterministic lie inert. Both are needed — determinism without forcing serves the first uploader's
text/htmlfaithfully; forcing without determinism leaves caches flapping between safe answers.Work items
haverstack/serverto implement against the fixturesRefs #65 (deterministic type; this is its backstop), #52 (fixtures), #51 (origin-scoped credential threat model context).