This repository has been archived on 2018-10-12. You can view files and clone it, but cannot push or open issues or pull requests.
node-task/node_modules/csurf/index.js

239 lines
4.9 KiB
JavaScript

/*!
* csurf
* Copyright(c) 2011 Sencha Inc.
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2014 Douglas Christopher Wilson
* MIT Licensed
*/
/**
* Module dependencies.
*/
var Cookie = require('cookie');
var csrfTokens = require('csrf');
var sign = require('cookie-signature').sign;
/**
* CSRF protection middleware.
*
* 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. This
* token is validated against the visitor's session.
*
* @param {Object} options
* @return {Function} middleware
* @api public
*/
module.exports = function csurf(options) {
options = options || {};
// get value getter
var value = options.value || defaultValue
// token repo
var tokens = csrfTokens(options);
// default cookie key
if (options.cookie && !options.cookie.key) {
options.cookie.key = '_csrf'
}
// ignored methods
var ignoreMethods = options.ignoreMethods === undefined
? ['GET', 'HEAD', 'OPTIONS']
: options.ignoreMethods
if (!Array.isArray(ignoreMethods)) {
throw new TypeError('option ignoreMethods must be an array')
}
// generate lookup
var ignoreMethod = getIgnoredMethods(ignoreMethods)
return function csrf(req, res, next) {
var secret = getsecret(req, options.cookie)
var token
// lazy-load token getter
req.csrfToken = function csrfToken() {
var sec = !options.cookie
? getsecret(req, options.cookie)
: secret
// use cached token if secret has not changed
if (token && sec === secret) {
return token
}
// generate & set new secret
if (sec === undefined) {
sec = tokens.secretSync()
setsecret(req, res, sec, options.cookie)
}
// update changed secret
secret = sec
// create new token
token = tokens.create(secret)
return token
}
// generate & set secret
if (!secret) {
secret = tokens.secretSync()
setsecret(req, res, secret, options.cookie)
}
// verify the incoming token
if (!ignoreMethod[req.method]) {
verifytoken(req, tokens, secret, value(req))
}
next()
}
};
/**
* Default value function, checking the `req.body`
* and `req.query` for the CSRF token.
*
* @param {IncomingMessage} req
* @return {String}
* @api private
*/
function defaultValue(req) {
return (req.body && req.body._csrf)
|| (req.query && req.query._csrf)
|| (req.headers['x-csrf-token'])
|| (req.headers['x-xsrf-token']);
}
/**
* Get a lookup of ignored methods.
*
* @param {array} methods
* @returns {object}
* @api private
*/
function getIgnoredMethods(methods) {
var obj = Object.create(null)
for (var i = 0; i < methods.length; i++) {
var method = methods[i].toUpperCase()
obj[method] = true
}
return obj
}
/**
* Get the token secret from the request.
*
* @param {IncomingMessage} req
* @param {Object} [cookie]
* @api private
*/
function getsecret(req, cookie) {
var secret
if (cookie) {
// get secret from cookie
var bag = cookie.signed
? 'signedCookies'
: 'cookies'
secret = req[bag][cookie.key]
} else if (req.session) {
// get secret from session
secret = req.session.csrfSecret
} else {
throw new Error('misconfigured csrf')
}
return secret
}
/**
* Set a cookie on the HTTP response.
*
* @param {OutgoingMessage} res
* @param {string} name
* @param {string} val
* @param {Object} [options]
* @api private
*/
function setcookie(res, name, val, options) {
var data = Cookie.serialize(name, val, options);
var prev = res.getHeader('set-cookie') || [];
var header = Array.isArray(prev) ? prev.concat(data)
: Array.isArray(data) ? [prev].concat(data)
: [prev, data];
res.setHeader('set-cookie', header);
}
/**
* Set the token secret on the request.
*
* @param {IncomingMessage} req
* @param {OutgoingMessage} res
* @param {string} val
* @param {Object} [cookie]
* @api private
*/
function setsecret(req, res, val, cookie) {
if (cookie) {
// set secret on cookie
if (cookie.signed) {
var secret = req.secret
if (!secret) {
throw new Error('cookieParser("secret") required for signed cookies')
}
val = 's:' + sign(val, secret)
}
setcookie(res, cookie.key, val, cookie);
} else if (req.session) {
// set secret on session
req.session.csrfSecret = val
} else {
/* istanbul ignore next: should never actually run */
throw new Error('misconfigured csrf')
}
}
/**
* Verify the token.
*
* @param {IncomingMessage} req
* @param {Object} tokens
* @param {string} secret
* @param {string} val
* @api private
*/
function verifytoken(req, tokens, secret, val) {
// valid token
if (tokens.verify(secret, val)) {
return
}
var err = new Error('invalid csrf token')
err.status = 403
err.code = 'EBADCSRFTOKEN'
throw err
}