3,079   ExpressJS MongoDB NodeJS

考虑到跨站攻击,添加csrfToken验证

Express框架提供了csurf,GitHub地址:https://github.com/expressjs/csurf

原理大概是:
1,生成互相关联的secret和token;
2,将token传到前端页面,下回请求时带上token,服务端会根据secret加以验证;
3,secret可以保存到cookie中即前端,或者session即后端,默认后者

我采取的方式是将secret保存到session中,而session保存在数据库中

1,在app.js初始化csurf


var config = require("./config");
var helps = require("./common/helps");


var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');



var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

// 加载csurf
var csrf = require('csurf');

var routes = require('./routes/index');
var users = require('./routes/users');
var topic = require('./routes/topic');
var upload = require('./routes/upload');


var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/upload',express.static(path.join(__dirname, 'upload')));
global.appRoot = path.resolve(__dirname);

const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
app.use(session({
    secret: config.session.cookie.secret,
    name: config.session.cookie.name,
    cookie: {
        maxAge:config.session.cookie.maxAge
    },
    resave: false,
    saveUninitialized: true,
        store: new MongoStore({ url:config.session.database.address })
}));

// 在session设置之后才初始化csrf,因为secret需要保存到session中
var csrfProtection = csrf();
app.use(csrfProtection);

// 自定义token验证失败时的返回
app.use(function (err, req, res, next) {
    if (err.code !== 'EBADCSRFTOKEN') return next(err)

    // handle CSRF token errors here
    var name = 'submit';
    var notice = 'Invalid csrf token , please refresh';
    helps.resJsonError(req,res,name,notice);
})

2,获取token

function getCsrfToken(req,res,next){
        // csrf 的 secret 保存在session.csrfSecret 
        // 获取token时发现secret为undefined,会重新生成secret和token,保证每次请求的secret和token都不一样
	req.session.csrfSecret = undefined;
    res.locals.csrfToken = req.csrfToken();
	return next();
}

3,前端获取token,并在请求时返回
html

//layout.jade
doctype html
html
	head
		meta(http-equiv="content-type" content="text/html; charset=UTF-8")
		meta(name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1")
		if csrfToken
			meta(name="csrfToken" content="#{csrfToken}")

js

// 在每个ajaxsend发送之前,添加上token
	var csrfToken = $("meta[name='csrfToken']").attr('content');
	var setCSRFToken = function (csrfToken) {
	  $(document).ajaxSend(function (event,xhr,options) {
	  	var type = options.type.toUpperCase();
	    if (type == 'POST') {
	      	if(xhr.setRequestHeader){
	      		xhr.setRequestHeader('csrf-token', csrfToken);
	      	}else{
	      		options.data['_csrf'] = csrfToken;
	      		
	      		if(options.url.indexOf("?") == -1){
	      			options.url = options.url+"?_csrf="+csrfToken;
	      		}else{
	      			options.url = options.url+"&_csrf="+csrfToken;
	      		}
	      	}
	    }
	  });
	};

	setCSRFToken(csrfToken);




Leave a Reply

Your email address will not be published. Required fields are marked *