18. Security
Introduction
Security is a cornerstone of any production‑ready Next.js application. As developers we must protect user data, prevent unauthorized actions, and safeguard credentials. This chapter walks through the three most prevalent injection‑style attacks—Cross‑Site Scripting (XSS), Cross‑Site Request Forgery (CSRF), and SQL Injection—and shows concrete mitigation techniques that fit naturally into the Next.js ecosystem. The second half focuses on secrets management, explaining how to store API keys, database passwords, and JWT signatures safely using environment variables, naming conventions, and external vaults.
Cross‑Site Scripting (XSS)
XSS occurs when untrusted data is injected into a page and executed as JavaScript in the victim’s browser. In Next.js, the most common vector is rendering user‑supplied HTML without proper escaping.
Why the naïve approach is dangerous
<div dangerouslySetInnerHTML={{ __html: userComment }} /> // XSS if userComment contains <script>
If userComment contains a script tag, the browser will execute it, potentially stealing cookies, performing actions on behalf of the user, or defacing the site.
Safe default rendering
React (and thus Next.js) automatically escapes strings placed inside JSX curly braces:
<div>{userComment}</div> // React escapes by default
This converts characters like <, >, &, ", and ' into their HTML entities, neutralizing script tags.
When HTML is required
Sometimes you need to allow a limited set of HTML (e.g., bold, links). In those cases, sanitize the input before injecting it:
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userComment) }} />
DOMPurify removes dangerous constructs while preserving safe markup. Remember to keep the library up‑to‑date and configure it with a whitelist that matches your application’s needs.
Additional XSS hardening tips
- Set a strict
Content‑Security‑Policyheader (e.g.,default-src 'self'; script-src 'self') to mitigate the impact of any slipped‑through script. - Avoid using
dangerouslySetInnerHTMLaltogether unless absolutely necessary. - Validate input on both client and server sides; reject anything that does not conform to an expected format.
Cross‑Site Request Forgery (CSRF)
CSRF tricks an authenticated user into submitting a state‑changing request (e.g., changing email, making a purchase) without their consent. The attack exploits the fact that browsers automatically include cookies with requests to the originating site.
Mitigation strategies
- SameSite cookie attribute – Setting
SameSite=StrictorSameSite=Laxprevents the browser from sending cookies on cross‑site requests. - Anti‑CSRF tokens – A unique, unpredictable token is embedded in forms or headers and verified on the server.
- Origin/Referer validation – The server checks that the request originates from a trusted origin.
Next.js API route example with double‑submit cookie
The csrf-csrf package provides a convenient middleware that implements the double‑submit cookie pattern.
import { csrfProtection } from "csrf-csrf";
export default async function handler(req, res) {
if (req.method