ExpressでCSRF対策を行うための csurf
モジュールの使い方を紹介します。
インストール
csurf
モジュールはnpmからインストールできます。
$ npm install --save csurf
csurf
モジュールはクッキーもしくはセッションを利用します。クッキーを利用するときは cookie-parser
モジュールを、セッションを利用するときは express-session
などのセッションモジュールを利用します。今回はセッションを利用します。
セッションを利用できるようにするため、express-session
モジュールをインストールします。またHTMLフォームにトークンを埋め込むため、テンプレートエンジンとして pug
を、POSTリクエストのボディをパースするために body-parser
モジュールを利用します。
$ npm install --save express-session pug body-parser
サンプルコード
次は csurf
モジュールを使ってCSRF対策を施した例です。
app.js
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const csrf = require('csurf');
const app = express();
const sess = {
secret: 'secretsecretsecret',
cookie: { maxAge: 60000 },
resave: false,
saveUninitialized: true,
}
if (app.get('env') === 'production') {
app.set('trust proxy', 1)
sess.cookie.secure = true
}
const csrfProtection = csrf({ cookie: false });
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session(sess));
app.set('view engine', 'pug');
app.get('/', csrfProtection, (req, res) => {
res.render('index', { csrfToken: req.csrfToken() });
});
app.post('/', csrfProtection, (req, res) => {
res.send(req.body.text);
});
app.listen('3000', () => {
console.log('Application started');
});
まず const csrf = require('csurf');
でモジュールをインストールしたのち、 const csrfProtection = csrf()
でCSRF対策のためのミドルウェアを作ります。今回はクッキーではなくセッションにトークンを保持してCSRF対策を行うので cookie
オプションをfalseに設定しました。
あとは csrfProtection
をCSRF対策を施したい各ルートに対して埋め込みます。トークンは req.csrfToken()
で取得できるので、これをテンプレートにパラメータとして渡してフォームに埋め込みます。`
トークンは _csrf
という名前で埋め込みます。
views/index.pug
html
body
form(action="/" method="POST")
input(type="hidden" name="_csrf" value=csrfToken)
input(type="text" name="text" value="")
input(type="submit" name="submit")
動作確認
http://localhost:3000 にアクセスし、フォームのHTMLソースを表示すると次のようにトークンが埋め込まれていることを確認できます。
<form action="/" method="POST">
<input type="hidden" name="_csrf" value="xe1t9is6-Q1bcuuJ8G5rdTXWCRqzkSat7FUI">
<input type="text" name="text" value="">
<input type="submit" name="submit">
</form>
このフォームをポストすると csrfProtection
フォームによりトークンのチェックが行われます。試しにブラウザの開発者ツールなどでトークンを変更したりするとリクエストがエラーになることが確認できます。
Ajaxの場合
Ajaxリクエストの場合はフォームではなく、HTMLの <head>
タグ内の <meta>
タグとしてトークンを埋め込むのが簡単です。
<meta name="csrf-token" content="xe1t9is6-Q1bcuuJ8G5rdTXWCRqzkSat7FUI">
リクエストする時は次のように CSRF-Token
ヘッダにトークンを仕込みます。
var token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
fetch('/process', {
credentials: 'same-origin',
headers: {
'CSRF-Token': token
},
method: 'POST',
body: {
favoriteColor: 'blue'
}
})