koa2实现网站csrf防御

2023-01-11 20:53:41 浏览数 (2)

什么是csrf攻击?

csrf/xsrf又叫跨站请求伪造。

先说常见的登陆鉴权: 用户在你的网站登陆后,一般把登陆凭证(token)存储在cookie里,之后每次调接口都会自动携带,后端根据这条cookie鉴权,判定是登陆状态,进而允许进行安全操作。

虽然这种鉴权方式最为简便,但存在一种安全隐患,就是csrf。

csrf攻击者会利用http请求自动携带cookie的机制,在用户登陆后,引导用户点击它的攻击链接,进而拿到用户的token去进行恶意请求,比如购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。

防御csrf攻击

思路:

由于csrf攻击者只能拿到cookie去干坏事,但它无法知道cookie里有什么,也拿不到其他有效信息。我们只需要除cookie外再加一道它做不到的验证就可以了。

前端首次加载页面的时候,调接口让后端植入一条csrfToken到cookie里。然后前端每次请求从cookie里取出然后放到请求头里给后端传输。

后端将植入给前端的csrfToken存储在session,然后一些安全接口(一般是除了get请求外的接口),请求时,需要先进行csrf比对,取出request请求头里的csrfToken和自己session里的csrfToken进行比对,完全一致才放行

代码实现

前端(react)

代码语言:javascript复制
1//App.tsx
2//根组件,判断cookie里有没有csrfToken,没有就请求后端种植
3  useEffect(() => {
4    if (!cookie.get("csrf_token")) {
5      request("/all/getCsrfToken");
6    }
7  }, []);
代码语言:javascript复制
1//request.ts
2//axios请求拦截器内置了csrf方法,如下配置即可自动取出cookie里的csrf并放到请求头
3request.interceptors.request.use((config: any) => {
4  // 开启顶部加载进度条
5  Nprogress.start();
6
7  config.xsrfHeaderName = "X-CSRF-TOKEN";
8  config.xsrfCookieName = "csrf_token";
9
10  return config;
11});

后端(koa2)

后端利用koa-csrf中间件实现

代码语言:javascript复制
1yarn add koa-csrf

1、先封装一个csrf实例

代码语言:javascript复制
1//先封装一个csrf实例
2const CSRF = require("koa-csrf");
3
4const csrfMD = new CSRF({
5  invalidSessionSecretMessage: "Invalid session secret",
6
7  invalidTokenMessage: "Invalid CSRF token",
8
9  invalidTokenStatusCode: 403,
10});
11
12module.exports = csrfMD;

参数:

invalidTokenMessage

使 koa 抛出的错误信息内容,默认值为:"Invalid CSRF token"。它可以是一个接收 ctx 作为参数的函数,函数最后返回错误信息内容。

invalidTokenStatusCode

验证失败时的响应状态码,默认值为:403(Forbidden)。跟 invalidTokenMessage 参数一样,它也会被传递给 ctx.throw,用于抛出错误和拒绝请求。

excludedMethods

排除的请求方法,默认值为:["GET", "HEAD", "OPTIONS"]。

disableQuery

是否禁止通过查询字符串传递 _csrf 校验 token,默认值为 false。如果校验 token 出现在 URL 中,则可能会通过 Referer 泄露,应尽量把 Token 放在表单中,把敏感操作由 GET 改为 POST。

2、写一个给前端种植csrfToken的接口(必须是get)

代码语言:javascript复制
1const Csrf = require("@utils/csrf");
2
3router.get("/getCsrfToken", Csrf, async (ctx) => {
4  ctx.cookies.set("csrf_token", ctx.csrf, {
5    httpOnly: false,
6    maxAge: 24 * 60 * 60 * 1000,
7  });
8
9  ctx.body = "csrfToken种植成功!";
10});

3、需要防御csrf的接口(post|put|delete),使用csrf即可自动校验

代码语言:javascript复制
1router.delete("/delete",Csrf, async (ctx) => {
2  const { id } = ctx.query;
3  const result = await sql(`delete from user where id="${id}"`);
4
5  ctx.body = result;
6});

0 人点赞