環境
- Nextjs 13
- edge-csrf 1.0.3-rc1
- TypeScript 5
やりたいこと
今日は、Next.jsの開発について書こうと思います。Next.jsは、開発がとても便利であることがよく知られています。
しかし、CSRFトークンの機能がRailsのようにネイティブで用意されていないため、自分で実装する必要があります。
これは面倒ですが、edge-csrfというライブラリを使えば簡単にCSRFトークンの実装ができます。先日、私自身もこのライブラリを使ってCSRFトークンの実装を行い、スムーズに開発を進めることができました。
今回はそのメモ
https://github.com/amorey/edge-csrf
インストール
npm install edge-csrf yarn add edge-csrf
ミドルウェアの追加
APIのエンドポイントごとにCSRFの実装するのは流石に面倒。そこでNextjsのミドルウェア機能を使う。これを使うとAPIリクエストごとに自動でCSRFトークンをチェックしてくれる
プロジェクト直下にmiddleware.tsを作成 人によってはsrc直下になるのかな
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; }
フロントエンドの実装
あとは以下のようなフォームを 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' }); }
確認
<input type="hidden" value={csrfToken}>
のようにセットされていればフォームの送信は成功するが
<input type="hidden" value='invalid_token'>
のようになっていると送信に失敗するはず
またcurlで試しても失敗すればおk
❯ curl -XPOST http://localhost:3000/api/form-handler/
invalid csrf token
オプション
クッキーや挙動の設定は公式ドキュメント参照
サンプル
公式Gitにサンプルプロジェクトがあるのでこれが一番参考になるかも
https://github.com/amorey/edge-csrf/tree/main/example-ts
自分用にMantineのFormを利用したバージョンのサンプルも作成した