我想在我的app中添加一个使用MEAN堆栈的CSRF保护。
我尝试了某人已经给出的答案:CSRF Protection in ExpressJS
但这是旧款快递版,所以我做了一些改动:
app.use(cookieParser(config.cookieSecret, { httpOnly: true }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({ secret: config.sessionSecret, resave: false, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use(csrf({value: function(req) {
var token = (req.body && req.body._csrf)
|| (req.query && req.query._csrf)
|| (req.headers['x-csrf-token'])
|| (req.headers['x-xsrf-token']);
return token;
}
}));
app.use(function(req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
我可以看到名为XSRF-TOKEN
的令牌在我的应用上生成(使用Chrome检查)。
但是当我发布一个表单(Angular frontend)时,我有一个关于令牌的错误:
{"message":"invalid csrf token","error":{"expose":true,"code":"EBADCSRFTOKEN","statusCode":403,"status":403}}
我错过了什么吗?我想知道req.csrfToken()
是否会生成Angular给出的好令牌......
编辑:
我看到AngularJS在XSRF-TOKEN
次请求仅中使用了$http
。所以我想我必须在我的表单中添加一个隐藏的输入来发布csrf值,由Express检查,但是如何?
答案 0 :(得分:12)
最后我能够发送好的令牌值。这是完整的答案。
AngularJS在使用$http
服务发出请求期间使用CSRF保护(Angular称为XSRF)。
执行 XHR 请求时, $ http 服务会从中读取令牌 cookie(默认情况下为 XSRF-TOKEN )并将其设置为HTTP标头 (的 X-XSRF-TOKEN 强>)。由于只有在您的域上运行的JavaScript才可以 读取cookie,您的服务器可以放心XHR来自 您的域上运行的JavaScript。标题不会设置为 跨域请求。
有很多帖子解释了如何使用 ExpressJS 3.xx 发送XSRF-TOKEN,但有些内容随 4.xx版本而变化。 Connect中间件不再包含在内。 Express使用自己的middelware:cookie-parser,body-parser,express-session和csurf。
第一步是将cookie从后端发送到前端(Express to Angular):
var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var config = require('./lib/config'); //config.js file with hard-coded options.
var csrf = require('csurf');
app.use(cookieParser(config.cookieSecret, { httpOnly: true }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
name: 'sessionID',
secret: config.sessionSecret,
cookie: {
path: '/',
httpOnly: true,
secure: false,
maxAge: 3600000
},
rolling: true,
resave: false,
saveUninitialized: true
}));
app.use(csrf());
app.use(function(req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
现在,Angular能够在 $ http 请求期间设置其HTTP标头(X-XSRF-TOKEN)。示例:
<body ng-app="testApp">
<div ng-controller="LoginCtrl">
<form role="form" ng-submit="login()" >
<input type="email" ng-model="user.email" />
<input type="password" ng-model="user.password" />
<input type="submit" value="Log In" />
</form>
<p style="color:red">{{loginMsg}}</p>
</div>
</body>
<script>
var app = angular.module('testApp', ['ngResource']);
app.controller('LoginCtrl', function ($scope, $http) {
$scope.user = {};
$scope.loginMsg = '';
$scope.login = function () {
$http.post('/login', $scope.user).success(function () {
$window.location.href='/profile';
}).error(function () {
$scope.loginMsg = 'Wrong credentials, try again.';
});
};
});
</script>
这是我的问题,我不知道如何添加此输入。最后,我创建了一个名为$csrf
的新Angular服务:
app.factory('$csrf', function () {
var cookies = document.cookie.split('; ');
for (var i=0; i<cookies.length; i++) {
var cookie = cookies[i].split('=');
if(cookie[0].indexOf('XSRF-TOKEN') > -1) {
return cookie[1];
}
}
return 'none';
});
我在Plunker上做了一个例子:http://plnkr.co/edit/G8oD0dDJQmjWaa6D0x3u
希望我的回答能有所帮助。
答案 1 :(得分:1)
如果有人收到“csurf配置错误”,请尝试:
app.use( csrf( { cookie: true } ));
答案 2 :(得分:0)
我创建了可以直接使用的存储库。
https://github.com/nil4you/node-csrf-and-http-only-cookie-with-spa
https://github.com/nil4you/angular-csrf-and-httponly-cookie-demo
在此处提出任何评论,因为我看不到太多的编码问题,而全都是关于正确的设置问题。