csurf
Node.js 防范csrf攻击中间件
需要session中间件(如:express-session)或者cookie中间件(cookie-parser)首先被初始化
如果设置cookie选项为非false的值,需在此模块之前引用cookie-parser
-
除此之外,在此模块之前还须使用session中间件,列如:
- express-session
- cookie-session
安装
通过npm安装:
npm install csurf
API
var csurf = require('csurf')
csurf([options])
为CSRF令牌创建和验证,创建一个中间件,中间件加入 req.csrfToken()
函数创建一个应该被添加到请求中的令牌,这些令牌是随时变化的,在隐藏表单字段中,query-string等。令牌根据访问者的session和cookie来验证。
options
csurf函数具有可选项 options
对象, 此对象包含以下任意键:
- cookie
确定用户的令牌秘密是否存在cookie或 req.session
中。默认是 false
。
当cookie设置成 true
, 然后模块会改变行为并不在使用 req.session
。意味着不需要使用session中间件。相反,在此组件之前,只需使用 cookie-parser
中间件。
var csurf = require('csurf');
csurf({cookie:true})
当cookie设置成一个对象,cookie的秘密存储被启用,该对象包含此功能的可选项(当设置 true
, 默认选项被使用)。这些选项包含以下任意键:
csurf({cookie:{
key: '_csrf',
path: '/'
}})
key - 用于存储令牌的cookie名称(默认:_csrf)
path - cookie的路径(默认:/)
其他选项参考 res.cookie
- ignoreMethods
一组用于禁用CSRF令牌检查的方法。默认 [GET,'HEAD','OPTIONS']
csurf({
ignoreMethods: [GET,'HEAD','OPTIONS']
})
- sessionKey
确定位于session对象上的 req
上的的属性("key")。默认 session
(比如:req.session)。来自框架的CSRF令牌被存储并且像 req[sessionKey].csrfSecret
来获取。
如果 cookie
的选项不是false,那么这个选项无效。
- value
为了验证会提供中间件函数来调用读取来自请求的令牌。此函数 value(req)
被调用。并且它预期的以一个字符串返回令牌。
默认值是来自以下顺序读取令牌的函数:
-
req.body._csrf
- 通常通过body-parser
模块产生。 -
req.query._csrf
- express.js内置的,通过它获取来自URL的查询字符串 -
req.headers['csrf-token']
-CSRF-Token
HTTP请求头 -
req.headers['xsrf-token']
-XSRF-Token
HTTP请求头 -
req.headers['x-csrf-token']
-X-CSRF-Token
HTTP请求头 -
req.headers['x-xsrf-token']
-X-XSRF-Token
HTTP请求头
示例
express
需分别在服务端和客户端操作。
以下是服务端代码示例,生成一个需要CSRF令牌发布的表单:
var express = require('express'), // express框架
bodyparser = require('body-parser'), // 使用POST,需要引入此中间件来解析URL编码体
cookieParser = require('cookie-parser'), // cookie中间件,csurf需依赖它
csrf = require('csurf'); // csurf中间件
// 设置路由中间件
var csrfProtection = csrf({cookie:true}),
parseForm = bodyParser.urlencodeed({extended:false});
// 创建epxress app
var app = express();
// cookies
// 需要它,因为 csrfProtection 中"cookie"设置的是true,需要依赖cookie
app.use(cookieParser());
app.get('/form', csrfProtection, function (req, res) {
// 把 csrfToken 传递给视图
res.render('send', { csrfToken: req.csrfToken() })
})
app.post('/process', parseForm, csrfProtection, function (req, res) {
res.send('data is being processed')
})
以下是客户端代码示例,(依赖模板语言,此处用handlebars为例),设置 csrfToken
值作为隐藏域字段名为 _csrf
的值:
忽略路由
注意:
允许本网站以外请求并不需要验证的,可以禁用CSRF验证。
不要只对你自己的网站请求启用CSRF验证。
一个存在的session,即使它属于认证用户,不足于满足防范CSRF攻击。
以下是如何�请求理由的示例,以便某些路由不验证有效的CSRF令牌。
var express = require('express'), // express框架
bodyparser = require('body-parser'), // 使用POST,需要引入此中间件来解析URL编码体
cookieParser = require('cookie-parser'), // cookie中间件,csurf需依赖它
csrf = require('csurf'); // csurf中间件
var app = express()
// 创建api路由
var api = function(){
var router = new express.Router()
router.post('/getProfile', function (req, res) {
res.send('这里没有csrf')
})
return router
}
// 在crsf被创建前使用api中间件,不会csrf验证
app.use('/api', api)
// 在 "/api" 后, 现在加入crsf和其他中间件
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(csrf({ cookie: true }))
app.get('/form', function (req, res) {
// 把 csrfToken 传递给视图
res.render('send', { csrfToken: req.csrfToken() })
})
自定义错误处理
当CSRF令牌验证失败,抛出一个错误 err.code === 'EBADCSRFTOKEN'
。这能用于显示自定义错误信息。
var express = require('express'), // express框架
bodyparser = require('body-parser'), // 使用POST,需要引入此中间件来解析URL编码体
cookieParser = require('cookie-parser'), // cookie中间件,csurf需依赖它
csrf = require('csurf'); // csurf中间件
var app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(csrf({ cookie: true }))
// 错误处理
app.use(function (err, req, res, next) {
if (err.code !== 'EBADCSRFTOKEN') return next(err)
// CSRF验证失败
res.status(403)
res.send('form tampered with')
})