@next-safe/middleware
Strict Content-Security-Policy (CSP) for Next.js with composable middleware
Works for hybrid apps and supports pages with any data fetching method.
Always sets CSP by HTTP Response header and enables easy setup of reporting.
Getting started
Read the docs
Contribute
What
This package strives to make the setup and deployment of a Strict Content Security Policy (CSP)
with Next.js an easy task. The design approach that makes this possible requires Next.js Middleware, which has been introduced as Beta in Next.js 12 and is stable since Next.js 12.2.
This package handles all Strict CSP conundrums for you and works for:
This package always sets CSP as HTTP response header. That enables violation reporting and report-only mode even for static pages. Plus, it provides a middleware and API handlers that make the setup of CSP violation reporting very easy.
Why
Configuring and maintaining a Content-Security-Policy (CSP) can be a tedious and error prone task. Furthermore, classic CSPs with a whitelist approach don't give you the security you might think you get from them, because in a lot of cases, they are automatically bypassable.
There is a much better option: a Hash-based/Nonce-based Strict CSP.
Such CSPs provide much better security and have always the same structure, so they don't need the maintenance that whitelist CSPs need, once they've been set up properly. But this setup is usually a a very big issue with Next.js (and with all web frameworks in general).
This is where this package comes in: To make this setup easy, convenient and a lot less error-prone.
Good Resources about (Strict) Content-Security-Policy (CSP)
Getting started
Install @next-safe/middleware
from NPM
npm -i @next-safe/middleware
yarn add @next-safe/middleware
Quickstart: Strict Content-Security-Policy (CSP)
Create the file middleware.js
in your Next.js project folder:
import {
chainMatch,
isPageRequest,
csp,
strictDynamic,
} from "@next-safe/middleware";
const securityMiddleware = [
csp({
directives: {
"img-src": ["self", "data:", "https://images.unsplash.com"],
"font-src": ["self", "https://fonts.gstatic.com"],
},
}),
strictDynamic(),
];
export default chainMatch(isPageRequest)(...securityMiddleware);
Create the file pages/_document.js
in your Next.js project folder:
import {
getCspInitialProps,
provideComponents,
} from "@next-safe/middleware/dist/document";
import Document, { Html, Main } from "next/document";
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await getCspInitialProps({ ctx });
return initialProps;
}
render() {
const { Head, NextScript } = provideComponents(this.props);
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
For every page under pages
that uses getServerSideProps
for data fetching:
import { gsspWithNonce } from "@next-safe/middleware/dist/document";
export const getServerSideProps = gsspWithNonce(async (ctx) => {
return { props: { message: "Hi, from getServerSideProps" } };
});
const Page = ({ message, nonce }) => <h1>{`${message}. Nonce ${nonce}`}</h1>;
export default Page;
Thats it. You should be all set now with a Strict CSP for your Next.js app!
Quickstart: CSP Violation Reporting
Add the reporting
middleware in middleware.js
:
import {
chainMatch,
isPageRequest
csp,
reporting,
strictDynamic,
strictInlineStyles,
} from '@next-safe/middleware';
const securityMiddleware = [
csp(),
strictDynamic(),
reporting(),
];
export default chainMatch(isPageRequest)(...securityMiddleware);
Create the file pages/api/reporting.js
to set up the reporting endpoint:
import { reporting } from "@next-safe/middleware/dist/api";
const consoleLogReporter = (data) =>
console.log(JSON.stringify(data, undefined, 2));
export default reporting(consoleLogReporter);
Thats it. Browsers will send CSP violation reports to this endpoint. You can easily react on validated reporting data by adding any number of custom reporters.
Send violation reports to Sentry
If you use Sentry for monitoring your app, there is a convenient helper sentryCspReporterForEndpoint
to create a reporter, that ingests all CSP violations into your Sentry project:
import {
reporting,
sentryCspReporterForEndpoint,
} from "@next-safe/middleware/dist/api";
const sentryCspEndpoint = process.env.SENTRY_CSP_ENDPOINT;
const sentryCspReporter = sentryCspReporterForEndpoint(sentryCspEndpoint);
export default reporting(sentryCspReporter);
Quickstart: Compose middleware
Here's an example to show how you can combine security middleware from this package with your custom middleware by using chain
and chainMatch
:
import {
chain,
chainMatch,
isPageRequest,
csp,
strictDynamic,
} from "@next-safe/middleware";
const geoBlockMiddleware = (req) => {
const BLOCKED_COUNTRY = "GB";
const country = req.geo.country || "US";
if (country === BLOCKED_COUNTRY) {
const response = new Response("Blocked for legal reasons", { status: 451 });
return response;
}
};
const securityMiddleware = [csp(), strictDynamic()];
export default chain(
geoBlockMiddleware,
chainMatch(isPageRequest)(...securityMiddleware)
);
If you only want to use the composition features from this package, there's an extra bundle @next-safe/middleware/dist/compose
just for that.
Contributors
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome! Check out the contributing guide for getting started!