gpt4 book ai didi

spring-security - CSRF 跨域

转载 作者:行者123 更新时间:2023-12-03 18:59:35 25 4
gpt4 key购买 nike

我的 REST API 后端目前使用基于 cookie 的 CSRF 保护。

基本过程是后端设置一个可由客户端应用程序读取的 cookie,然后在后续的 HXR 请求(我的 CORS 设置允许)中,自定义 header 与 cookie 一起传递,服务器检查两个值是否匹配.

本质上,这一切都是通过 Spring Security 中的一行非常简单的 Java 代码实现的。

.csrf().csrfTokenRepository(new CookieCsrfTokenRepository())

当 UI 来自同一个域时,这很有效,因为客户端中的 JS 可以轻松访问(非 http-only)cookie 以读取值并发送自定义 header 。

当我希望我的客户端应用程序部署在不同的域上时,我的挑战就出现了,例如
API: api.x.com
UI: ui.y.com

我解决这个问题的想法是
  • 与仅在 cookie 中发送 token 不同,它可以在自定义响应 header 中与 cookie 一起发送回。
  • 客户端然后读取自定义 header 和本地疮(使用本地存储或可能通过在客户端动态创建 cookie,但这次是在 UI 域上,以便它可以稍后读取)。
  • 该值随后由客户端在自定义请求 header 中发出 XHR 请求时使用,并且在步骤 1 中设置的 cookie 也将随之使用。
  • 服务器检查这两个值(cookie 和请求 header )是否已设置以及它们是否完全匹配。

  • 这是众所周知/可接受的方法吗?任何人都可以从安全角度识别这种方法的任何明显缺陷。

    显然,API 服务器需要允许 UI 域的 CORS + 允许凭据并在 CORS 策略中公开自定义响应 header 。

    编辑

    我将尝试使用我编写的这个自定义存储库在 Spring Security 中实现这一点:
    import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
    import org.springframework.security.web.csrf.CsrfToken;
    import org.springframework.security.web.csrf.CsrfTokenRepository;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    /**
    * This class is essentially a wrapper for a cookie based CSRF protection scheme.
    * <p>
    * The issue with the pure cookie based mechanism is that if you deploy the UI on a different domain to the API then the client is not able to read the cookie value when a new CSRF token is generated (even if the cookie is not HTTP only).
    * <p>
    * This mechanism essentially does the same thing, but also provides a response header so that the client can read this value and the use some local mechanism to store the token (session storage, local storage, local user agent DB, construct a new cookie on the UI domain etc).
    */
    public class CrossDomainHeaderAndCookieCsrfTokenRepository implements CsrfTokenRepository {

    public static final String XSRF_HEADER_NAME = "X-XSRF-TOKEN";
    private static final String XSRF_TOKEN_COOKIE_NAME = "XSRF-TOKEN";
    private static final String CSRF_QUERY_PARAM_NAME = "_csrf";

    private final CookieCsrfTokenRepository delegate = new CookieCsrfTokenRepository();

    public CrossDomainHeaderAndCookieCsrfTokenRepository() {
    delegate.setCookieHttpOnly(true);
    delegate.setHeaderName(XSRF_HEADER_NAME);
    delegate.setCookieName(XSRF_TOKEN_COOKIE_NAME);
    delegate.setParameterName(CSRF_QUERY_PARAM_NAME);
    }

    @Override
    public CsrfToken generateToken(final HttpServletRequest request) {
    return delegate.generateToken(request);
    }

    @Override
    public void saveToken(final CsrfToken token, final HttpServletRequest request, final HttpServletResponse response) {
    delegate.saveToken(token, request, response);
    response.setHeader(token.getHeaderName(), token.getToken());
    }

    @Override
    public CsrfToken loadToken(final HttpServletRequest request) {
    return delegate.loadToken(request);
    }
    }

    最佳答案

    我认为您可以为 CsrfTokenRepository 提供另一种实现,以支持 CSRF token 的不同域模式。

    您可以通过对代码进行以下更改来克隆原始实现:

    ....

    private String domain;
    private Pattern domainPattern;

    ....

    public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {

    ....

    String domain = getDomain(request);
    if (domain != null) {
    cookie.setDomain(domain);
    }

    response.addCookie(cookie);
    }

    .....

    public void setDomainPattern(String domainPattern) {
    if (this.domain != null) {
    throw new IllegalStateException("Cannot set both domainName and domainNamePattern");
    }
    this.domainPattern = Pattern.compile(domainPattern, Pattern.CASE_INSENSITIVE);
    }

    public void setDomain(String domain) {
    if (this.domainPattern != null) {
    throw new IllegalStateException("Cannot set both domainName and domainNamePattern");
    }
    this.domain = domain;
    }

    private String getDomain(HttpServletRequest request) {
    if (this.domain != null) {
    return this.domain;
    }
    if (this.domainPattern != null) {
    Matcher matcher = this.domainPattern.matcher(request.getServerName());
    if (matcher.matches()) {
    return matcher.group(1);
    }
    }
    return null;
    }

    然后,提供您的新实现。
    .csrf().csrfTokenRepository(new CustomCookieCsrfTokenRepository())

    关于spring-security - CSRF 跨域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45424496/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com