GithubHelp home page GithubHelp logo

csrf's Introduction

koa-csrf

build status build status code style styled with prettier made with lass license

CSRF tokens for Koa

NOTE: As of v5.0.0+ ctx.csrf, ctx_csrf, and ctx.response.csrf are removed – instead use ctx.state._csrf. Furthermore we have dropped invalidTokenMessage and invalidTokenStatusCode in favor of an errorHandler function option.

Table of Contents

Install

npm:

npm install koa-csrf

Usage

  1. Add middleware in Koa app (see options below):

    const Koa = require('koa');
    const bodyParser = require('koa-bodyparser');
    const session = require('koa-generic-session');
    const convert = require('koa-convert');
    const CSRF = require('koa-csrf');
    
    const app = new Koa();
    
    // set the session keys
    app.keys = [ 'a', 'b' ];
    
    // add session support
    app.use(convert(session()));
    
    // add body parsing
    app.use(bodyParser());
    
    // add the CSRF middleware
    app.use(new CSRF());
    
    // your middleware here (e.g. parse a form submit)
    app.use((ctx, next) => {
      if (![ 'GET', 'POST' ].includes(ctx.method))
        return next();
      if (ctx.method === 'GET') {
        ctx.body = ctx.state._csrf;
        return;
      }
      ctx.body = 'OK';
    });
    
    app.listen();
  2. Add the CSRF token in your template forms:

    Jade Template:

    form(action='/register', method='POST')
      input(type='hidden', name='_csrf', value=_csrf)
      input(type='email', name='email', placeholder='Email')
      input(type='password', name='password', placeholder='Password')
      button(type='submit') Register

    EJS Template:

    <form action="/register" method="POST">
      <input type="hidden" name="_csrf" value="<%= _csrf %>" />
      <input type="email" name="email" placeholder="Email" />
      <input type="password" name="password" placeholder="Password" />
      <button type="submit">Register</button>
    </form>

Options

  • errorHandler (Function) - defaults to a function that returns ctx.throw(403, 'Invalid CSRF token')
  • excludedMethods (Array) - defaults to [ 'GET', 'HEAD', 'OPTIONS' ]
  • disableQuery (Boolean) - defaults to false
  • ignoredPathGlobs (Array) - defaults to an empty Array, but you can pass an Array of glob paths to ignore

Contributors

Name Website
Nick Baugh https://github.com/niftylettuce
Imed Jaberi https://www.3imed-jaberi.com/

License

MIT © Jonathan Ong

csrf's People

Contributors

3imed-jaberi avatar asplayer9119 avatar cesarandreu avatar dead-horse avatar dependabot[bot] avatar evert avatar hung-phan avatar jonathanong avatar karthikv avatar mikepb avatar niftylettuce avatar popomore avatar stephenmathieson avatar titanism avatar venables avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

csrf's Issues

delegates

this.response.csrf
this.request.assertCSRF(body)

tests for `csrf.middleware`

i can't seem to get it working, but app.use(require('koa-csrf')()) works fine. since csrf.middleware is the suggested way of using this thing, i'm surprised there are no tests for it

Upgrading to Koa2.0

Hi, I was just wondering if there was any plan to upgrade to Koa2.0
Thanks.

unnecessary try catch in middleware

i actually looked at the middleware now, and this entire catch block is unnecessary:

try {
    // bodyparser middlewares maybe store body in request.body
    // or you can just set csrf token header
    this.assertCSRF(this.request.body)
  } catch (err) {
    if (err.status === 403) {
      this.status = 403
      this.body = 'invalid csrf token'
    } else {
      this.throw(err)
    }
    return
  }

an upstream error handler should handle the rendering of the error

tests

that's right, i haven't even tried this.

Seems like `context.csrf` is being set without me doing anything

I am debugging my project and have a breakpoint set at this line: https://github.com/koajs/csrf/blob/master/src/index.js#L56

Now when I evaluate (ctx.csrf), I get a value back. However I am not sending a CSRF token anywhere in my application..all I've done is set Koa to use the CSRF middleare..I haven't embedded _csrf in a hidden form field anywhere, nor have I appended a csrf token to any of my requests. So my question is, how is ctx.csrf already set?

change .csrf from a getter to a yieldable

