amorey / edge-csrf

CSRF protection for Next.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Edge-CSRF

Edge-CSRF is CSRF protection for Next.js that runs in middleware (edge runtime).

This library uses the cookie strategy from expressjs/csurf and the crypto logic from pillarjs/csrf except it only uses Next.js edge runtime dependencies so it can be used in Next.js middleware.

Features

  • Supports app-router and pages-router Next.js 13 and Next.js 14
  • Runs in edge runtime
  • Implements cookie strategy from expressjs/csurf and the crypto logic from pillarjs/csrf
  • Gets token from HTTP request header (X-CSRF-Token) or from request body field (csrf_token)
  • Handles form-urlencoded, multipart/form-data or json-encoded HTTP request bodies
  • Customizable cookie options
  • TypeScript definitions included

Note: There's an issue with Next.js middleware in v13.3.X and v13.4.X that prevents edge-csrf from working properly with the pages-router in a dev environment (vercel/next.js#48083, vercel/next.js#48546)

Quickstart

To use Edge-CSRF, first add it as a dependency to your app:

npm install edge-csrf
# or
pnpm add edge-csrf
# or
yarn add edge-csrf

Next, create a middleware file (middleware.ts) for your project and add the Edge-CSRF middleware:

// middleware.ts

import csrf from 'edge-csrf';
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

// initalize protection function
const csrfProtect = csrf({
  cookie: {
    secure: process.env.NODE_ENV === 'production',
  },
});

export async function middleware(request: NextRequest) {
  const response = NextResponse.next();

  // csrf protection
  const csrfError = await csrfProtect(request, response);

  // check result
  if (csrfError) {
      return new NextResponse('invalid csrf token', { status: 403 });
  }
    
  return response;
}

Now, all HTTP submission requests (e.g. POST, PUT, DELETE, PATCH) will be rejected if they do not include a valid CSRF token. To add the CSRF token to your forms, you can fetch it from the X-CSRF-Token HTTP response header server-side or client-side. For example:

App Router

// app/page.tsx

import { headers } from 'next/headers';

export default function Page() {
  const csrfToken = headers().get('X-CSRF-Token') || 'missing';

  return (
    <form action="/api/form-handler" method="post">
      <input type="hidden" value={csrfToken}>
      <input type="text" name="my-input">
      <input type="submit">
    </form>
  );
}
// app/form-handler/route.ts

import { NextResponse } from 'next/server';

export async function POST() {
  return NextResponse.json({ status: 'success'});
}

Pages Router

// pages/form.ts

import type { NextPage, GetServerSideProps } from 'next';
import React from 'react';

type Props = {
  csrfToken: string;
};

export const getServerSideProps: GetServerSideProps = async ({ res }) => {
  const csrfToken = res.getHeader('x-csrf-token') || 'missing';
  return { props: { csrfToken } };
}

const FormPage: NextPage<Props> = ({ csrfToken }) => {
  return (
    <form action="/api/form-handler" method="post">
      <input type="hidden" value={csrfToken}>
      <input type="text" name="my-input">
      <input type="submit">
    </form>
  );
}

export default FormPage;
// pages/api/form-handler.ts

import type { NextApiRequest, NextApiResponse } from 'next';

type Data = {
  status: string
};

export default function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
  // this code won't execute unless CSRF token passes validation 
  res.status(200).json({ status: 'success' });
}

Examples

See more examples in the examples directory in this repository.

Configuration

To configure the CSRF middleware function just pass an object containing your options to the initialization method:

const csrfProtect = csrf({
  cookie: {
    name: '_myCsrfSecret'
  },
  secretByteLength: 20
});

Here are the default configuration values:

// default config

{
  cookie: {
    name: '_csrfSecret',
    path: '/',
    maxAge: undefined,
    domain: '',
    secure: true,
    httpOnly: true,
    sameSite: 'strict'
  },
  excludePathPrefixes: ['/_next/'],
  ignoreMethods: ['GET', 'HEAD', 'OPTIONS'],
  saltByteLength: 8,
  secretByteLength: 18,
  token: {
    responseHeader: 'X-CSRF-Token',
    value: undefined
  }
}

About

CSRF protection for Next.js

License:MIT License


Languages

Language:TypeScript 97.0%Language:JavaScript 1.6%Language:Shell 1.4%