Comparing version 1.8.3 to 1.9.0
@@ -0,1 +1,32 @@ | ||
1.9.0 / 2016-05-27 | ||
================== | ||
* Pass invalid csrf token error to `next()` instead of throwing | ||
* Pass misconfigured error to `next()` instead of throwing | ||
* Provide misconfigured error when using cookies without cookie-parser | ||
* deps: cookie@0.3.1 | ||
- Add `sameSite` option | ||
- Fix cookie `Max-Age` to never be a floating point number | ||
- Improve error message when `expires` is not a `Date` | ||
- Throw better error for invalid argument to parse | ||
- Throw on invalid values provided to `serialize` | ||
- perf: enable strict mode | ||
- perf: hoist regular expression | ||
- perf: use for loop in parse | ||
- perf: use string concatination for serialization | ||
* deps: csrf@~3.0.3 | ||
- Use `tsscmp` module for timing-safe token verification | ||
- deps: base64-url@1.2.2 | ||
- deps: rndm@1.2.0 | ||
- deps: uid-safe@2.1.1 | ||
* deps: http-errors@~1.5.0 | ||
- Add `HttpError` export, for `err instanceof createError.HttpError` | ||
- Support new code `421 Misdirected Request` | ||
- Use `setprototypeof` module to replace `__proto__` setting | ||
- deps: inherits@2.0.1 | ||
- deps: statuses@'>= 1.3.0 < 2' | ||
- perf: enable strict mode | ||
* perf: enable strict mode | ||
* perf: remove argument reassignment | ||
1.8.3 / 2015-06-10 | ||
@@ -2,0 +33,0 @@ ================== |
159
index.js
@@ -5,6 +5,8 @@ /*! | ||
* Copyright(c) 2014 Jonathan Ong | ||
* Copyright(c) 2014-2015 Douglas Christopher Wilson | ||
* Copyright(c) 2014-2016 Douglas Christopher Wilson | ||
* MIT Licensed | ||
*/ | ||
'use strict' | ||
/** | ||
@@ -15,8 +17,15 @@ * Module dependencies. | ||
var Cookie = require('cookie'); | ||
var createError = require('http-errors'); | ||
var sign = require('cookie-signature').sign; | ||
var Tokens = require('csrf'); | ||
var Cookie = require('cookie') | ||
var createError = require('http-errors') | ||
var sign = require('cookie-signature').sign | ||
var Tokens = require('csrf') | ||
/** | ||
* Module exports. | ||
* @public | ||
*/ | ||
module.exports = csurf | ||
/** | ||
* CSRF protection middleware. | ||
@@ -31,24 +40,24 @@ * | ||
* @return {Function} middleware | ||
* @api public | ||
* @public | ||
*/ | ||
module.exports = function csurf(options) { | ||
options = options || {}; | ||
function csurf (options) { | ||
var opts = options || {} | ||
// get cookie options | ||
var cookie = getCookieOptions(options.cookie) | ||
var cookie = getCookieOptions(opts.cookie) | ||
// get session options | ||
var sessionKey = options.sessionKey || 'session' | ||
var sessionKey = opts.sessionKey || 'session' | ||
// get value getter | ||
var value = options.value || defaultValue | ||
var value = opts.value || defaultValue | ||
// token repo | ||
var tokens = new Tokens(options); | ||
var tokens = new Tokens(opts) | ||
// ignored methods | ||
var ignoreMethods = options.ignoreMethods === undefined | ||
var ignoreMethods = opts.ignoreMethods === undefined | ||
? ['GET', 'HEAD', 'OPTIONS'] | ||
: options.ignoreMethods | ||
: opts.ignoreMethods | ||
@@ -62,10 +71,16 @@ if (!Array.isArray(ignoreMethods)) { | ||
return function csrf(req, res, next) { | ||
var secret = getsecret(req, sessionKey, cookie) | ||
return function csrf (req, res, next) { | ||
// validate the configuration against request | ||
if (!verifyConfiguration(req, sessionKey, cookie)) { | ||
return next(new Error('misconfigured csrf')) | ||
} | ||
// get the secret from the request | ||
var secret = getSecret(req, sessionKey, cookie) | ||
var token | ||
// lazy-load token getter | ||
req.csrfToken = function csrfToken() { | ||
req.csrfToken = function csrfToken () { | ||
var sec = !cookie | ||
? getsecret(req, sessionKey, cookie) | ||
? getSecret(req, sessionKey, cookie) | ||
: secret | ||
@@ -81,3 +96,3 @@ | ||
sec = tokens.secretSync() | ||
setsecret(req, res, sessionKey, sec, cookie) | ||
setSecret(req, res, sessionKey, sec, cookie) | ||
} | ||
@@ -97,8 +112,10 @@ | ||
secret = tokens.secretSync() | ||
setsecret(req, res, sessionKey, secret, cookie) | ||
setSecret(req, res, sessionKey, secret, cookie) | ||
} | ||
// verify the incoming token | ||
if (!ignoreMethod[req.method]) { | ||
verifytoken(req, tokens, secret, value(req)) | ||
if (!ignoreMethod[req.method] && !tokens.verify(secret, value(req))) { | ||
return next(createError(403, 'invalid csrf token', { | ||
code: 'EBADCSRFTOKEN' | ||
})) | ||
} | ||
@@ -108,3 +125,3 @@ | ||
} | ||
}; | ||
} | ||
@@ -120,9 +137,9 @@ /** | ||
function defaultValue(req) { | ||
return (req.body && req.body._csrf) | ||
|| (req.query && req.query._csrf) | ||
|| (req.headers['csrf-token']) | ||
|| (req.headers['xsrf-token']) | ||
|| (req.headers['x-csrf-token']) | ||
|| (req.headers['x-xsrf-token']); | ||
function defaultValue (req) { | ||
return (req.body && req.body._csrf) || | ||
(req.query && req.query._csrf) || | ||
(req.headers['csrf-token']) || | ||
(req.headers['xsrf-token']) || | ||
(req.headers['x-csrf-token']) || | ||
(req.headers['x-xsrf-token']) | ||
} | ||
@@ -138,3 +155,3 @@ | ||
function getCookieOptions(options) { | ||
function getCookieOptions (options) { | ||
if (options !== true && typeof options !== 'object') { | ||
@@ -170,3 +187,3 @@ return undefined | ||
function getIgnoredMethods(methods) { | ||
function getIgnoredMethods (methods) { | ||
var obj = Object.create(null) | ||
@@ -191,20 +208,37 @@ | ||
function getsecret(req, sessionKey, cookie) { | ||
var secret | ||
function getSecret (req, sessionKey, cookie) { | ||
// get the bag & key | ||
var bag = getSecretBag(req, sessionKey, cookie) | ||
var key = cookie ? cookie.key : 'csrfSecret' | ||
if (!bag) { | ||
/* istanbul ignore next: should never actually run */ | ||
throw new Error('misconfigured csrf') | ||
} | ||
// return secret from bag | ||
return bag[key] | ||
} | ||
/** | ||
* Get the token secret bag from the request. | ||
* | ||
* @param {IncomingMessage} req | ||
* @param {String} sessionKey | ||
* @param {Object} [cookie] | ||
* @api private | ||
*/ | ||
function getSecretBag (req, sessionKey, cookie) { | ||
if (cookie) { | ||
// get secret from cookie | ||
var bag = cookie.signed | ||
var cookieKey = cookie.signed | ||
? 'signedCookies' | ||
: 'cookies' | ||
secret = req[bag][cookie.key] | ||
} else if (req[sessionKey]) { | ||
return req[cookieKey] | ||
} else { | ||
// get secret from session | ||
secret = req[sessionKey].csrfSecret | ||
} else { | ||
throw new Error('misconfigured csrf') | ||
return req[sessionKey] | ||
} | ||
return secret | ||
} | ||
@@ -222,11 +256,11 @@ | ||
function setcookie(res, name, val, options) { | ||
var data = Cookie.serialize(name, val, options); | ||
function setCookie (res, name, val, options) { | ||
var data = Cookie.serialize(name, val, options) | ||
var prev = res.getHeader('set-cookie') || []; | ||
var prev = res.getHeader('set-cookie') || [] | ||
var header = Array.isArray(prev) ? prev.concat(data) | ||
: Array.isArray(data) ? [prev].concat(data) | ||
: [prev, data]; | ||
: [prev, data] | ||
res.setHeader('set-cookie', header); | ||
res.setHeader('set-cookie', header) | ||
} | ||
@@ -245,3 +279,3 @@ | ||
function setsecret(req, res, sessionKey, val, cookie) { | ||
function setSecret (req, res, sessionKey, val, cookie) { | ||
if (cookie) { | ||
@@ -253,3 +287,4 @@ // set secret on cookie | ||
if (!secret) { | ||
throw new Error('cookieParser("secret") required for signed cookies') | ||
/* istanbul ignore next: should never actually run */ | ||
throw new Error('misconfigured csrf') | ||
} | ||
@@ -260,3 +295,3 @@ | ||
setcookie(res, cookie.key, val, cookie); | ||
setCookie(res, cookie.key, val, cookie) | ||
} else if (req[sessionKey]) { | ||
@@ -272,18 +307,16 @@ // set secret on session | ||
/** | ||
* Verify the token. | ||
* | ||
* @param {IncomingMessage} req | ||
* @param {Object} tokens | ||
* @param {string} secret | ||
* @param {string} val | ||
* @api private | ||
* Verify the configuration against the request. | ||
* @private | ||
*/ | ||
function verifytoken(req, tokens, secret, val) { | ||
// valid token | ||
if (!tokens.verify(secret, val)) { | ||
throw createError(403, 'invalid csrf token', { | ||
code: 'EBADCSRFTOKEN' | ||
}); | ||
function verifyConfiguration (req, sessionKey, cookie) { | ||
if (!getSecretBag(req, sessionKey, cookie)) { | ||
return false | ||
} | ||
if (cookie && cookie.signed && !req.secret) { | ||
return false | ||
} | ||
return true | ||
} |
{ | ||
"name": "csurf", | ||
"description": "CSRF token middleware", | ||
"version": "1.8.3", | ||
"version": "1.9.0", | ||
"author": "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)", | ||
@@ -12,15 +12,19 @@ "contributors": [ | ||
"dependencies": { | ||
"cookie": "0.1.3", | ||
"cookie": "0.3.1", | ||
"cookie-signature": "1.0.6", | ||
"csrf": "~3.0.0", | ||
"http-errors": "~1.3.1" | ||
"csrf": "~3.0.3", | ||
"http-errors": "~1.5.0" | ||
}, | ||
"devDependencies": { | ||
"body-parser": "~1.12.4", | ||
"connect": "3", | ||
"cookie-parser": "~1.3.5", | ||
"body-parser": "1.15.1", | ||
"connect": "3.4.1", | ||
"cookie-parser": "1.4.3", | ||
"cookie-session": "~1.1.0", | ||
"istanbul": "0.3.15", | ||
"mocha": "2.2.5", | ||
"supertest": "1.0.1" | ||
"eslint": "2.10.2", | ||
"eslint-config-standard": "5.3.1", | ||
"eslint-plugin-promise": "1.3.1", | ||
"eslint-plugin-standard": "1.3.2", | ||
"istanbul": "0.4.3", | ||
"mocha": "2.5.3", | ||
"supertest": "1.1.0" | ||
}, | ||
@@ -31,2 +35,3 @@ "engines": { | ||
"scripts": { | ||
"lint": "eslint **/*.js", | ||
"test": "mocha --check-leaks --reporter spec --bail test/", | ||
@@ -33,0 +38,0 @@ "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --check-leaks --reporter dot test/", |
@@ -147,2 +147,53 @@ # csurf | ||
### Ignoring Routes | ||
**Note** CSRF checks should only be disabled for requests that you expect to | ||
come from outside of your website. Do not disable CSRF checks for requests | ||
that you expect to only come from your website. An existing session, even if | ||
it belongs to an authenticated user, is not enough to protect against CSRF | ||
attacks. | ||
The following is an example of how to order your routes so that certain endpoints | ||
do not check for a valid CSRF token. | ||
```js | ||
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 | ||
var api = createApiRouter() | ||
// mount api before csrf is appended to the app stack | ||
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 | ||
} | ||
``` | ||
### Custom error handling | ||
@@ -149,0 +200,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
20252
249
239
11
+ Addedcookie@0.3.1(transitive)
+ Addedhttp-errors@1.5.1(transitive)
+ Addedinherits@2.0.3(transitive)
+ Addedsetprototypeof@1.0.2(transitive)
- Removedcookie@0.1.3(transitive)
- Removedhttp-errors@1.3.1(transitive)
- Removedinherits@2.0.4(transitive)
Updatedcookie@0.3.1
Updatedcsrf@~3.0.3
Updatedhttp-errors@~1.5.0