Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

egg csrf token + cors踩坑 #90

Open
lizhongzhen11 opened this issue May 1, 2020 · 4 comments
Open

egg csrf token + cors踩坑 #90

lizhongzhen11 opened this issue May 1, 2020 · 4 comments

Comments

@lizhongzhen11
Copy link
Owner

lizhongzhen11 commented May 1, 2020

自己的项目到收尾阶段了,今天开始给所有 post 方法接口加 token 校验,以前都是和java对接的,当时就记得他们在登录接口返回后把 token 给我,然后我存起来每次调用带过去,貌似不难,但是,我用egg可是第一次去写这个,踩了好久的坑,都怪自己菜,也怪egg官网例子太简单!

首先,照着egg官网 安全威胁-csrf-的防范 来做的话,一定很懵逼。

为什么这么说呢?

他是告诉我怎么配置了,但是怎么拿?

他直接来个在 jQuery 中例子。首先 var csrftoken = Cookies.get('csrfToken'); 这段代码的 Cookies 是什么?jQuery中可不是这样写的。我下意识的在浏览器 Application 工具栏找了半天也没发现跟 token 有关的字段啊!

正常思维,肯定是先有个登录接口,登录接口不用 token,登陆后把token给前端,然后前端存起来,以后调相关接口时一并发过来才对啊(当然,以后还会更新token)。关键问题是,这个 token 怎么拿???

其实真的很简单,但是官方文档就是不写明白,我没办法只能打印输出去找,但是没找到。后来在 egg-security 库里才发现直接 ctx.csrf 就行了,真的是fuck啊!(虽然官方文档有 ctx.csrf,但是你看他例子,现在才想明白,他这种就类似于jsp形式,直接服务端写html,多别扭啊)

ctx.csrf 为何为空?

这个问题困扰了我很久,估计两个小时要有的。我在 egg issues 不停的翻,终于在 4216 处找到了答案,说到底还是我菜啊,cookie 的 SameSite 不了解。。。

知道问题的原因就很好办了,这时候只能放弃 csrfdomainWhiteList 属性了,改用 corsorigincredentials 配合,同时前端 axios 需要配置 :

withCredentials: true

egg config.default.js:

config.security = {
    csrf: {
      headerName: 'x-csrf-token',
      ignore: /^\/login|\/upload/
    }
  };

config.cors = {
    origin: (http) => {
      const allowOrigin = ['http://localhost:8080', 'http://localhost:8082', 'http://localhost:8083',]
      const origin = http.request.header.origin
      if (allowOrigin.includes(origin)) {
        return origin
      }
      return 'http://localhost:8080'
    },
    credentials: true
  }

前端request也要配置下(response 也要相应改下,主要是存token):

// 请求拦截
instance.interceptors.request.use((config) => {
  config.headers['x-csrf-token'] = session.get('csrftoken')
  return config
}, (err) => {
  return Promise.reject(err)
})

这样配置下,然后在 login 接口处测试下:

// 调用 rotateCsrfSecret 刷新用户的 CSRF token
ctx.rotateCsrfSecret();
const csrftoken = ctx.csrf;
console.log(csrftoken)

发现能打印输出了。这里关键就是cookie在不同域之间的传递问题。

2020-05-03 补充

我犯了大错!

我根本没搞懂cors和csrf就忙着上。我发现打开电脑启动项目,第一次login时返回的 token 为空,很纳闷,随后去官方 issue 上找,果然发现不少跟我一样问题的。

问题的根源在于我是前后端分离项目,域名一样但是端口不同,已经涉及到跨域了,根据回答,我应该只需要设置 cors 即可,csrf 意义不大:invalid csrf token

@ghost
Copy link

ghost commented May 4, 2020

老哥,这个前后端分离的项目,egg 会自动生成一个 csrf token 到cookei里面了 我拿这个放到header请求还是不行
我的目的是获取 session的 现在get能获取到session post 请求获取不到

@ghost
Copy link

ghost commented May 4, 2020

刚刚有仔细看了下他的两个cookie 一个设置到了127.0.0.1 一个设置到了 localhost 127.0。0.1 post可以拿到session 真是日了狗了

@ghost
Copy link

ghost commented May 4, 2020

失误原来是我获取验证码的时候用的是127.0.0.1 post 请求的时候是 locahost 怪不得拿不到呢

@lizhongzhen11
Copy link
Owner Author

@lebow01
根据官方回复,前后端分离项目建议直接启用cors防御而不是csrf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant