如何使用 Apollo 和 GraphQL 在 Nextjs 中实现 CSRF 保护

The*_*olf 5 csrf apollo express-session graphql next.js

按照Nextjs 存储库中的这个示例,我想实现 CSRF 保护(可能使用csurf包),因为我使用带有 express-session 的会话 ID cookie。

我尝试在我的自定义服务器中设置 csurf 并将生成的令牌保存在 res.locals.csrfToken 中,可以在第一页加载时通过位于 /lib/withApollo.js 中的静态方法“getInitialProps”获取我的示例链接。一旦我尝试更改页面(带有链接)或尝试使用 apollo 发出发布请求(例如登录),服务器就会更改 csrf 令牌,因此 Apollo 使用的令牌不再有用,所以我得到了“csrf 无效”错误。

带有 csurf 配置的自定义服务器

const csrf = require('csurf');
const csrfProtection = csrf();
////express-session configuration code////
app.use(csrfProtection);
app.use((req, res, next) => {
    res.locals.csrfToken = req.csrfToken();
next();
})
Run Code Online (Sandbox Code Playgroud)

/lib/initApollo.js

function create(initialState, { getToken, cookies, csrfToken }) {
  const httpLink = createHttpLink({
    uri: "http://localhost:3000/graphql",
    credentials: "include"
  });

    const authLink = setContext((_, { headers }) => {
    const token = getToken();
    return {
      headers: {
        ...headers,
        authorization: token ? `Bearer ${token}` : "",
        Cookie: cookies ? cookies : "",
        "x-xsrf-token": csrfToken ? csrfToken : ""
      }
    };
  });
Run Code Online (Sandbox Code Playgroud)

/lib/withApollo.js

static async getInitialProps(ctx) {
  const {
    Component,
    router,
    ctx: { req, res }
  } = ctx;
  const apollo = initApollo(
    {},
    {
      getToken: () => parseCookies(req).token,
      cookies: req ? req.headers.cookie : "",
      csrfToken: res ? res.locals.csrfToken : document.cookie
    }
  );
Run Code Online (Sandbox Code Playgroud)

有了这个配置,每条路由都受到了 csrf 的保护,但是服务器上创建的令牌经常变化,Apollo 无法在需要时立即检索更新的令牌,因此第一次加载成功,但随后的页面更改(链接)或任何发布请求失败,因为令牌已更改。

Nga*_*ine 5

更新

经过这么多浏览,我终于能够发送 csrf cookie。我认为问题在于这个词return。当您使用 return 时,它会排除 cookie。这就是我通过编辑所做的/lib/initApollo.js

函数创建(initialState,{ getToken,cookies,csrfToken }){
  const httpLink = createHttpLink({
    uri: "http://localhost:3000/graphql",
    凭据:“包括”
  });

    const authLink = setContext((_, { headers }) => {
      const token = getToken();
      返回 {
        标题:{
          ...标题,
          授权:令牌?`Bearer ${token}` : "",
          “x-xsrf-token”:csrfToken ?csrfToken : ""
        }
        饼干: {
          ...饼干
        }
      };
    });
  });

喂!!但是 SSR 没有 cookie。我认为我们应该有两个来自客户端的端点,另一个用于 SSR。SSR url 可以免 csrf。


Nga*_*ine 2

这可能不是您正在寻找的答案。我在这里读到,如果您使用 JWT,则不需要 CSRFToken。我不完全确定,但这是目前唯一的办法。

本杰明·M解释如下:

我找到了一些关于CSRF+不使用cookie进行身份验证的信息:

https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/ “由于您不依赖cookie,因此不需要防止跨站点请求”

http://angular-tips.com/blog/2014/05/json-web-tokens-introduction/ “如果我们采用 cookies 方式,您确实需要执行 CSRF 来避免跨站点请求。这是我们可以做到的正如您将看到的,使用 JWT 时会忘记。” (JWT = Json Web Token,无状态应用程序基于令牌的身份验证)

http://www.jamesward.com/2013/05/13/securing-single-page-apps-and-rest-services “在不冒 CSRF 漏洞风险的情况下进行身份验证的最简单方法就是避免使用 cookie 来识别用户”

http://sitr.us/2011/08/26/cookies-are-bad-for-you.html “CSRF 的最大问题是 cookie 完全无法防御此类攻击。如果您使用 cookie 身份验证您还必须采取额外的措施来防止 CSRF。您可以采取的最基本的预防措施是确保您的应用程序在响应 GET 请求时不会产生任何副作用。

还有很多页面指出,如果您不使用 cookie 进行身份验证,则不需要任何 CSRF 保护。当然,您仍然可以将 cookie 用于其他所有用途,但避免在其中存储诸如 session_id 之类的任何内容。

完整文章在这里:使用无状态(=无会话)身份验证时需要 CSRF 令牌吗?