feng xiaohan

CSRF

CSRF(Cross-Site Request Forgery):跨域请求伪造。

CSRF Attacks

一种特殊的攻击方式,通过伪造一个网站,来欺骗用户发送敏感信息,然后根据收集的敏感信息伪装用户向真正的网站发送一些不利请求信息。

防止 CSRF Attacks

确保人们只能在你的视图界面使用你的 session(使用你的应用程序呈现的视图),这样在任何虚假伪造的页面上都不能使用你的 session(即使它看上去很像你的页面)。为了达到这一点,我们可以使用 CSRF Token。

使用 CSRF Token

安装和引入

安装 csurf 包,它允许我们生成CSRF Token嵌入到表单中。在视图和服务器上都包含这个 token,每当我们发起请求时,这个包将检查传入的请求是否具有有效的 token。

此包依赖 express 和 express-session。

npm install --save csurf

场景:虚假网站使用用户的 session 模拟用户向真正的后端发送请求,但是CSRF Token是随机的 hash 值,它只有一个值有效,而且只有在服务器上运行的包(csurf)才知道这个 token 值是否有效。所以虚拟网站发送请求会丢失 token。

app.js

const app = express();
const csrf = require("csurf"); // 引入csurf包

const csrfProtection = csrf(); // 定义csrf的执行函数

app.use(csrfProtection); // 注册中间件

对于任何非 GET 的请求,csurf 包会在视图(request body)中寻找一个CSRF Token,所以我们需要先生成它:

生成 token

exports.getIndex = (req, res, next) => {
  Product.find()
    .then((products) => {
      res.render("shop/index", {
        prods: products,
        pageTitle: "Shop",
        path: "/",
        isAuthenticated: req.session.isLoggedIn,
        csrfToken: req.csrfToken(), // 创建一个token并存储到csrfToken
      });
    })
    .catch((err) => console.log(err));
};

也可以将生成 token 注册成一个中间件,让每个页面的非 GET 请求都能执行

app.js

...
app.use((req, res, next) => {
  res.locals.isAuthenticated = req.session.isLoggedIn;
  res.locals.csrfToken = req.csrfToken();
  next();
});

这样需要在每个发送非 GET 请求的表单上加上:

<input type="hidden" name="_csrf" value="<%= csrfToken %>">

页面通过请求发送 token 并隐藏 token

我使用 ejs 在页面用户进行操作时发送请求,在请求中携带 token,通过type="hidden"来隐藏 token:

<form action="/logout" method="post">
    <input type="hidden" name="_csrf" value="<%= csrfToken %>">
    <button type="submit">Logout</button>
</form>

将 input 的 name 设置为_csrf,csurf 包会自动寻找这个参数。