Comparing version 0.1.2 to 1.0.0
197
index.js
@@ -1,85 +0,158 @@ | ||
var crypto = require('crypto') | ||
var uid = require('uid') | ||
/*! | ||
* Connect - csrf | ||
* Copyright(c) 2011 Sencha Inc. | ||
* MIT Licensed | ||
*/ | ||
var ignoreMethods = { | ||
GET: true, | ||
HEAD: true, | ||
OPTIONS: true | ||
} | ||
/** | ||
* Module dependencies. | ||
*/ | ||
exports.get = function () { | ||
var csrf = this._csrf | ||
if (csrf) | ||
return csrf | ||
var uid = require('uid2'); | ||
var crypto = require('crypto'); | ||
var salt = uid() | ||
/** | ||
* Anti CSRF: | ||
* | ||
* 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. | ||
* | ||
* The default `value` function checks `req.body` generated | ||
* by the `bodyParser()` middleware, `req.query` generated | ||
* by `query()`, and the "X-CSRF-Token" header field. | ||
* | ||
* This middleware requires session support, thus should be added | ||
* somewhere _below_ `session()` and `cookieParser()`. | ||
* | ||
* Options: | ||
* | ||
* - `value` a function accepting the request, returning the token | ||
* | ||
* @param {Object} options | ||
* @api public | ||
*/ | ||
return this._csrf = salt + ';' | ||
+ createHash(salt, this.session.secret) | ||
} | ||
module.exports = function csrf(options) { | ||
options = options || {}; | ||
var value = options.value || defaultValue; | ||
exports.set = function (req, res, next) { | ||
var secret = req.session.secret | ||
if (secret) | ||
return next() | ||
return function(req, res, next){ | ||
crypto.pseudoRandomBytes(24, function (err, buf) { | ||
if (err) | ||
return next(err) | ||
// already have one | ||
var secret = req.session.csrfSecret; | ||
if (secret) return createToken(secret); | ||
req.session.secret = buf.toString('base64') | ||
next() | ||
}) | ||
} | ||
// generate secret | ||
uid(24, function(err, secret){ | ||
if (err) return next(err); | ||
req.session.csrfSecret = secret; | ||
createToken(secret); | ||
}); | ||
exports.check = function (req, res, next) { | ||
if (ignoreMethods[req.method]) | ||
return next() | ||
// generate the token | ||
function createToken(secret) { | ||
var token; | ||
check(req, res, next, req.headers['x-csrf-token'] | ||
|| (req.body && req.body._csrf) | ||
) | ||
} | ||
// lazy-load token | ||
req.csrfToken = function csrfToken() { | ||
return token || (token = saltedToken(secret)); | ||
}; | ||
exports.checkBody = function (req, res, next) { | ||
if (ignoreMethods[req.method]) | ||
return next() | ||
// ignore these methods | ||
if ('GET' == req.method || 'HEAD' == req.method || 'OPTIONS' == req.method) return next(); | ||
check(req, res, next, req.body && req.body._csrf) | ||
// determine user-submitted value | ||
var val = value(req); | ||
// check | ||
if (!checkToken(val, secret)) { | ||
var err = new Error('invalid csrf token'); | ||
err.status = 403; | ||
next(err); | ||
return; | ||
} | ||
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']); | ||
} | ||
exports.checkHeader = function (req, res, next) { | ||
if (ignoreMethods[req.method]) | ||
return next() | ||
/** | ||
* Return salted token. | ||
* | ||
* @param {String} secret | ||
* @return {String} | ||
* @api private | ||
*/ | ||
check(req, res, next, req.headers['x-csrf-token']) | ||
function saltedToken(secret) { | ||
return createToken(generateSalt(10), secret); | ||
} | ||
function check(req, res, next, value) { | ||
if (!value) | ||
return next(passError(403)) | ||
/** | ||
* Creates a CSRF token from a given salt and secret. | ||
* | ||
* @param {String} salt (should be 10 characters) | ||
* @param {String} secret | ||
* @return {String} | ||
* @api private | ||
*/ | ||
var secret = req.session.secret | ||
if (!secret) | ||
return next(passError(403)) | ||
function createToken(salt, secret) { | ||
return salt + crypto | ||
.createHash('sha1') | ||
.update(salt + secret) | ||
.digest('base64'); | ||
} | ||
var frags = value.split(';') | ||
var salt = frags[0] | ||
var hash = frags[1] | ||
if (!hash || createHash(salt, secret) !== hash) | ||
return next(passError(403)) | ||
/** | ||
* Checks if a given CSRF token matches the given secret. | ||
* | ||
* @param {String} token | ||
* @param {String} secret | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
next() | ||
function checkToken(token, secret) { | ||
if ('string' != typeof token) return false; | ||
return token === createToken(token.slice(0, 10), secret); | ||
} | ||
function createHash(salt, secret) { | ||
return crypto.createHash('sha1') | ||
.update(salt + ';' + secret) | ||
.digest('base64') | ||
/** | ||
* Generates a random salt, using a fast non-blocking PRNG (Math.random()). | ||
* | ||
* @param {Number} length | ||
* @return {String} | ||
* @api private | ||
*/ | ||
function generateSalt(length) { | ||
var i, r = []; | ||
for (i = 0; i < length; ++i) { | ||
r.push(SALTCHARS[Math.floor(Math.random() * SALTCHARS.length)]); | ||
} | ||
return r.join(''); | ||
} | ||
function passError(status) { | ||
var err = new Error() | ||
err.status = status | ||
return err | ||
} | ||
var SALTCHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
{ | ||
"name": "csurf", | ||
"description": "CSRF middleware", | ||
"version": "0.1.2", | ||
"description": "CSRF token middleware", | ||
"version": "1.0.0", | ||
"author": { | ||
@@ -11,14 +11,25 @@ "name": "Jonathan Ong", | ||
}, | ||
"dependencies": { | ||
"uid": "~0.0.2" | ||
}, | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/discore/csurf.git" | ||
"url": "https://github.com/expressjs/csurf.git" | ||
}, | ||
"bugs": { | ||
"mail": "me@jongleberry.com", | ||
"url": "https://github.com/discore/csurf/issues" | ||
"url": "https://github.com/expressjs/csurf/issues" | ||
}, | ||
"dependencies": { | ||
"uid2": "~0.0.2" | ||
}, | ||
"devDependencies": { | ||
"cookie-session": "*", | ||
"body-parser": "*", | ||
"mocha": "^1.17.0", | ||
"should": "^3.0.0", | ||
"supertest": "*", | ||
"connect": "*" | ||
}, | ||
"scripts": { | ||
"test": "make test" | ||
} | ||
} |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
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
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
4380
5
134
0
1
6
1
0
1
+ Addeduid2@~0.0.2
+ Addeduid2@0.0.4(transitive)
- Removeduid@~0.0.2
- Removeduid@0.0.2(transitive)