在app.js
之后,bodyParser
文件服务器端有以下内容
let dotEnv = require("dotenv");
dotEnv.load();
let express = require("express");
let app = express();
if (process.env.NODE_ENV === 'production') {
app = require('./public/web/server').app;
}
let passport = require("passport");
let server = require("http").Server(app);
let io = require("socket.io").listen(server);
// load intial configuration
require("./startup/initial-configuration")(app, io);
require("./server/config/socket")(io);
require("./server/config/database")(app, process.env.NODE_ENV);
require("./server/authentication/passport.local")(passport);
require("./server/authentication/passport.impersonate");
require("./startup/initial-routes")(app);
if (process.env.NODE_ENV === 'production') {
app.get('*.*', express.static('./public/web/browser', {
maxAge: '1y'
}));
app.get('*', (req, res) => {
res.render('index', {
req,
res
}, (err, html) => {
if (html) {
res.send(html);
} else {
// console.error(err);
res.send(err);
}
});
});
}
require("./server/middleware/custom-middleware")(app);
module.exports = { app: app, server: server };
如您所见,我在initial-configuration
内加载了一个文件app.js
,该文件的内容为:
const path = require("path");
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
const csurf = require("csurf");
const helmet = require("helmet");
const compression = require("compression");
const useragent = require("express-useragent");
const cors = require("cors");
const passport = require("passport");
const express = require("express");
const cookieMiddleware = require("../server/middleware/cookie-middleware");
const checkCSRFMiddleware = require("../server/middleware/checkCSRF-middleware");
const notificationModel = require("../server/model/notification/notification.model");
const logger = require("./logger");
const morgan = require("morgan");
module.exports = (app, io) => {
app.set("case sensetive routing", true);
if (process.env.NODE_ENV === "production") {
app.enable("trust proxy");
}
app.use((req, res, next) => {
res.io = io;
res.header(
"Access-Control-Allow-Headers",
"X-CSRF-Token, Content-Type"
);
notificationModel.setIO(io);
next();
});
let corsOption = {
origin: true,
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
credentials: true,
exposedHeaders: ["x-auth-token"]
};
app.use(cors(corsOption));
// app.use(logger('dev'));
app.use(helmet());
app.use(useragent.express());
app.use(compression());
app.use(bodyParser.json());
app.use(
bodyParser.urlencoded({
extended: false
})
);
app.use(cookieParser());
app.use(cookieMiddleware);
app.use(passport.initialize());
app.use(require('csurf')({cookie: true}))
// error handler
app.use(function (err, req, res, next) {
if (err.code !== 'EBADCSRFTOKEN') return next(err)
// handle CSRF token errors here
res.status(403)
res.send('session has expired or form tampered with')
})
app.use(function (req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken())
next()
})
// app.use(express.static(path.join(__dirname, "../public/web/browser")));
app.use(
morgan("combined", {
stream: logger.stream
})
);
};
在Angular中,我仅在app.module中导入了以下行
HttpClientXsrfModule.withOptions({
cookieName: "XSRF-TOKEN",
headerName: "X-CSRF-TOKEN"
}),
我所有的请求标头都有
Cookie:_csrf=TmghRq3eWC-PxQfp6pvuHw07; XSRF-TOKEN=vMPrZZtA--BgtY1YVqDRXmi5A6RSbMNb61JA
但是我所有的发帖请求都失败,并说code: "EBADCSRFTOKEN"
。
我应该在有角度的地方做些什么吗?我应该在表格数据后追加吗?
对于任何帮助,谢谢。
答案 0 :(得分:3)
在您的代码中,您正在使用许多模块。为了找出问题所在,我建议将您的代码减少到最小版本,以删除启用csrf并非必需的所有内容。
这只是一个建议,但是在我的Angular 7应用程序中,我添加了这个(不需要更改cookiename和令牌,因为它们在Angular的源代码中具有默认值):
HttpClientModule,
HttpClientXsrfModule.withOptions()
然后在具有Express 4的服务器(主文件app.js)中,我添加了此代码(按此确切顺序):
const csrf = require('csurf');
app.use(bodyParser.urlencoded({
extended: false
}));
// then add cookie parser and csrf config
app.use(cookieParser());
app.use(csrf({
cookie: {
// here you can configure your cookie. Default values are ok, but I decided to be more explicit
// http://expressjs.com/en/4x/api.html#req.cookies
key: '_csrf',
path: '/',
httpOnly: false, // if you want you can use true here
secure: false, // if you are using HTTPS I suggest true here
signed: false, // I don't know if csurf supports signed cookies, so I used false
// not mandatory, but if you want you can use sameSite: 'strict'
// sameSite: 'strict', // https://www.owaspsafar.org/index.php/SameSite
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}
}));
app.use((req, res, next) => {
const csrfTokenToSendToFrontEnd = req.csrfToken();
console.log('csrfTokenToSendToFrontEnd: ', csrfTokenToSendToFrontEnd);
// this cookie must be XSRF-TOKEN, because already defined as default in Angular.
res.cookie('XSRF-TOKEN', csrfTokenToSendToFrontEnd);
next();
});
// here requires the api file with all your rest apis (not static paths)
const routesApi = require('./src/routes/index')(express, passport);
app.use('/api', routesApi);
最后,在文件结尾之前(在500个中间件之前),我添加了它来处理错误:
// error handler
app.use((err, req, res, next) => {
if (err.code !== 'EBADCSRFTOKEN') {
return next(err);
}
res.status(403).json({
message: 'error'
});
});
我只复制了相关代码。如果您有任何疑问,请随时检查是否忘记了任何内容。
答案 1 :(得分:0)
有多个模块可以帮助您在应用程序中实现CSRF令牌。其中之一是csurf。通过运行以下命令安装该模块以及cookie-parser依赖项:
npm install cookie-parser csurf --save
这两个模块都是中间件,可以改变Express中请求的行为。我们已经在使用body-parser解析POST正文以检索消息。此外,我们将使用它来检查_csrf令牌。 cookie解析器中间件将检查cookie和请求正文中是否都存在_csrf令牌,从而检查cookie中的令牌是否可用,并且csurf将自动保护所有POST,PUT,PATCH或DELETE操作。他们匹配。
将以下代码添加到index.js文件中以配置中间件:
const express = require('express');
const bodyParser = require('body-parser');
const csurf = require('csurf');
const cookieParser = require('cookie-parser');
const PORT = process.env.PORT || 3000;
const app = express();
const csrfMiddleware = csurf({
cookie: true
});
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(cookieParser());
app.use(csrfMiddleware);
app.get('/', (req, res) => {
res.send(`
<h1>Hello World</h1>
<form action="/entry" method="POST">
<div>
<label for="message">Enter a message</label>
<input id="message" name="message" type="text" />
</div>
<input type="submit" value="Submit" />
<input type="hidden" name="_csrf" value="${req.csrfToken()}" />
</form>
`);
});
app.post('/entry', (req, res) => {
console.log(`Message received: ${req.body.message}`);
res.send(`CSRF token used: ${req.body._csrf}, Message received: ${req.body.message}`);
});
app.listen(PORT, () => {
console.log(`Listening on http://localhost:${PORT}`);
});
重新启动服务器,然后导航到http://localhost:3000。在输入框中输入一些文字,然后点击提交。您应该会看到该消息出现在控制台中,并在浏览器中看到类似以下消息的消息: