fix(security): Host-header allowlist + restore dropped security headers#178
Open
aaronjmars wants to merge 1 commit into
Open
fix(security): Host-header allowlist + restore dropped security headers#178aaronjmars wants to merge 1 commit into
aaronjmars wants to merge 1 commit into
Conversation
Detected by Aeon + semgrep. Severity: medium CWE-350 (reverse-DNS trust) / CWE-352 (CSRF via DNS rebinding) / CWE-1021 (UI layering / dropped X-Frame-Options). - Add `map $http_host $is_flowsint_host` allowlist + default-deny `if` at the top of the server block. Default permits localhost / 127.0.0.1 / ::1 (any port, case-insensitive); two commented-out template lines show LAN operators how to add their server hostname/IP. - Re-declare the four `add_header` security headers in the static-assets and `/index.html` location blocks, which previously dropped them due to nginx`s replace-all add_header inheritance. - README: one paragraph under "Deploy on a network" naming the new operator step alongside the existing `.env` rotation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a Host-header allowlist to
flowsint-app/nginx.confso the frontend rejects requests carrying an unexpectedHostheader before they reach/api/. Without this check, browser CORS does not protect the API from DNS rebinding: after the attacker's DNS flip the browser treats the rebound request as same-origin and skips the CORS check, leavingPOST /api/auth/registerand any other unauthenticated route reachable from an attacker page.Also re-declares the four
add_headersecurity headers in the two location blocks (location ~* \.(js|css|...)$andlocation = /index.html) that were silently dropping them. nginx'sadd_headerinheritance is replace-all, not merge — a location block that sets any header drops every header inherited fromserver. The main HTML page was shipping withoutX-Frame-Options,X-Content-Type-Options,X-XSS-Protection, orReferrer-Policy.Impact
DNS rebinding (primary). Attacker JS on
evil.commakesevil.comresolve briefly to the attacker's IP, then rebinds to a victim's flowsint instance (e.g.127.0.0.1). Browser sendsHost: evil.comto flowsint; the existingserver_name _;accepts any Host, andproxy_set_header Host $host;forwards the attacker-controlled value to the FastAPI upstream. The CORS middleware inflowsint-api/app/main.pydoes not help, because from the browser's perspective the request is same-origin (the JS and the rebound request both useevil.com).Concrete reachable surface from an attacker page, today, with the default deploy:
POST /api/auth/register— open registration. Attacker can plant accounts in the victim's instance. The first registration on a fresh deploy becomes the de-facto admin.POST /api/auth/token— login. Attacker can mount distributed credential-stuffing against the victim's instance from the victim's own browser.GET /health— info leak (low).JWT bearer tokens are localStorage-scoped to the legitimate origin, so authenticated endpoints don't directly leak — but that's not the boundary the deployment claims. The README's "Everything is stored on your machine" privacy posture assumes the local instance isn't drivable from arbitrary origins; this fix restores that.
Dropped security headers (secondary). With the prior config, hitting
/(the SPA) returns/index.htmlfrom thelocation = /index.htmlblock, which setsadd_header Cache-Control "no-store, ..."and thereby drops every server-level security header. So the main HTML page goes out with noX-Frame-Options(clickjacking), noReferrer-Policy(cross-site referrer leak), and noX-Content-Type-Options(MIME confusion). Same loss applied to the static-assets location for.js/.css.Location
flowsint-app/nginx.conf—map $http_host $is_flowsint_hostallowlist +if ($is_flowsint_host = 0)gate at the top of theserverblock; security headers re-declared in the static-assets and/index.htmllocations.README.md— one paragraph under "Deploy on a network (team / server)" naming the new operator step for LAN deploys.Fix
map $http_host $is_flowsint_hostblock athttplevel. Default0; allowlist entries forlocalhost,127.0.0.1,[::1](any port, case-insensitive), matching the documented single-user install path (http://localhost:5173/register). Two commented-out template lines show LAN operators how to add their server hostname/IP.if ($is_flowsint_host = 0) { return 403; }at the top of theserverblock, before any header or location processing.location ~* \.(js|css|...)$andlocation = /index.html, re-declare the fouradd_headersecurity headers that nginx's replace-all inheritance was dropping..envsecret rotation step.Verification
nginx -t -c flowsint-app/nginx.conf -p <prefix>/→syntax is ok/test is successful.localhost,localhost:5173,127.0.0.1,LocalHost:5173,[::1],[::1]:5173, etc.; blocked:evil.com,evil.com:5173,localhost.evil.com,127.0.0.1.attacker.com,127-0-0-1.attacker.com,127.0.0.1evil, empty host,192.168.1.42(operator must opt-in), AWS-metadata IP, etc.).192.168.1.42:5173is correctly blocked under the default config — the README change tells operators to extend the allowlist (matches existing.env-rotation pattern)..js/.css) and/index.htmlconfirmed to keep the fouradd_headersecurity headers in the patched location blocks.Detected by
Aeon + semgrep
/index.html).generic.nginx.security.request-host-usedatflowsint-app/nginx.conf:69flagged the unvalidatedHostpropagation;generic.nginx.security.header-redefinitionat:84/:90/:102flagged the silently dropped headers.Filed by Aeon.