Node.js CSRF protection middleware.Node.js CSRF保护中间件。
Requires either a session middleware or cookie-parser to be initialized first.需要首先初始化会话中间件或cookie解析器。
false
value, then you must use cookie-parser before this module.cookie
选项设置为非false
值,则必须在此模块之前使用cookie解析器。If you have questions on how this module is implemented, please read Understanding CSRF.如果您对如何实施本模块有疑问,请阅读理解CSRF。
This is a Node.js module available through the npm registry. 这是一个Node.js模块,可通过npm注册表获得。Installation is done using the 使用npm install
command:npm install
命令完成安装:
$ npm install csurf
var csurf = require('csurf')
Create a middleware for CSRF token creation and validation. 创建用于CSRF令牌创建和验证的中间件。This middleware adds a 此中间件添加了一个req.csrfToken()
function to make a token which should be added to requests which mutate state, within a hidden form field, query-string etc. req.csrfToken()
函数,以生成一个令牌,该令牌应添加到在隐藏表单字段、查询字符串等中改变状态的请求中。This token is validated against the visitor's session or csrf cookie.此令牌根据访问者的会话或csrf cookie进行验证。
The csurf
function takes an optional options
object that may contain any of the following keys:csurf
函数采用可选options
对象,该对象可能包含以下任意键:
Determines if the token secret for the user should be stored in a cookie or in 确定是否应将用户的令牌密钥存储在cookie或req.session
. req.session
中。Storing the token secret in a cookie implements the double submit cookie pattern. 将令牌机密存储在cookie中实现双重提交cookie模式。Defaults to 默认为false
.false
。
When set to 当设置为true
(or an object of options for the cookie), then the module changes behavior and no longer uses req.session
. true
(或cookie的选项对象)时,模块将更改行为,不再使用req.session
。This means you are no longer required to use a session middleware. 这意味着您不再需要使用会话中间件。Instead, you do need to use the cookie-parser middleware in your app before this middleware.相反,您确实需要在应用程序中使用cookie解析器中间件,然后再使用该中间件。
When set to an object, cookie storage of the secret is enabled and the object contains options for this functionality (when set to 设置为对象时,将启用机密的cookie存储,并且对象包含此功能的选项(设置为true
, the defaults for the options are used). The options may contain any of the following keys:true
时,将使用选项的默认值)。选项可能包含以下任意键:
key
- '_csrf'
).'_csrf'
)。path
- '/'
).'/'
)。signed
- false
).false
)。secure
- false
).false
)。maxAge
- httpOnly
- false
).false
)。sameSite
- false
). false
)。'strict'
, 'lax'
, 'none'
, or true
(which maps to 'strict'
).'strict'
、'lax'
、'none'
或true
(映射为'strict'
)。domain
- An array of the methods for which CSRF token checking will disabled. 将禁用其CSRF令牌检查的方法数组。Defaults to 默认设置为['GET', 'HEAD', 'OPTIONS']
.['GET', 'HEAD', 'OPTIONS']
。
Determines what property ("key") on 确定会话对象req
the session object is located. req
上的属性(“键”)所在位置。Defaults to 默认为'session'
(i.e. looks at req.session
). 'session'
(即查看req.session
)。The CSRF secret from this library is stored and read as 此库中的CSRF机密存储并读取为req[sessionKey].csrfSecret
.req[sessionKey].csrfSecret
。
If the "cookie" option is not 如果false
, then this option does nothing."cookie"
选项不为false
,则此选项不起任何作用。
Provide a function that the middleware will invoke to read the token from the request for validation. 提供一个函数,中间件将调用该函数从验证请求中读取令牌。The function is called as 该函数以value(req)
and is expected to return the token as a string.value(req)
的形式调用,并期望以字符串形式返回令牌。
The default value is a function that reads the token from the following locations, in order:默认值是从以下位置按顺序读取令牌的函数:
req.body._csrf
- body-parser
module.body-parser
模块生成。req.query._csrf
- req.headers['csrf-token']
- CSRF-Token
HTTP request header.CSRF-Token
HTTP请求标头。req.headers['xsrf-token']
- XSRF-Token
HTTP request header.XSRF-Token
HTTP请求标头。req.headers['x-csrf-token']
- X-CSRF-Token
HTTP request header.X-CSRF-Token
HTTP请求标头。req.headers['x-xsrf-token']
- X-XSRF-Token
HTTP request header.X-XSRF-Token
HTTP请求标头The following is an example of some server-side code that generates a form that requires a CSRF token to post back.下面是一些服务器端代码的示例,这些代码生成的表单需要CSRF令牌才能回发。
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
// setup route middlewares设置路由中间件
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
// create express app创建快捷应用程序
var app = express()
// parse cookies解析cookies
// we need this because "cookie" is true in csrfProtection
app.use(cookieParser())
app.get('/form', csrfProtection, function (req, res) {
// pass the csrfToken to the view
res.render('send', { csrfToken: req.csrfToken() })
})
app.post('/process', parseForm, csrfProtection, function (req, res) {
res.send('data is being processed')
})
Inside the view (depending on your template language; handlebars-style is demonstrated here), set the 在视图内部(取决于模板语言;此处演示了把手样式),将csrfToken
value as the value of a hidden input field named _csrf
:csrfToken
值设置为名为_csrf
:
<form action="/process" method="POST">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
Favorite color: <input type="text" name="favoriteColor">
<button type="submit">Submit</button>
</form>
When accessing protected routes via ajax both the csrf token will need to be passed in the request. 当通过ajax访问受保护的路由时,需要在请求中传递csrf令牌。Typically this is done using a request header, as adding a request header can typically be done at a central location easily without payload modification.通常,这是使用请求头完成的,因为添加请求头通常可以在中心位置轻松完成,而无需修改有效负载。
The CSRF token is obtained from the CSRF令牌是从服务器端的req.csrfToken()
call on the server-side. req.csrfToken()
调用获得的。This token needs to be exposed to the client-side, typically by including it in the initial page content. 此令牌需要向客户端公开,通常是通过将其包含在初始页面内容中。One possibility is to store it in an HTML 一种可能是将其存储在HTML中<meta>
tag, where value can then be retrieved at the time of the request by JavaScript.<meta>
标记,JavaScript可以在请求时检索该值。
The following can be included in your view (handlebar example below), where the 以下内容可以包含在视图中(下面的把手示例),其中csrfToken
value came from req.csrfToken()
:csrfToken
值来自req.csrfToken()
<meta name="csrf-token" content="{{csrfToken}}">
The following is an example of using the Fetch API to post to the 以下是使用Fetch API从页面上的/process
route with the CSRF token from the <meta>
tag on the page:<meta>
标记使用CSRF令牌发布到/process
路由的示例:
// Read the CSRF token from the <meta> tag从<meta>标签读取CSRF
var token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
// Make a request using the Fetch API
fetch('/process', {
credentials: 'same-origin', // <-- includes cookies in the request
headers: {
'CSRF-Token': token // <-- is the csrf token as a header
},
method: 'POST',
body: {
favoriteColor: 'blue'
}
})
Many SPA frameworks like Angular have CSRF support built in automatically. 许多SPA框架(如Angular)都自动内置了CSRF支持。Typically they will reflect the value from a specific cookie, like 通常,它们将反映特定cookie的值,如XSRF-TOKEN
(which is the case for Angular).XSRF-TOKEN
(Angular就是这种情况)。
To take advantage of this, set the value from 要利用这一点,请在SPA框架使用的cookie中设置req.csrfToken()
in the cookie used by the SPA framework. req.csrfToken()
中的值。This is only necessary to do on the route that renders the page (where 这只需要在呈现页面的路由上执行(例如,在Express中调用res.render
or res.sendFile
is called in Express, for example).res.render
或res.sendFile
)。
The following is an example for Express of a typical SPA response:以下是一个典型SPA响应的示例:
app.all('*', function (req, res) {
res.cookie('XSRF-TOKEN', req.csrfToken())
res.render('index')
})
Note CSRF checks should only be disabled for requests that you expect to come from outside of your website. 注意:CSRF检查只应针对您希望来自网站外部的请求禁用。Do not disable CSRF checks for requests that you expect to only come from your website. 不要对您希望仅来自您的网站的请求禁用CSRF检查。An existing session, even if it belongs to an authenticated user, is not enough to protect against CSRF attacks.现有会话,即使它属于经过身份验证的用户,也不足以抵御CSRF攻击。
The following is an example of how to order your routes so that certain endpoints do not check for a valid CSRF token.下面是一个如何对路由进行排序的示例,以便某些端点不检查有效的CSRF令牌。
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
// create express app创建快捷应用程序
var app = express()
// create api router创建api路由器
var api = createApiRouter()
// mount api before csrf is appended to the app stack在csrf附加到应用程序堆栈之前装载api
app.use('/api', api)
// now add csrf and other middlewares, after the "/api" was mounted
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(csrf({ cookie: true }))
app.get('/form', function (req, res) {
// pass the csrfToken to the view
res.render('send', { csrfToken: req.csrfToken() })
})
app.post('/process', function (req, res) {
res.send('csrf was required to get here')
})
function createApiRouter () {
var router = new express.Router()
router.post('/getProfile', function (req, res) {
res.send('no csrf to get here')
})
return router
}
When the CSRF token validation fails, an error is thrown that has 当CSRF令牌验证失败时,将抛出一个错误,错误代码为err.code === 'EBADCSRFTOKEN'
. err.code === 'EBADCSRFTOKEN'
。This can be used to display custom error messages.这可用于显示自定义错误消息。
var bodyParser = require('body-parser')
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var express = require('express')
var app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(csrf({ cookie: true }))
// error handler
app.use(function (err, req, res, next) {
if (err.code !== 'EBADCSRFTOKEN') return next(err)
// handle CSRF token errors here
res.status(403)
res.send('form tampered with')
})