環境
- 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を利用したバージョンのサンプルも作成した