【问题标题】:Double Submit CSRF protection cross domain双提交 CSRF 保护跨域
【发布时间】:2015-11-20 10:45:30
【问题描述】:

我对如何进行 CSRF 保护感到困惑。我有单独的前端(angularjs)和后端(Spring)。它们部署在完全不同的地方,并通过 REST 进行通信。

我的问题如下。 Angular 拒绝跨域发送我的 CSRF cookie - 我只能发送 CSRF 标头。我尝试将withCredentials 添加到后端的角度和CORS 过滤器中,并按照here under Usage 的描述设置xsrf 标头和cookie。

任何想法我可能做错了什么?如果您想要我的代码的某些特定部分,请发布,我会提供。

@添加相关代码:

CORS过滤器

public class CORSFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
        response.setHeader("Access-Control-Allow-Origin", "http://localhost:9000");
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,origin,content-type,accept,X-XSRF-TOKEN, authorization, customer-id, X-AUTH-TOKEN");
        response.setHeader("Access-Control-Expose-Headers", "employee_name, employee_id, employee_customer_id, X-AUTH-TOKEN");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        if (request.getMethod()!="OPTIONS") {
            chain.doFilter(req, res);
        } else {
        }
    }

CSRF 过滤器

public class StatelessCSRFFilter extends OncePerRequestFilter {

    private static final String CSRF_TOKEN = "CSRF-TOKEN";
    private static final String X_CSRF_TOKEN = "X-XSRF-TOKEN";
    private final RequestMatcher requireCsrfProtectionMatcher = new DefaultRequiresCsrfMatcher();
    private final AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (requireCsrfProtectionMatcher.matches(request)) {
            final String csrfTokenValue = request.getHeader(X_CSRF_TOKEN);
            final Cookie[] cookies = request.getCookies();

            String csrfCookieValue = null;
            if (cookies != null) {
                for (Cookie cookie : cookies) {
                    if (cookie.getName().equals(CSRF_TOKEN)) {
                        csrfCookieValue = cookie.getValue();
                    }
                }
            }
            if (csrfTokenValue == null || !csrfTokenValue.equals(csrfCookieValue)) {
                accessDeniedHandler.handle(request, response, new AccessDeniedException(
                        "Missing or non-matching CSRF-token"));
                return;
            }
        }
        filterChain.doFilter(request, response);
    }

    public static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
        private final Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");

        @Override
        public boolean matches(HttpServletRequest request) {
            return !allowedMethods.matcher(request.getMethod()).matches();
        }
    }

app.js

(...)
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-TOKEN';
$httpProvider.defaults.xsrfCookieName = 'CSRF-TOKEN';
$httpProvider.interceptors.push('InterceptorCsrf');
$httpProvider.defaults.withCredentials = true;
(...)

InterceptorCsrf.js

angular.module('EnterprisePortalApp')
        .factory('InterceptorCsrf',function($cookies, $cookieStorage){
            function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e16]+1e16).replace(/[01]/g,b)};
            return {
                //With each request generate new csrf token
                request: function(config) {
                    $cookieStorage.put("CSRF-TOKEN", b());
                    config.headers['X-XSRF-TOKEN'] = $cookies.get('CSRF-TOKEN');
                    return config;
                }
            }   
});

【问题讨论】:

  • 您能否详细说明一下它们部署在完全不同的地方是什么意思?是在不同的服务器上吗?
  • @MicheleRicciardi 是的
  • 您是否在后端正确配置了Access-Control-Allow-Origin 标头?您可以在后端和前端部分共享代码块相关部分。这样人们就可以检查问题。
  • @İlkerKorkut 我已经添加了相关代码

标签: javascript java angularjs spring cookies


【解决方案1】:

您的代码块似乎没问题。你有没有试过把"Access-Control-Allow-Origin", "http://localhost:9000"改成这个*

顺便说一句,it's a bug in chrome pointing localhost with its port 无法修复 (SO discussion)。

您也可以尝试为其他服务器和客户端主机提供不同的域名(而不是 localhost,您可以使用 nginx 代理设置等。这可能会有些棘手)。

根据这种情况的额外信息:

如果对 REST 服务使用基于令牌的身份验证,则无需额外实施 csrf 保护。

如果用户需要在每次请求此 REST 服务时发送他的访问令牌(例如jwt),您的服务会受到 csrf 保护,以及类似的 csrf 保护方法。 User gets token->request messages with token->decode token on backend->getuserid(basic) 并使他的进程成为像这样的基于令牌的请求进程。在这种情况下,如果用户没有令牌,他就不能做任何事情。

【讨论】:

  • 我不能同时使用response.setHeader("Access-Control-Allow-Credentials", "true");"Access-Control-Allow-Origin", "http://localhost:9000"。 ` 当凭证标志为真时,不能在“Access-Control-Allow-Origin”标头中使用通配符“*”。` 被抛出
  • 您在每个请求中都使用X-AUTH-TOKEN 吗?如果是这样,您不需要 csrf 令牌。顺便说一句,您是否尝试为前端主机和休息服务主机提供特定域?
  • 您能解释一下为什么我不需要 csrf 令牌吗?
  • @KrzysztofPiszko 如果用户需要在每次请求此 REST 服务时发送他的访问令牌(例如 jwt),您的服务会受到 csrf 保护,以及类似的 csrf 保护方法。 User gets token->request messages with token->decode token on backend->getuserid and make his process 像这样的基于令牌的请求过程。如果你有这个规范,那么不要浪费你的时间来实现 csrf 保护,在这种情况下,如果用户没有令牌,他将无能为力。
  • 知道了,没想到会这么简单。现在我已经重新阅读了 csrf 的内容,它似乎很合适。谢谢!
【解决方案2】:

为我工作,使用以下代码

  1. 服务器端,我已经按照官方Spring Angular 指南中的描述编写了CSRFCORS 过滤器。
  2. 客户端,我必须编写一个 $http 拦截器,如下所示,因为 AngularJS doesn't automatically add 是跨域请求的标头。

    angular.module('appBoot')
      .factory('XSRFInterceptor', function ($cookies, $log) {
    
        var XSRFInterceptor = {
    
          request: function(config) {
    
            var token = $cookies.get('XSRF-TOKEN');
    
            if (token) {
              config.headers['X-XSRF-TOKEN'] = token;
              $log.info("X-XSRF-TOKEN: " + token);
            }
    
            return config;
          }
        };
        return XSRFInterceptor;
      });
    
    angular.module('appBoot', ['ngCookies', 'ngMessages', 'ui.bootstrap', 'vcRecaptcha'])
        .config(['$httpProvider', function ($httpProvider) {
    
          $httpProvider.defaults.withCredentials = true;
          $httpProvider.interceptors.push('XSRFInterceptor');
    
        }]);
    

【讨论】:

  • 我是通过前端生成token来实现的,所以和你的方法不太一样。
猜你喜欢
  • 2015-03-03
  • 2017-04-25
  • 1970-01-01
  • 2014-02-19
  • 2013-03-08
  • 2018-01-07
  • 2013-02-04
  • 2015-04-26
  • 2011-02-16
相关资源
最近更新 更多