考虑到跨站攻击,添加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