-
Notifications
You must be signed in to change notification settings - Fork 218
Description
Although IHP has strong XSS protection, adding a builtin built-in feature for managing CSP would make it even more secure.
For Shipnix, I have added some security headers with a Content Security Policy that works well, but as you can see the code quality could be better and more maintainable.
module Application.Helper.SecurityHeaders where
import Application.Helper.Common (CSPNonce (CSPNonce))
import Web.Controller.Prelude
setSecurityHeaders :: _ => IO ()
setSecurityHeaders = do
nonce <- generateAuthenticationToken
putContext (CSPNonce nonce)
-- https://securityheaders.com
setHeader ("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
setHeader ("X-Frame-Options", "SAMEORIGIN")
setHeader ("X-Content-Type-Options", "nosniff")
setHeader ("Content-Security-Policy", contentSecurityPolicy nonce)
setHeader ("Referrer-Policy", "strict-origin-when-cross-origin")
contentSecurityPolicy :: _ => Text -> ByteString
contentSecurityPolicy nonce =
( if isDevelopment
then
[plain|default-src 'self';
img-src 'self' https://tailwindui.com https://avatars.githubusercontent.com data:;
connect-src ws://localhost:* 'self';
style-src 'nonce-#{nonce}' 'self';
script-src 'strict-dynamic' 'nonce-#{nonce}';
object-src 'none';
frame-ancestors http://localhost:8000;
base-uri 'none';
|]
else [plain|default-src 'self';img-src 'self' https://avatars.githubusercontent.com data:;style-src 'nonce-#{nonce}' 'self';script-src 'strict-dynamic' 'nonce-#{nonce}';object-src 'none';frame-ancestors https://shipnix.io;base-uri 'none';connect-src 'self' https://shipnix.io https://plausible.io wss://shipnix.io;|]
)
|> csI for example generate nonces for each IHP pageload that are put into the context, restricting any unauthorized script and CSS files, preventing for example data mining attacks.
scripts :: Html
scripts =
[hsx|
<script nonce={nonce} src={assetPath "/prod.js"}></script>
<script nonce={nonce} defer data-domain="shipnix.io" src="https://plausible.io/js/script.js"></script>
|]
where
CSPNonce nonce = fromFrozenContextCSP's are a pain to write, and IHP has the opportunity to add a type-safe API for it with good defaults.
Yesod could be used as an inspiration, but I also think we could make it even nicer for IHP
https://hackage.haskell.org/package/yesod-csp-0.2.5.0/docs/Yesod-Csp.html
Instead of having a list with directives, I think it would be nice to have it in a set structure that ensures that every rule has some mandatory directives to ensure some minimum default with the possibility to customize it according to specific needs.
data CSP = CSP
{ defaultSrc = CSPSourceList
, scriptScr = CSPSourceList
, frameAncestors = CSPSourceList
, reportURI = CSPEscapedURI
..... etc
}