我对CSRF的token、cookies、token的理解,总体来说是很薄弱的。我一直无法让它们在我的应用程序中有效地工作。
目前,我有一个使用 express 进行路由的节点服务器。我的问题是,每当我尝试将 csurf(一个节点 csrf 中间件包)与任何应用程序集成时,它都出于某种原因不起作用。
我按照教程和所有内容尝试了很多不同的设置。
const cookieParser = require("cookie-parser");
const csrf = require("csurf");
const bodyParser = require("body-parser");
const express = require("express");
const admin = require("firebase-admin");
const serviceAccount = require("./secretKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
const csrfMiddleware = csrf({ cookie: true });
const PORT = process.env.PORT || 3000;
const app = express();
app.engine("html", require("ejs").renderFile);
app.use(express.static("static"));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(csrfMiddleware);
app.all("*", (req, res, next) => {
console.log(req)
res.cookie("XSRF-TOKEN", req.csrfToken());
next();
});
app.get("/login", function (req, res) {
res.render("login.html");
});
app.get("/signup", function (req, res) {
res.render("signup.html");
});
app.get("/profile", function (req, res) {
const sessionCookie = req.cookies.session || "";
admin
.auth()
.verifySessionCookie(sessionCookie, true /** checkRevoked */)
.then(() => {
res.render("profile.html");
})
.catch((error) => {
res.redirect("/login");
});
});
app.get("/", function (req, res) {
res.render("index.html");
});
app.post("/sessionLogin", (req, res) => {
const idToken = req.body.idToken.toString();
const expiresIn = 60 * 60 * 24 * 5 * 1000;
admin
.auth()
.createSessionCookie(idToken, { expiresIn })
.then(
(sessionCookie) => {
const options = { maxAge: expiresIn, httpOnly: true };
res.cookie("session", sessionCookie, options);
res.end(JSON.stringify({ status: "success" }));
},
(error) => {
res.status(401).send("UNAUTHORIZED REQUEST!");
}
);
});
app.get("/sessionLogout", (req, res) => {
res.clearCookie("session");
res.redirect("/login");
});
app.listen(PORT, () => {
console.log(`Listening on http://localhost:${PORT}`);
});
上面的代码片段实际上直接取自这个 youtube 教程,并做了一些小调整: https://www.youtube.com/watch?v=kX8by4eCyG4&ab_channel=MaksimIvanov (firebase 用于身份验证,但这无关)
我的问题是,每次我尝试通过“登录”路由登录时,我都会在节点控制台中收到此错误。
ForbiddenError: invalid csrf token
这是登录页面的正文
<main>
<section>
<form id="login">
<label>Login</label>
<input type="text" name="login" />
<label>Password</label>
<input type="password" name="password" />
<button>Log in</button>
</form>
</section>
<script>
window.addEventListener("DOMContentLoaded", () => {
var firebaseConfig = { some private stuff };
firebase.initializeApp(firebaseConfig);
firebase
.auth()
.setPersistence(firebase.auth.Auth.Persistence.NONE);
document
.getElementById("login")
.addEventListener("submit", (event) => {
event.preventDefault();
const login = event.target.login.value;
const password = event.target.password.value;
firebase
.auth()
.signInWithEmailAndPassword(login, password)
.then(({ user }) => {
return user.getIdToken().then((idToken) => {
return fetch("/sessionLogin", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type":
"application/json",
},
body: JSON.stringify({ idToken }),
});
});
})
.then(() => {
return firebase.auth().signOut();
})
.then(() => {
window.location.assign("/profile");
});
return false;
});
});
</script>
目前,我的实际项目有不同的设置,但即使是本教程设置似乎也不起作用。我仍然收到错误:ForbiddenError: invalid csrf token
。
我想知道为什么会遇到此问题,但 youtube 教程却没有。我还想知道是否可以将 csrf 令牌发送到独立的前端(与后端完全分开),因为这是我实际想要的设置。当前示例中的设置从服务器本身提供静态 HTML。
我正在本地主机上手动测试和开发所有内容。