unlike express, we create the secret only when we need to to avoid setting cookies and updating sessions unnecessarily. however, this means with a getter, we do so synchronously.

how about changing the api to:

var csrf = yield this.csrf()

and optionally get it synchronously:

var csrf = this.csrfToken
// or without a getter
var csrf = this.csrfSync()

another breaking change, but i think it's for the better. unless we make the dev set the secret themselves, which would be kind of annoying.

Add csrf token in ctx.state

Screenshot 2019-12-25 at 16 25 30

ctx.state is a recommended namespace for passing information through middleware and to your frontend views.

why change tokens on every request

May I know the advantages of keep changing tokens?

As far as I know, only one csrf secret is created with each session, therefore, all tokens will be valid within same session (elder one wont be expired as the result of creating new one).

Can I simply create one token while creating the session and always return the same token for all incoming requests?

ctx.session.csrf = ctx.csrf;
// then always return ctx.session.csrf

Cannot read property '_csrf' of null

I am using an Angular JS on the client side, which sends a form data to the server. The form is trivial: username and password. AngularJS has a default action: looks for a cookie 'XSRF-TOKEN' with a token value set by server, modifies the request headers by adding 'X-XSRF-TOKEN' to the request headers and sends it to server using it's $http module.

The problem arises, when an "empty" request is made - when a user clicks login button without entering any data to fields. In that case this.request.body is null.

I now do a form validation on a client side to disable the sending of an empty body, as well as an early validation on a server side to check the presence of this.request.body after parsing the request. But it feels kind of patchy and might break future requests, where body is actually not required.

Use of koa-session instead koa-generic-session

Hi, I would like to ask, what if I use koa-session instead of koa-generic-session. Will it compatible with koa-csrf? since koa-generic-session seems to be deprecated.

Thanks in advance.

Bayu.

Default token signature weaknesses,

I appreciate that you've made the tokenizer and validator extensible. However, secret keys really shouldn't be used with a straight hash algorithm.

var mac = crytpo.createHmac('sha256', secret).update(salt).digest('base64')

SHA1 is also pretty insecure at this point in time. Is there a reason for not using SHA-256?

