Skip to content
This repository has been archived by the owner on Sep 14, 2022. It is now read-only.

Csurf-expire patch #160

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ following keys:
- `key` - the name of the cookie to use to store the token secret
(defaults to `'_csrf'`).
- `path` - the path of the cookie (defaults to `'/'`).
- `maxAge` - the maximum age the cookie can be before it expires in seconds. Setting this parameter will cause a 403 response if any cookie and csfr token is sent beyond the maxAge window.
- any other [res.cookie](http://expressjs.com/4x/api.html#res.cookie)
option can be set.

Expand Down Expand Up @@ -116,7 +117,7 @@ var bodyParser = require('body-parser')
var express = require('express')

// setup route middlewares
var csrfProtection = csrf({ cookie: true })
var csrfProtection = csrf({ cookie: { maxAge: 60 * 60 * 8 } })
var parseForm = bodyParser.urlencoded({ extended: false })

// create express app
Expand Down Expand Up @@ -220,7 +221,7 @@ 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.use(csrf({ cookie: { maxAge: 60 * 60 * 8 } }))

app.get('/form', function (req, res) {
// pass the csrfToken to the view
Expand Down
40 changes: 38 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ function csurf (options) {
// generate & set new secret
if (sec === undefined) {
sec = tokens.secretSync()
sec = setExpiry(sec, cookie)
setSecret(req, res, sessionKey, sec, cookie)
}

Expand All @@ -104,11 +105,12 @@ function csurf (options) {
// generate & set secret
if (!secret) {
secret = tokens.secretSync()
secret = setExpiry(secret, cookie)
setSecret(req, res, sessionKey, secret, cookie)
}

// verify the incoming token
if (!ignoreMethod[req.method] && !tokens.verify(secret, value(req))) {
if (!ignoreMethod[req.method] && (!tokens.verify(secret, value(req)) || !verifyExpiry(secret, cookie))) {
return next(createError(403, 'invalid csrf token', {
code: 'EBADCSRFTOKEN'
}))
Expand Down Expand Up @@ -232,6 +234,25 @@ function getSecretBag (req, sessionKey, cookie) {
}
}

/**
* Set an expiry time on the cookie.
*
* @param {string} val
* @param {Object} [options]
* @api private
*/

function setExpiry (val, options) {
var secret = val
if (options) {
if (options.maxAge) {
var time = ((new Date().getTime() + (options.maxAge * 1000)).toString(21))
secret += ('-' + time)
}
}
return secret
}

/**
* Set a cookie on the HTTP response.
*
Expand All @@ -244,7 +265,6 @@ function getSecretBag (req, sessionKey, cookie) {

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)
Expand Down Expand Up @@ -289,6 +309,22 @@ function setSecret (req, res, sessionKey, val, cookie) {
throw new Error('misconfigured csrf')
}
}
/**
* Verify the cookie/token has not expired.
* @private
*/
function verifyExpiry (secret, cookie) {
if (cookie) {
if (cookie.maxAge) {
var index = secret.lastIndexOf('-')
if (index === -1) { return false }
var time = secret.substr(index + 1, secret.length - index)
time = parseInt(time, 21)
return (new Date().getTime()) < time
}
}
return true
}

/**
* Verify the configuration against the request.
Expand Down
39 changes: 34 additions & 5 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

process.env.NODE_ENV = 'test'

var assert = require('assert')
Expand Down Expand Up @@ -262,11 +261,11 @@ describe('csurf', function () {

describe('with "ignoreMethods" option', function () {
it('should reject invalid value', function () {
assert.throws(createServer.bind(null, {ignoreMethods: 'tj'}), /option ignoreMethods/)
assert.throws(createServer.bind(null, { ignoreMethods: 'tj' }), /option ignoreMethods/)
})

it('should not check token on given methods', function (done) {
var server = createServer({ignoreMethods: ['GET', 'POST']})
var server = createServer({ ignoreMethods: ['GET', 'POST'] })

request(server)
.get('/')
Expand Down Expand Up @@ -346,7 +345,7 @@ describe('csurf', function () {
})
app.use('/new', function (req, res, next) {
// regenerate session
req.session = {hit: 1}
req.session = { hit: 1 }
next()
})
app.use(function (req, res) {
Expand Down Expand Up @@ -388,6 +387,36 @@ describe('csurf', function () {
.expect(500, /misconfigured csrf/, done)
})
})
describe('when using "maxAge" cookie option', function () {
var server = createServer({ cookie: { maxAge: 3600 } })
it('should work in with valid token', function (done) {
request(server)
.get('/')
.expect(200, function (err, res) {
if (err) return done(err)
var token = res.text
request(server)
.post('/')
.set('Cookie', cookies(res))
.set('csrf-token', token)
.expect(200, done)
})
})
it('should reject expired tokens', function (done) {
var server = createServer({ cookie: { maxAge: -3600 } })
request(server)
.get('/')
.expect(200, function (err, res) {
if (err) return done(err)
var token = res.text
request(server)
.post('/')
.set('Cookie', cookies(res))
.set('csrf-token', token)
.expect(403, done)
})
})
})
})

function cookie (res, name) {
Expand Down Expand Up @@ -415,7 +444,7 @@ function createServer (opts) {
req.query = url.parse(req.url, true).query
next()
})
app.use(bodyParser.urlencoded({extended: false}))
app.use(bodyParser.urlencoded({ extended: false }))
app.use(csurf(opts))

app.use(function (req, res) {
Expand Down