Skip to content

347 Fill the gaps in the response security headers - #349

Merged
dfe-lance merged 2 commits into
mainfrom
347-security-headers
Aug 25, 2026
Merged

347 Fill the gaps in the response security headers#349
dfe-lance merged 2 commits into
mainfrom
347-security-headers

Conversation

@dfe-lance

Copy link
Copy Markdown
Contributor

Closes #347.

A security assessment picked up a handful of low-severity header and cookie
items. Individually each is minor; together they are the cheapest security work
available, and several had been noted informally for a while.

A security assessment picked up a handful of low-severity header and cookie
items. Individually each is minor; together they are the cheapest security work
available, and several had been noted informally for a while.

The existing set was most of the way there — HSTS, a content-security policy
and X-Frame-Options were already in place, and the session cookie already sets
Secure outside development. This fills what was missing rather than starting
from nothing:

- X-Content-Type-Options: nosniff, so a browser stops second-guessing a
  declared content type. It matters here because the storage browser and the
  content pipeline both serve files an author supplied.
- Referrer-Policy: strict-origin-when-cross-origin. Without it the full URL,
  window and request identifiers included, travels to any third-party origin in
  the Referer header. Same-origin navigation is unaffected.
- Permissions-Policy denying camera, microphone, geolocation and the rest. The
  service asks for none of them, so an injected frame or script cannot ask on
  its behalf either.
- The antiforgery cookie now follows the same environment-dependent Secure
  policy the session cookie already had; it had been left on the default.
  SameAsRequest in development keeps local HTTP working, where Always would
  have the browser drop the cookie and fail every form POST.
- TRACE is refused with 405 before routing. Cross-site tracing is already closed
  by HttpOnly cookies and modern browsers, so this removes a surface rather than
  fixing an exploit.
- Kestrel no longer advertises itself. The banner carries no version, but naming
  the stack tells a scanner which exploits are worth trying and buys nothing.

The headers moved into one middleware. They only do anything if they are on
every response, and spread across controllers a new endpoint silently misses
them. The content-security policy moved there unchanged and is pinned by a test
so folding it in cannot quietly drop it.

Headers are assigned rather than appended: appending to one something upstream
already set produces two of it, and a browser given two conflicting security
headers may pick the one we did not want.

Left alone deliberately: 'unsafe-inline' and 'unsafe-eval' in the policy's
script-src. Removing them means threading a per-request nonce through every
inline script and style, including those the frontend toolkit and the analytics
tags emit — real regression risk that deserves its own change and its own
testing rather than riding along with a header sweep.

Refs #347
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

Review app for PR 349 was deleted

@dfe-lance
dfe-lance merged commit 29c7573 into main Aug 25, 2026
13 of 15 checks passed
@dfe-lance
dfe-lance deleted the 347-security-headers branch August 25, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Security hardening: missing response headers, antiforgery cookie flag, TRACE

2 participants