这是防止Next中的CSRF攻击的有效方法吗?js应用

nhn9ugyo  于 2023-04-30  发布在  其他
关注(0)|答案(2)|浏览(119)

我在造一个Nextjs应用程序,在其中一个页面上,我需要调用其中一个/api路由。
调用此API路由将执行状态更改操作,因此确保调用不是来自试图模拟我的用户的攻击者。
以下面的例子为例:

fetch('/api/grantPermissions', {
  headers: ...,
  method: 'POST',
  body: JSON.stringify({resource: 'someresourceid', permission: 'somepermission'})
})

我注意到保护Next的解决方案并不多。js API路由来自CSRF攻击,所以我考虑的是:
1.当用户登录时,将生成一个随机的32字节十六进制字符串
1.它存储在会话对象中(使用Iron-Session)
1.使用getServerSideProps(),会话中存储的字符串被注入到需要进行fetch调用的页面中
1.当正在进行获取调用时,CSRF令牌与请求附连(例如,例如,在正文或自定义标题中)
1.然后,/api/grant路由检查提供的CSRF令牌是否与会话中的令牌相同
这是否是使用同步器令牌模式防止CSRF攻击的安全方法?这种方法可能导致哪些漏洞?

uemypmqf

uemypmqf1#

我首先考虑的是if setting SameSite=Lax for the session cookie is good enough
这是否是使用同步器令牌模式防止CSRF攻击的安全方法?
四个可能的改进点:
1.必须使会话无效才能循环CSRF令牌。
1.随机的32字节十六进制字符串应该用密码学上强的伪随机数生成器生成。
1.您没有提到CSRF对登录的保护,这可以减轻会话固定攻击。
1.看起来iron-session使用无状态会话,这意味着后端没有状态。这听起来像是双重提交Cookie模式的用例。然而,iron-session似乎是使用加密和签名的cookie实现的。考虑到您信任Iron-session,如果您记得在检查会话cookie中的CSRF令牌是否与自定义请求头中的CSRF令牌匹配之前验证会话cookie的签名,我相信Synchronizer Token Pattern应该可以工作。
我喜欢你的方法:

  • 在页面上存储CSRF令牌
  • 使用自定义请求头,X-CSRF-TOKENX-XSRF-TOKEN或仅XSRF-TOKENoften used

如果你考虑到这四点改进,我相信你已经有了一个更安全的同步器令牌模式的实现。然而,我不是一个回答,如果它是足够安全的考虑到你的风险承受能力。看看OWASP CSRF cheatsheet,如果你还没有看到它。
这种方法可能导致哪些漏洞?

  • CSRF保护可以是bypassed if you have a XSS vulnerability
  • 每会话令牌具有有效会话的时间范围,攻击者可以在该时间范围内利用被盗的CSRF令牌。
  • 你依赖iron-session

关于用于保护Next的现有解决方案。来自CSRF攻击的js API路由:

祝你好运!

myzjeezk

myzjeezk2#

有很多灵感来自:https://levelup.gitconnected.com/how-to-implement-csrf-tokens-in-express-f867c9e95af0

import { randomBytes } from 'crypto';
import { NextRequest, NextResponse } from 'next/server';
import { GetServerSideProps } from 'next';

export default function setCsrfCookie(req:NextRequest, res:NextResponse):string {
  if (typeof window !== 'undefined') {
    return '';
  }
  const token = randomBytes(100).toString('base64');
  // @ts-ignore
  res.setHeader("set-cookie", `csrf_token=${token}; path=/; samesite=Strict; httponly;`);

  return token;
}

export default function Page(props: { csrfToken: string }) {
    return (
      <form method="post">
        <input type="hidden" name="csrf_token" value={csrfToken} />
      </form>
    )
}

export async function getServerSideProps(context:GetServerSideProps) {
  const { locale, req, res } = context;
  const csrfToken = setCsrfCookie(req, res);

  return {
    props: {
      csrfToken,
    },
  }
}

然后,在您提交的API路由中,您可以验证cookie csrf_token是否等于表单主体值。

相关问题