Repository navigation
Send a Content-Security-Policy and refuse framing - #41
Merged
Merged
Conversation
Two gaps in what the browser was told about the page: - Nothing stopped another site from showing the app in a frame, say invisibly over its own buttons, and tricking the user into clicking Archive & Find Next or pressing A. localhostOnly lets the framed page load, and its clicks make same-origin requests. - Nothing limited what markup in the page could do. The preview bug showed that one slip let an email run script with the app's access. securityHeaders now sets, on every response including refusals, a Content-Security-Policy that allows only the app's own script, stylesheet, images and API, no inline script, handler or style attributes, and no framing; X-Frame-Options: DENY for browsers that only know that; and X-Content-Type-Options: nosniff. The five inline style attributes the policy would block become CSS classes, and the status pill starts with the hidden attribute.
Owner
Author
|
Checked the two PRs together, and this one alone:
So the policy stops the preview bug on its own too, and #40 removes its cause. |
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.
Problem
The server told the browser nothing about what the page may do. That left two gaps.
http://localhost:8080in a frame, for example invisibly over its own buttons, and trick the user into clicking Archive & Find Next (which archives without confirmation) or pressingA.localhostOnlylets the framed page load, because its Host is localhost.CrossOriginProtectionallows.main: a page on another origin showed the app in its iframe.Fix
securityHeadersininternal/server/server.gowraps the existing checks. It sets these headers on every response, refusals included:Content-Security-Policy:default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'on…handler attributes orstyleattributes.img-src 'self'covers the browser's own/favicon.icorequest.X-Frame-Options: DENY, for browsers that only know that header.X-Content-Type-Options: nosniff, so a response is never run as another type. The Go test checks that the script and stylesheet keep their JavaScript and CSS types.Left out on purpose:
upgrade-insecure-requests, which would breakhttp://localhost.innerHTMLwould need.Inline styles. The policy blocks
styleattributes, so the five the app had became CSS:index.html) starts with thehiddenattribute..archive-status[hidden] { display: none; }is needed because.archive-statussetsdisplay: flex.showArchiveStatus()andshowArchiveError()sethidden = false.showError()becomes.error-message.enhanceEmailFormatting()becomes.preview-link,.preview-addressand.preview-label. They are scoped under.preview-body, so the label still beats.preview-body strong.Setting
element.stylefrom JavaScript (the popup position, the modal) is not affected by the policy.Docs. The CLAUDE.md Browser Protections entry describes the headers and the rule "classes or
hidden, neverstyleattributes". The README Security section mentions the framing and script protection.This is independent of #40, and the two merge cleanly in either order. Each fixes the preview bug on its own; together they are two layers.
Tests
TestHandler_SecurityHeaders(Go): the page, script, stylesheet, API, a 404, a foreign Host (403) and a cross-site POST (403) all carry the headers. The static files keep their Content-Types.cross-site.spec.js, "a page on another site cannot show the app in a frame": a page on127.0.0.1:18081frames the app. The browser requests the app, but the frame doesn't show it.content-security-policy.spec.js:<img onerror>added to the page doesn't run its handler, and the browser reports ascript-src-attrviolation.On
main, all four new tests fail:With the change:
go test -raceandgo vetpass.Testing on Fastmail
go run main.go) and reload the page.Content-Security-PolicyandX-Frame-Options: DENY.