Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

CSRF fails after the server restart #832

Closed
emirotin opened this Issue · 13 comments

3 participants

@emirotin

Using express 3.2.6

Here's my relevant code

csrf_token = (req, res, next) ->
    res.locals.csrf_token = req.session._csrf
    next()

app
    .use(express.logger())
    .use(express.compress())
    .use(express.timeout(30000))
    .use(express.bodyParser())
    .use(catchErrors)
    .use(express.methodOverride())
    .use(express.cookieParser('secret'))
    .use(express.session())
    .use('/v1', express.csrf())
    .use(csrf_token)
    .use(express['static']("#{config.ROOT}/public"))
    .use(app.router)

Then in my view I have

    div(style="display: none")
        input#csrf-token(type="hidden", value="#{csrf_token}")

And finally I pick this value before every AJAX request.

Appears that after the server restart upon opening the page for the 1st time csrf_token is undefined that makes all my requests fail until I reload the page.

@jonathanong

if the route is anything but /v1 then req.session._csrf is going to be undefined

@emirotin

@jonathanong obviously, but the route is /v1/service/inv-view and as I explained the issue only happens the first time I load the page and doesn't happen after reload

@jonathanong

'/v1' !== '/v1/service/inv-view'. you want .use('/v1*', express.csrf())

@emirotin

I don't think you're right.
http://expressjs.com/api.html#app.use

Say for example you wanted to prefix all static files with "/static", you could use the "mounting" feature to support this. Mounted middleware functions are not invoked unless the req.url contains this prefix, at which point it is stripped when the function is invoked. This affects this function only, subsequent middleware will see req.url with "/static" included unless they are mounted as well.

I also have different middleware mounted to prefixes in the same way and they work OK

@jonathanong

errr you're right. was thinking about routing

@jonathanong

what happens if you just .use(express.csrf())?

@emirotin

The same
(and I need mounting cause I also get POSTs from Facebook who cannot pass the token)

@tj
Owner
tj commented

you're using the in-memory session store, you'll want to pass something like connect-redis to .session({ store: .. })

@tj tj closed this
@emirotin

But why isn't in-memory store working?
I didin't find in docs that I must use some persistent storage

@tj
Owner
tj commented

it does work, but when the process goes down all the sessions go with it, the memory store is only really useful for tiny "toy" sites and development

@emirotin

But it doesn't actually

I restart the server
I request the page
I expect the server to generate the token
() I put this token to res.locals and out put it to the page
Then I pick it from the page and put inside of the AJAX request payload
And I see that when the page loads for the first time the token from (
) is undefined

It is obvious that in-memory store is erased when the server restarts, of course. But I don't complain about my old session lost.
I tell you that the new token does not reach the page when it loads for the first time.

@tj
Owner
tj commented

oh ok I see, hmm, can you reproduce with a small stand-alone app I can check out? or better yet a test for this repo

@emirotin

OK, I have it reproducible https://www.dropbox.com/s/n6vq4xjpjznfhgx/token-error.zip
And I assume I also have an explanation

Look:
I only want CSRF to be checked on POSTS to specific prefix (like '/api'), so I mount it to that prefix.
Which means the middleware itself isn't called for other requests

Now what happens:
GET '/' -> middleware not called, session._csrf not set, page renders with undefined inside of input
pick _csrf from the input (undefined)
POST '/api/time' -> middleware is called, it sets session._csrf to a newly generated value, then picks _csrf == undefined from my post body and fails

Now when I reload the page the token is already in session._csrf and thus reaches the page

Solution? Creating 2 middleware (initCsrf -> mount for every request, checkCsrf -> mount for particular point).
See https://www.dropbox.com/s/tmlamdb5w51cpac/token-error2.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.