Skip to content

Commit

Permalink
rewrite better csp implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
kuroji-fusky committed Sep 2, 2023
1 parent 75def2e commit 591e584
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 160 deletions.
15 changes: 8 additions & 7 deletions apps/website/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export default function RootLayout({
>
<head>
<script
nonce={nonce}
dangerouslySetInnerHTML={{ __html: DEV_CONVERSION_INLINE_SCRIPT }}
defer
/>
Expand All @@ -73,20 +74,20 @@ export default function RootLayout({
<body className="bg-100 text-700 !overflow-x-hidden bg-background prose-headings:font-bold prose-headings:font-inter text-sm font-medium font-open-sans">
<SkipNav />
<NoJSReminder />
{/* Platform announcements sent through the API goes here */}
<div id="myfursona-announcements"></div>
<div id="myfursona-app">
<Providers>
<Providers>
<div id="myfursona-app">
{/* Platform announcements sent through the API goes here */}
<div id="announcements"></div>
<header className="sticky top-0 z-10">
<Navbar />
<Sidebar />
</header>
<main id="skip-navigation" className="min-h-[100dvh]">
<main id="skip-navigation" className="min-h-[calc(100dvh-6rem)]">
{children}
</main>
<Footer />
</Providers>
</div>
</div>
</Providers>
</body>
</html>
)
Expand Down
5 changes: 3 additions & 2 deletions apps/website/src/components/base/Analytics.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import Script from "next/script"
import dedent from "dedent"

export default function Analytics({ nonce }: { nonce: string }) {
return (
<>
{/* Site analytics - Umami */}
<Script
id="umami"
async
defer
src="https://analytics.umami.is/script.js"
data-website-id="DEV"
/>
{/* Behavior analytics - Microsoft Clarity */}
<script
nonce={nonce}
dangerouslySetInnerHTML={{
__html: `
__html: dedent`
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
Expand Down
76 changes: 32 additions & 44 deletions apps/website/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,40 @@
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
// import { generateCSPString } from "./utils"
import { generateCSP } from "./utils"

export default function middleware(request: NextRequest) {
const requestHeaders = new Headers(request.headers)
const generatedNonce = crypto.randomUUID()

// const csp = generateCSPString({
// defaultSrc: {},
// scriptSrc: {
// // TODO only enable 'unsafe-eval' on dev environments only
// unsafeEval: true,
// domains: [
// "https://assets.hcaptcha.com",
// "https://www.clarity.ms/",
// "https://analytics.umami.is/"
// ],
// nonce: generatedNonce
// },
// styleSrc: {
// unsafeInline: true
// },
// connectSrc: {
// domains: [
// "https://assets.hcaptcha.com",
// "https://api.stripe.com",
// "https://analytics.umami.is/"
// ]
// },
// frameSrc: {
// domains: [
// "https://www.youtube-nocookie.com",
// "https://hooks.stripe.com",
// "https://js.stripe.com"
// ]
// },
// imgSrc: {
// domains: ["https://c.bing.com", "https://c.clarity.ms"]
// },
// upgradeInsecureRequests: true
// })
const csp = generateCSP({
"default-src": ["self"],
"script-src": [
"self",
"unsafe-eval",
"https://assets.hcaptcha.com",
"https://www.clarity.ms/",
"https://analytics.umami.is/",
`nonce-${generatedNonce}`
],
"style-src": ["self", "unsafe-inline"],
"connect-src": [
"self",
"https://assets.hcaptcha.com",
"https://api.stripe.com",
"https://analytics.umami.is/"
],
"frame-src": [
"https://www.youtube-nocookie.com",
"https://hooks.stripe.com",
"https://js.stripe.com"
],
"frame-ancestors": ["none"],
"img-src": ["self", "data:", "https://c.bing.com", "https://c.clarity.ms"],
"upgrade-insecure-requests": true
})

// Set the CSP header so that Next.js can read it and generate tags with the nonce
// requestHeaders.set("Content-Security-Policy", csp)
requestHeaders.set("Content-Encoding", "br")
requestHeaders.set("Content-Security-Policy", csp)
requestHeaders.set("x-nonce", generatedNonce)

const response = NextResponse.next({
Expand All @@ -52,13 +44,9 @@ export default function middleware(request: NextRequest) {
})

response.headers.set("Content-Encoding", "br")
// response.headers.set("Content-Security-Policy", csp)
response.headers.set("X-Content-Type-Options", "no-sniff")
response.headers.set("X-Frame-Options", "DENY")
/**
** Technically not supported by most browsers as it's a non-standard, but it's here
** just for good measure.
**/
response.headers.set("Content-Security-Policy", csp)
// This header technically not supported by most browsers
// as it's a non-standard, but it's here just for good measure.
response.headers.set("X-XSS-Protection", "1; mode=block")

return response
Expand Down
62 changes: 62 additions & 0 deletions apps/website/src/utils/generateCSP.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
type CSPDirective =
| (
| "none"
| "self"
| "unsafe-inline"
| "unsafe-hashes"
| "unsafe-eval"
| "strict-dynamic"
| "blob:"
| "https:"
| "data:"
| "mediastream:"
| "nonce-"
| "sha256-"
)[]
| string[]
type CSPDirectiveWithWasm = CSPDirective | "wasm-unsafe-eval"[]

type CSPPolicies = Partial<{
"script-src": CSPDirectiveWithWasm
"connect-src": CSPDirectiveWithWasm
"default-src": CSPDirective
"style-src": CSPDirective
"font-src": CSPDirective
"frame-src": CSPDirective
"frame-ancestors": "none"[] | string[]
"img-src": CSPDirective
"worker-src": CSPDirective
"upgrade-insecure-requests": true
}>

export function generateCSP(policy: CSPPolicies): string {
let _directives = []

const joinSpaces = (s: string[]) => s.join(" ")

Object.entries(policy).forEach(([directive, values]) => {
if (directive !== "upgrade-insecure-requests") {
const parsedValues = joinSpaces(
(values as string[]).map((value) => {
if (value === "self") return `'self'`
if (value === "none") return `'none'`
if (value === "unsafe-inline") return `'unsafe-inline'`
if (value === "unsafe-hashes") return `'unsafe-hashes'`
if (value === "unsafe-eval") return `'unsafe-eval'`
if (value.startsWith("nonce-") || value.startsWith("sha"))
return `'${value}'`
return value
})
)

_directives.push(`${directive} ${parsedValues};`)
}
if (directive == "upgrade-insecure-requests") {
_directives.push(`upgrade-insecure-requests;`)
}
})

return joinSpaces(_directives)
.replace(/(\s;\s)/g, "; ")
.replace(/\s;/g, ";")
}
105 changes: 0 additions & 105 deletions apps/website/src/utils/generateCSPString.ts

This file was deleted.

4 changes: 2 additions & 2 deletions apps/website/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { generateCSPString } from "./generateCSPString"
import { generateCSP } from "./generateCSP"

export { generateCSPString }
export { generateCSP }

0 comments on commit 591e584

Please sign in to comment.