# all files / keystone/lib/security/ csrf.js

93.88% Statements 46/49
90.32% Branches 28/31
100% Functions 9/9
93.88% Lines 46/49

    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
    791×
    1×
    1×

    1×
    1×
    1×
    1×
    1×
    1×
    1×
    1×

    1×
    10×

    1×
    5×

    1×
    7×

    1×
    5×

    1×
    2×
    2×
    2×

    1×
    6×
    3×
    3×
    1×
    2×

    2×

    2×

    1×
    5×
    3×

    5×

    5×

    1×

    1×
    1×
    1×

    3×
    1×

    2×
    1×

    1×
    1×

     var crypto = require('crypto');
    var scmp = require('scmp');
    var utils = require('keystone-utils');

    exports.TOKEN_KEY = '_csrf';
    exports.LOCAL_KEY = 'csrf_token_key';
    exports.LOCAL_VALUE = 'csrf_token_value';
    exports.SECRET_KEY = exports.TOKEN_KEY + '_secret';
    exports.SECRET_LENGTH = 10;
    exports.CSRF_HEADER_KEY = 'x-csrf-token';
    exports.XSRF_HEADER_KEY = 'x-xsrf-token';
    exports.XSRF_COOKIE_KEY = 'XSRF-TOKEN';

    function tokenize (salt, secret) {
    	return salt + crypto.createHash('sha1').update(salt + secret).digest('base64');
    }

    exports.createSecret = function () {
    	return crypto.pseudoRandomBytes(exports.SECRET_LENGTH).toString('base64');
    };

    exports.getSecret = function (req) {
    	return req.session[exports.SECRET_KEY] || (req.session[exports.SECRET_KEY] = exports.createSecret());
    };

    exports.createToken = function (req) {
    	return tokenize(utils.randomString(exports.SECRET_LENGTH), exports.getSecret(req));
    };

    exports.getToken = function (req, res) {
    	res.locals[exports.LOCAL_VALUE] = res.locals[exports.LOCAL_VALUE] || exports.createToken(req);
    	res.cookie(exports.XSRF_COOKIE_KEY, res.locals[exports.LOCAL_VALUE]);
    	return res.locals[exports.LOCAL_VALUE];
    };

    exports.requestToken = function (req) {
    	if (req.body && req.body[exports.TOKEN_KEY]) {
    		return req.body[exports.TOKEN_KEY];
    	} else if (req.query && req.query[exports.TOKEN_KEY]) {
    		return req.query[exports.TOKEN_KEY];
    	} else Iif (req.headers && req.headers[exports.XSRF_HEADER_KEY]) {
    		return req.headers[exports.XSRF_HEADER_KEY];
    	} else Iif (req.headers && req.headers[exports.CSRF_HEADER_KEY]) {
    		return req.headers[exports.CSRF_HEADER_KEY];
    	}
    	return '';
    };

    exports.validate = function (req, token) {
    	if (arguments.length === 1) {
    		token = exports.requestToken(req);
    	}
    	Iif (typeof token !== 'string') {
    		return false;
    	}
    	return scmp(token, tokenize(token.slice(0, exports.SECRET_LENGTH), req.session[exports.SECRET_KEY]));
    };

    exports.middleware = {
    	init: function (req, res, next) {
    		res.locals[exports.LOCAL_KEY] = exports.LOCAL_VALUE;
    		exports.getToken(req, res);
    		next();
    	},
    	validate: function (req, res, next) {
    		// Bail on safe methods
    		if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') {
    			return next();
    		}
    		// Validate token
    		if (exports.validate(req)) {
    			next();
    		} else {
    			res.statusCode = 403;
    			next(new Error('CSRF token mismatch'));
    		}
    	},
    };