all files / esecurity/lib/middleware/ xsrf.js

100% Statements 46/46
98.11% Branches 52/53
100% Functions 7/7
100% Lines 46/46
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110          28× 28× 28× 28× 28× 28× 28×   28× 14×   14×     28× 28×   28×     44×     36×   36×     34× 12×                           52× 14×     38×   32× 14×     18×           34×   23× 12×     23×       34×         12×   10×     10×         20× 16× 16×              
 
var 
    utils = require('../utils'),    
    crypto = require('crypto');
 
module.exports = function XsrfConstructor(opts) {
 
    opts = opts || {};
    opts.skip = opts.skip || function (req, res) {};
    opts.angular = opts.angular || false;
    opts.cookieName = opts.cookieName || '';
    opts.cookie = opts.cookie || {};
    opts.cookie.path = opts.cookie.path || '/';
    opts.log = opts.log || false;
    
    if (opts.angular === true) {
        opts.cookieName = 'XSRF-TOKEN';
        // Angular needs to read the cookie
        opts.cookie.httpOnly = false;
    }
 
    var isCookieEnabled = ('' !== opts.cookieName);
    var isLogEnable = ('function' === typeof opts.log);
    
    return function Xsrf(req, res, next) {
        
        // self-awareness
        if (req._esecurity_angularxsrf) {
            return next();
        }
 
        req._esecurity_angularxsrf = true;
        
        if ('function' == typeof opts.skip && opts.skip(req, res)) {
            return next();
        }
        
        var generateToken = function () {
                return crypto.createHash('sha1').update(
                            crypto.randomBytes(35)
                        ).digest('hex');
            },
            addXsrfCookie = function () {
 
                res.cookie(
                    opts.cookieName,
                    req.xsrfToken(),
                    opts.cookie
                );
 
            },
            getXsrfToken = function (type) {
 
                if ('session' === type) {
                    return req.session && req.session._esecurity_xsrf;
                }
 
                if (req.get('x-xsrf-token')) {
                    return req.get('x-xsrf-token');
                }
                else if ('POST' === req.method) {
                    return req.body['xsrf-token'] || req.body['XSRF-TOKEN'];
                }
                else {
                    return req.query['xsrf-token'] || req.query['XSRF-TOKEN'];
                }
 
            };
        
        // get token
        req.xsrfToken = function () {
 
            if (!req.session._esecurity_xsrf) {
                req.session._esecurity_xsrf = generateToken();
            }
            
            return req.session._esecurity_xsrf;
 
        };
        
        switch (req.method) {
            case 'OPTIONS':
            case 'HEAD':
                break;
            case 'GET':
 
                if (!getXsrfToken() || !getXsrfToken('session')) {
                    
                    if (isCookieEnabled) {
                        addXsrfCookie();
                    }
                    
                    return next();
                }
 
                break;
            default:
                
                if (!getXsrfToken() || !getXsrfToken('session') || getXsrfToken() !== getXsrfToken('session')) {
                    isLogEnable && opts.log('[' + req.ip + '] - 403 - XSRF token does not match.', req);
                    return next(utils.error(403, 'XSRF token does not match.'));
                }
                
                break;
        }
        
        return next();
    };
};