Closed Yu-Jack closed 3 years ago
res.getHeader("set-cookie") is returning an array, so I use for-loop and Cookie.parse. It works correctly, is there other case I may not consider it?
It looks like due to your usage, this is incorrectly treating the attributes like path
and domain
as case-sensitive. These keys are not case-sensitive and you should never use an API for something it was not designed to do. It may "seem" to work today, but that API will not guarantee it will work in the future, since that is not what it was designed for. Especially to land in such a popular module as this, we should never abuse any APIs and use them to do what they were designed for.
I've tested it when user use singed and unsigned mode, it works.
Can you add a test to demonstrate this? I just tested this and a signed cookie will fail with the provided code. I have marked this as "needs tests" to indicate you need to at least add a test that shows it working with signed cookies.
I added the check about path and domain. About "the browser would send back to you", it's interesting. Because express and server in test case, they act different behavior.
So these checks you implemented are not accurate. Please make sure the mechanism you are matching is not a simple string comparison, but actually matches the cookie specification: https://tools.ietf.org/html/rfc6265
For example if you first declare csurf({ cookie: { path: '/' }})
and then a csurf({ cookie: { path: '/foo' }})
, the second csurf should be using the cookie set by the first, but not vice-versa. Domains are similar, in that a cookie declared for .domain.com
should load for .foo.domain.com
, but not the other way around.
HI @dougwilson , thank you for your comments.
But, I think It's difficult to use the first setting. Because sometime some use cases are allowed use different setting in different path or router.
The restriction should be not allowed duplicated called in same middleware. Below situation A is allowed.
const csrfProtection1 = csrf({ cookie: {signed: false, key: "key1"} })
app.get('/path1', csrfProtection1, function (req, res) {
res.render('send', {token: req.csrfToken()})
})
app.post('/path1', csrfProtection1, function (req, res) {
res.send('data is being processed')
})
const csrfProtection2 = csrf({ cookie: {signed: true, key: "key1"} })
app.get('/path2', csrfProtection2, function (req, res) {
res.render('send', {token: req.csrfToken()})
})
app.post('/path2', csrfProtection2, function (req, res) {
res.send('data is being processed')
})
Below situation B should be not allowed even use same settings.
const csrfProtection1 = csrf({ cookie: {signed: false, key: "key1"} })
app.use(csrfProtection1)
const csrfProtection2 = csrf({ cookie: {signed: true, key: "key1"} })
app.get('/path2', csrfProtection2, function (req, res) {
res.render('send', {token: req.csrfToken()})
})
app.post('/path2', csrfProtection2, function (req, res) {
res.send('data is being processed')
})
So, I'm thinking a new way to prevent it, like this (just an example). It make situation A is allowed, but throw an error in situation B.
return function csrf (req, res, next) {
if (cookie) {
if (req.isCsurflnitialized) {
throw new Error("duplicated used in same middleware")
}
if (!req.isCsurflnitialized) {
req.isCsurflnitialized = true
}
}
// .... other code
}
I'm not sure which one is better for this module.
req
to remain first config Personally, I prefer solution 1. Thank you for helping!
Introduction
Recently, I answered a question in the stackoverflow. The user repeatedly call
csurf
then put it into express middleware. It result in a error message "invalid csrf token" when user make form post at first time.It only happen in cookie mode, not session mode.
Reason
This is a sample code make this happen.
Reproduced Way:
localhost:8080
)http://localhost:8080/settings
We could there are two line called
csurf()
and put it into middleware of express in mark1 and mark2. In mark1, csurf will generate asecret1
andtoken1
to give ejs template. But, in mark2, csurf is re-initialized, secret and token1 is changed. That means csurf will validate next request with newsecret2
andtoken2
.But, it only happen at the first time validation. Subsequently, the request will be validated correctly.
Solution
So, I think I could return secret from set-cookie header if user have called
csurf
in cookie mode. It only happen in cookie mode, not session mode.Discussion
I've tested it, it'll not happen in session mode. Because csurf assign secret into
req
so it could get correct secret in second csurf. But for more protection, I also addif (cookie)
to prevent.res.getHeader("set-cookie")
is returning an array, so I use for-loop andCookie.parse
. It works correctly, is there other case I may not consider it?I've tested it when user use singed and unsigned mode, it works.
I added the check about path and domain.
About "the browser would send back to you", it's interesting. Because express and server in test case, they act different behavior.
This is express will do.
This is server in test case will do
So, it will make two situation.
express will succeed in situation B, but failed in situation A. server in test case will succeed in situation A, but failed situation B So, I wrote these two test cases to test situation A and B.
Thank you!