For HMAC, It's important that the secret length matches the hash function. For SHA1 it's 20 bytes (160 bits), SHA256 is 32 bytes (256 bits), etc. The HMAC RFC (https://tools.ietf.org/html/rfc2104) provides guidance on key lengths.

If possible, addressing these would be a responsible thing to do to make the default more secure.

Importing koa-csrf in node 6

To be able to import CSRF in node 6.x we have to write like this:

const CSRF = require('koa-csrf').default;

Is this the right way? Also, the README.md example doesn't import CSRF at all(although it is using es6 modules).

Use with koa-router

I'm not sure how koa-csrf intercepts HTTP requests - does it matter if I set my koa-router middleware before or after setting koa-csrf?

This is what I'm using now:

server.use(new CSRF({
    invalidSessionSecretMessage: 'Invalid session secret',
    invalidSessionSecretStatusCode: 403,
    invalidTokenMessage: 'Invalid CSRF token',
    invalidTokenStatusCode: 403,
    excludedMethods: ['GET', 'HEAD', 'OPTIONS'],
    disableQuery: false
}));

server.use(router.routes());

I always get "Invalid CSRF token" following the example

I followed the example, and every time I make a POST request sending the csrf token like in the example, I always get this message: 'Invalid CSRF Token'

This is my code:

// app.js
app
    .use(session())
    .use(new CSRF({
      invalidSessionSecretMessage: 'Invalid session secret',
      invalidSessionSecretStatusCode: INVALIDSTATUSCODE,
      invalidTokenMessage: 'Invalid CSRF token',
      invalidTokenStatusCode: INVALIDSTATUSCODE,
      excludedMethods: [ 'GET', 'HEAD', 'OPTIONS' ],
      disableQuery: false
    }))
   // ... all other configurations...

// routes.js
route.get('/login', ctx => {
    console.log(ctx.csrf)
    ctx.state = { title : 'Login', login : true, csrf: ctx.csrf }
    ctx.render('login')
})

// login.pug
  form(submit='/auth/login' method='post')
    label Username
      input(name='username' type='text' placeholder='JohnDoe2' required)
    label Password
      input(name='password' type='password' placeholder='********' required)
    if login
      a(href='/auth/signup') Or register here
    input(name='_csrf' value=csrf hidden)
    input(type='submit' value='Send')

Am I missing something important?

Why assertCsrf method given a body value?

Currently, assertCsrf method has the body argument.

But I thought it look like that assertCsrf method presuppose request.body.

I seem that some developer have to understand those implements.

So, the body argument of assertCsrf method should have like default value which is this.request.body.

What do you think?

Exclude route

A post route handler is called by Azure AD as a result of authentication POST response.
Is it possible to exclude CSRF check for such route?

Enable csrf per route example

Currently i am mounting csrf middleware per route like the example below,

routes.js

const csrf = new CSRF({
  invalidSessionSecretMessage: 'Invalid session secret',
  invalidTokenMessage: 'Invalid CSRF token',
  invalidTokenStatusCode: 403,
});

const router = new Router();
route.get('/', csrf, Controller.getPage);

Is this the correct way of doing it or is there a better way? I can't find any example to validate it as the README.md only shows example of applying csrf to all routes.

Question: omitted options do not set defaults in this.tokens

If opts is omitted, this.tokens = csrf(opts) is created without the default values .. all the default values went into this.opts .. So either that is a bug or the code could probably be clearer commenting why this is the case and perhaps moving this.tokens = csrf(opts); closer to the top.

I think the fix is to change:
this.tokens = csrf(opts);
to
this.tokens = csrf(this.opts);

  constructor(opts) {

    this.opts = opts || {};

    if (!this.opts.invalidSessionSecretMessage)
      this.opts.invalidSessionSecretMessage = 'Invalid session secret';

    if (!this.opts.invalidSessionSecretStatusCode)
      this.opts.invalidSessionSecretStatusCode = 403;

    if (!this.opts.invalidTokenMessage)
      this.opts.invalidTokenMessage = 'Invalid CSRF token';

    if (!this.opts.invalidTokenStatusCode)
      this.opts.invalidTokenStatusCode = 403;

    if (!this.opts.excludedMethods)
      this.opts.excludedMethods = [ 'GET', 'HEAD', 'OPTIONS' ];

    if (typeof this.opts.disableQuery !== 'boolean')
      this.opts.disableQuery = false;

    this.tokens = csrf(opts);

    return this.middleware;

}

https://github.com/koajs/csrf/blob/master/src/index.js#L13

tests randomly fail

fresh clone/fork and:

~/dev/src/github.com/stephenmathieson/koa-csrf on master ∴ npm install
npm WARN deprecated [email protected]: Please use istanbul@>=0.4.0 instead
npm WARN deprecated [email protected]: Jade has been renamed to pug, please install the latest version of pug instead of jade
[email protected] node_modules/should
├── [email protected]
├── [email protected]
└── [email protected]

[email protected] node_modules/koa-session
├── [email protected]
└── [email protected] ([email protected])

[email protected] node_modules/csrf
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected] ([email protected])

[email protected] node_modules/co-body
├── [email protected]
├── [email protected]
├── [email protected] ([email protected], [email protected], [email protected])
└── [email protected] ([email protected], [email protected])

[email protected] node_modules/koa
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected] ([email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected])
└── [email protected] ([email protected])

[email protected] node_modules/mocha
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected] ([email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected])
└── [email protected] ([email protected], [email protected])

[email protected] node_modules/supertest
├── [email protected]
└── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected])

[email protected] node_modules/istanbul-harmony
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected] ([email protected])
├── [email protected]
├── [email protected] ([email protected])
├── [email protected]
├── [email protected] ([email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected])
└── [email protected] ([email protected], [email protected], [email protected])

~/dev/src/github.com/stephenmathieson/koa-csrf on master ∴ npm test

