Node.js で CSRF対策

helmetを導入することである程度セキュリティ対策をしてくれるが
CSRF対策とインジェクション対策は別途対応しないといけない
少しソースを変更することになるので早めに対応すること





CSRF(クロスサイトリクエストフォージェリ)とは


攻撃者の用意したサイトより意図しないリクエストが送信され誤って処理してしまうこと。
例えばECサイトなどでログイン済みだった場合など、
外部サイトから実装上適切な購入処理がリクエストされると
ユーザが意図せず商品を購入してしまうというもの

対策するためには
  • 画面表示時にサーバで生成したトークンを埋め込む
  • POSTした時にトークンも一緒に送信
  • サーバ側で突合
ということをやれば良い


csurfを使用したcsrf対策



環境はNode.js * Express * Pug

csurfはセッションとクッキーを利用しているそうなので一緒にインストールする

npm install --save cookie-parser
npm install --save express-session
npm install --save csurf

app.jsに以下を追記
csurfはクッキーとセッションの後に指定すること

var cookieParser = require("cookie-parser")
var session = require("express-session")
var csurf = require('csurf')

// ...


// クッキー
app.use(cookieParser())
// セッション管理
app.use(session({
  secret: "任意の文字列",
  resave: false,
  saveUninitialized: false
}));
// csrf
app.use(csurf({ cookie: true }))

/// ...

req.csrfToken() でランダムなトークンを取得できる
このトークンをフォームの隠しフィールドに仕込む

ブラウザからポストされた時にトークンのチェックを実行してくれる
攻撃者からトークンが盗まれない限り安全

デフォルトだとGETはそのまま通ってしまう
データ更新が発生するPOSTだけで問題ない?


以下具体例

route login
const express = require("express")
const router = express.Router()

/* GET home page. */
router.get("/", (req, res, next) => {
  const title = "ログイン"
  res.render("index", { title: title, csrfToken: req.csrfToken() })
})

module.exports = router


html
form(methos="POST", action="next")
  input(type="text" name="id")
  input(type="password" name="pass")
  input(type="hidden", name="_csrf" value=csrfToken)
formからpostする場合form内にトークンを入れること


ログイン先のサーバ処理
const express = require("express")
const router = express.Router()

router.post("/", (req, res, next) => {
  const title: string = "ログイン後の画面"
  req.session.loginid = req.body.id
  res.render("customer", { title: title, csrfToken: req.csrfToken() })
})

javascriptのXMLHttpRequest を使用する場合はヘッダにトークンを埋め込むといい
const xhr = new XMLHttpRequest()
xhr.open("POST", url)
xhr.setRequestHeader("X-XSRF-Token", document.getElementById("csrfToken").value)

共通画面(ヘッダ)などに埋め込んで置くとスマート
トークンが2つ埋め込まれていても動作には影響しない



参考


https://github.com/expressjs/csurf
http://dev.classmethod.jp/server-side/node-js_csrf/

あとはインジェクションか
NoSQLは対応が必要なのだろうか?

2017年6月30日金曜日