Skip to content

Commit

Permalink
merge from lusca master
Browse files Browse the repository at this point in the history
  • Loading branch information
fengmk2 committed Feb 10, 2015
2 parents d756a14 + f37271a commit 459ab5e
Show file tree
Hide file tree
Showing 12 changed files with 296 additions and 80 deletions.
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,16 @@ Fork from [lusca](https://github.com/krakenjs/lusca), [krakenjs/lusca#26](https:

```js
var express = require('express'),
app = express(),
lusca = require('lusca');
app = express(),
session = require('express-session'),
lusca = require('lusca');

//this or other session management will be required
app.use(session({
secret: 'abc',
resave: true,
saveUninitialized: true
}));

app.use(lusca({
csrf: true,
Expand All @@ -53,10 +61,12 @@ app.use(lusca.csrf());
app.use(lusca.csp({ /* ... */}));
app.use(lusca.xframe('SAMEORIGIN'));
app.use(lusca.p3p('ABCDEF'));
app.use(lusca.hsts({ maxAge: 31536000 });
app.use(lusca.xssProtection(true);
app.use(lusca.hsts({ maxAge: 31536000 }));
app.use(lusca.xssProtection(true));
```

__Please note that you must use [express-session](https://github.com/expressjs/session), [cookie-session](https://github.com/expressjs/cookie-session), their express 3.x alternatives, or other session object management in order to use lusca.__

### For koa

```js
Expand Down Expand Up @@ -87,13 +97,13 @@ app.use(lusca.hsts({ koa: true, maxAge: 31536000 });
app.use(lusca.xssProtection({ koa: true });
```
## API
### lusca.csrf(options)
* `key` String - Optional. The name of the CSRF token added to the model. Defaults to `_csrf`.
* `secret` String - Optional. The key to place on the session object which maps to the server side token. Defaults to `_csrfSecret`.
* `impl` Function - Optional. Custom implementation to generate a token.
Enables [Cross Site Request Forgery](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_\(CSRF\)) (CSRF) headers.
Expand All @@ -109,7 +119,19 @@ If enabled, the CSRF token must be in the payload when modifying data or you wil
Enables [Content Security Policy](https://www.owasp.org/index.php/Content_Security_Policy) (CSP) headers.
#### Example Options
```js
// Everything but images can only come from own domain (excluding subdomains)
{
policy: {
'default-src': '\'self\'',
'img-src': '*'
}
}
```
See the [MDN CSP usage](https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Using_Content_Security_Policy) page for more information on available policy options.
### lusca.xframe(value)
Expand Down
20 changes: 13 additions & 7 deletions lib/csrf.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ var token = require('./token');
* @param {Object} options
* key {String} The name of the CSRF token in the model. Default "_csrf".
* impl {Object} An object with create/validate methods for custom tokens. Optional.
* header {String} The name of the response header containing the CSRF token. Default "x-csrf-token".
*/
module.exports = function (options) {
var impl, key;
var impl, key, header, secret;

options = options || {};

key = options.key || '_csrf';
impl = options.impl || token;
header = options.header || 'x-csrf-token';
secret = options.secret || '_csrfSecret';

if (options.koa) {
// koa style middleware
Expand Down Expand Up @@ -50,22 +53,25 @@ module.exports = function (options) {

// express style middleware
return function csrf(req, res, next) {
var method, token;
var method, validate, _impl, _token;

//call impl
_impl = impl.create(req, secret);
validate = impl.validate || _impl.validate;
_token = _impl.token || _impl;
// Set the token
res.locals[key] = impl.create(req);

res.locals[key] = _token;
// Move along for safe verbs
method = req.method;

if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') {
return next();
}

// Validate token
token = req.body[key];
_token = req.body[key] || req.headers[header];

if (impl.validate(req, token)) {
if (validate(req, _token)) {
next();
} else {
res.statusCode = 403;
Expand Down
40 changes: 19 additions & 21 deletions lib/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,34 @@


var crypto = require('crypto');

var SECRET = '_csrfSecret';
var LENGTH = 10;



function create(req) {
var session = req.session,
secret = session[SECRET];
function create(req, secretKey) {

var session = req.session;
if (session === undefined) {
throw new Error('lusca requires req.session to be available in order to maintain state');
}
var secret = session[secretKey];
// Save the secret for validation
if (!secret) {
session[SECRET] = crypto.pseudoRandomBytes(LENGTH).toString('base64');
secret = session[SECRET];
session[secretKey] = crypto.pseudoRandomBytes(LENGTH).toString('base64');
secret = session[secretKey];
}

return tokenize(salt(LENGTH), secret);
}
return {
token: tokenize(salt(LENGTH), secret),
validate: function validate(req, token) {
if (typeof token !== 'string') {
return false;

}
return token === tokenize(token.slice(0, LENGTH), req.session[secretKey]);
}
};
}

function tokenize(salt, secret) {
return salt + crypto.createHash('sha1').update(salt + secret).digest('base64');
Expand All @@ -39,18 +48,7 @@ function salt(len) {
}


function validate(req, token) {
if (typeof token !== 'string') {
return false;

}
return token === tokenize(token.slice(0, LENGTH), req.session[SECRET]);
}




module.exports = {
create: create,
validate: validate
create: create
};
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@
"koa-middlewares": "~1.2.0",
"mocha": "*",
"pedding": "~1.0.0",
"supertest": "~0.13.0"
"supertest": "~0.13.0",
"body-parser": "^1.6.3",
"cookie-parser": "^1.3.2",
"cookie-session": "^1.0.2",
"data-driven": "^1.0.0",
"errorhandler": "^1.1.1",
"express-session": "^1.7.5"
}
}
4 changes: 2 additions & 2 deletions test/csp.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('CSP', function () {
app = mock({ csp: config });

app.get('/', function (req, res) {
res.send(200);
res.status(200).end();
});

request(app)
Expand All @@ -35,7 +35,7 @@ describe('CSP', function () {
app = mock({ csp: config });

app.get('/', function (req, res) {
res.send(200);
res.status(200).end();
});

request(app)
Expand Down

0 comments on commit 459ab5e

Please sign in to comment.