> [email protected] test /Users/stephenmathieson/dev/src/github.com/stephenmathieson/koa-csrf
> NODE_ENV=test mocha --harmony --reporter spec --require should



  CSRF Token
    should create
      ✓ a token
      ✓ a single token per request
      ✓ a new token per request
    should assert
      ✓ when no token is supplied
      ✓ when no secret is supplied
      ✓ when invalid csrf token
    should not assert when the token is supplied via
      ✓ json body
      ✓ querystring
      ✓ x-csrf-token
      ✓ x-xsrf-token
    .assertCSRF()
      ✓ should support a string value

  CSRF Token Middleware
    should create
      ✓ a token
      ✓ a single token per request
      1) a new token per request
      ✓ a null token when session is invalid
    should assert
      ✓ when no token is supplied
    should not assert when the token is supplied via
      ✓ json body
      ✓ querystring
      ✓ querystring with body
      ✓ x-csrf-token
      ✓ x-xsrf-token


  20 passing (111ms)
  1 failing

  1) CSRF Token Middleware should create a new token per request:
     Error: expected body 'fjn6oL6A--KQnQ2JliJVRlVMRL8atRinZV0M' to match /^\w+-[\w+\/]+/
      at error (node_modules/supertest/lib/test.js:265:13)
      at Test._assertBody (node_modules/supertest/lib/test.js:188:18)
      at Test._assertFunction (node_modules/supertest/lib/test.js:247:11)
      at Test.assert (node_modules/supertest/lib/test.js:148:18)
      at assert (node_modules/supertest/lib/test.js:127:12)
      at node_modules/supertest/lib/test.js:124:5
      at Test.Request.callback (node_modules/supertest/node_modules/superagent/lib/node/index.js:691:12)
      at IncomingMessage.<anonymous> (node_modules/supertest/node_modules/superagent/lib/node/index.js:922:12)
      at endReadableNT (_stream_readable.js:905:12)



npm ERR! Test failed.  See above for more details.

maybe we shouldnt use a regex?

why deprecate csrf@2

[email protected] deprecate: If you are using Koa v3.x, please make sure you are on at least 3.0.3 due to CSRF session reset issue (prevents XHR from working properly for example)

is this a mistake?

CSRF is not a constructor

I cloned this repo: https://github.com/mapmeld/koa-passport-example and getting this crucial error:

/usr/src/app/server.js:36
app.use(new CSRF({
        ^

TypeError: CSRF is not a constructor
    at Object.<anonymous> (/usr/src/app/server.js:36:9)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:427:7)
    at startup (bootstrap_node.js:151:9)
    at bootstrap_node.js:542:3
[nodemon] app crashed - waiting for file changes before starting...

I am running inside docker containers using docker-compose and the mac client.

I am not sure how to solve.

Add option for verifying csrf token in certain types request only

Hi

How about limit the csrf verification to application/x-www-form-urlencoded, multipart/form-data, and text/plain type requests only ? As the plain html form can only send these 3 types of requests (without XMLHttpRequest / fetch, which has same origin policy), there will be no CSRF risks in most cases if I implement a 'application/json' type rest style API

option to disable ?_csrf= support

just had our security audit, and it was recommended we don't use query strings for passing the token around:

[...] but in your case I think it's suboptimal. If a user is on a page with a CSRF token in the URL and gets redirected to an external site, the CSRF token will be passed in the referrer header. Because those tokens do not expire after a single use, it means that the site which is recieving the redirect could use it to CSRF that user. Additionally, those tokens might appear in server logs or elsewhere, so it's better to keep them in the body of the request [...]

this said, it'd be nice to just disable the functionality here (rather than having to worry about it upstream). would you be opposed to a pr adding an option to disable query-string support?

risks on ignoring `GET/HEAD` request?

Think of two situations:

  1. if there are some bad designs in someone's code and GET/HEAD methods are used to do write operations in backend, then risks rise.
  2. even if GET/HEAD are used to query data, attackers can easily get data which may be sensitive via csrf.

example is broken

~/dev/src/github.com/koajs/csrf on master ∴ node example
/Users/stephenmathieson/dev/src/github.com/koajs/csrf/node_modules/koa-session/index.js:45
    throw new TypeError('app instance required: `session(opts, app)`');
    ^

TypeError: app instance required: `session(opts, app)`
    at module.exports (/Users/stephenmathieson/dev/src/github.com/koajs/csrf/node_modules/koa-session/index.js:45:11)
    at Object.<anonymous> (/Users/stephenmathieson/dev/src/github.com/koajs/csrf/example.js:7:9)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:313:12)
    at Function.Module.runMain (module.js:467:10)
    at startup (node.js:136:18)
    at node.js:963:3

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.