首页 文章

双提交CSRF保护跨域

提问于
浏览
1

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

我的问题如下 . Angular拒绝发送我的CSRF cookie跨域 - 我可以发送的是CSRF头 . 我已经尝试将 withCredentials 添加到我的后端的角度和CORS过滤器,并设置xsrf标头和cookie,如here under Usage所述 .

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

@添加相关代码:

CORSFilter

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;
                }
            }   
});

2 回答

  • 1

    您的代码块似乎没问题 . 你有没有尝试过 "Access-Control-Allow-Origin", "http://localhost:9000" 更改为 * .

    BTW it's a bug in chrome pointing localhost with its port无法修复(SO discussion) .

    此外,您可以尝试提供不同的域名(而不是localhost,您可能使用nginx代理设置等,这可能是一些棘手的)休息服务器和客户端主机 .

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

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

    如果用户需要在每次请求此休息服务时发送其访问令牌(例如 jwt ),那么您的服务将受到csrf的保护,并且还具有与csrf保护类似的方法 . User gets token->request messages with token->decode token on backend->getuserid (基本)并使他的过程像这样基于令牌的请求过程 . 在这种情况下,如果用户没有做任何事情 .

  • -1

    使用以下代码为我工作

    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');
    
        }]);
    

相关问题