bodyParser
の後のapp.js
ファイルサーバー側に次のものがあります
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"
と表示されます。
angular側で何かを行う必要がありますか?フォームデータを追加する必要がありますか?
助けてくれてありがとう。
コードでは、多くのモジュールを使用しています。問題を切り分けるために、コードを最小限のバージョンに減らして、csrfを有効にするために必須ではないものをすべて削除することをお勧めします。
これは単なる提案ですが、私のAngular 7アプリケーションでこれを追加しました(Angularのソースコードにデフォルト値があるため、cookienameとtokenを変更する必要はありません):
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'
});
});
関連するコードのみをコピーしました。問題が発生した場合は、お気軽に質問してください。何か忘れた場合は、コードをもう一度確認します。
アプリケーションにCSRFトークンを実装するのに役立つ複数のモジュールがあります。それらの1つはcsurfです。以下を実行して、そのモジュールをcookie-parserの依存関係とともにインストールします。
npm install cookie-parser csurf --save
これらのモジュールは両方とも、Expressでのリクエストの動作を変更できるミドルウェアです。すでにbody-parserを使用してPOST bodyを解析し、メッセージを取得しています。さらに、これを使用して_csrfトークンをチェックします。cookie-parserミドルウェアは、トークンがCookieとcsurfで利用できるのは、_csrfトークンがCookieとリクエスト本文の両方に存在し、それらが一致することを確認することで、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:30 に移動します。入力ボックスにテキストを入力し、[送信]をクリックします。メッセージがコンソールに表示され、ブラウザに次のようなメッセージが表示されます。