2015-08-26 1 views
1

Я смущенно о том, как мне сделать защиту CSRF. У меня есть отдельный интерфейс (angularjs) и backend (Spring). Они развернуты в совершенно разных местах и ​​сообщают REST.Двойная передача CSRF защита кросс-домена

Моя проблема заключается в следующем. Угловой отказывается отправлять мой домен CSRF cookie - все, что я могу отправить, это заголовок CSRF. Я попытался добавить withCredentials как к угловому, так и к фильтрам CORS на моем бэкэнд и настройке заголовка xsrf и файла cookie, как описано here under Usage.

Любые идеи, что я могу делать неправильно? Если вам нужна определенная часть моего кода, отправьте сообщение, и я поставлю его.

@Adding соответствующий код:

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

Не могли бы вы рассказать о том, что вы имеете в виду, говоря, что они развернуты в совершенно разных местах? Это на разных серверах? –

+0

@MicheleRicciardi Yup –

+0

Вы правильно настроили заголовок 'Access-Control-Allow-Origin' на своем сервере? Вы можете делиться блоками, связанными с блоками, как с базовыми, так и с внешними разделами. Поэтому люди могут проверять проблемы. –

ответ

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 (базовый) и сделать свой процесс процессом запроса на токен, подобным этим. В этом случае, если пользователь не имеет токена, он не может ничего делать.

+0

Я не могу использовать 'response.setHeader (" Access-Control-Allow-Credentials "," true ");' и '" Access-Control-Allow-Origin "," http: // localhost: 9000 "' at в то же время. 'Подстановочный знак '*' не может использоваться в заголовке« Access-Control-Allow-Origin », когда флаг учетных данных имеет значение true.' Gets thrown –

+0

Используете ли вы 'X-AUTH-TOKEN' в каждом запросе? Если это так, вам не нужен токен csrf. Кстати, вы пытались предоставить конкретный домен как хосту, так и хосту? –

+0

Не могли бы вы объяснить, почему мне не нужен токен csrf? –

-1

Работа для меня, со следующим кодом

  1. стороны сервера, я закодировал CSRF и CORS фильтров, как описан в официальном Spring Angular руководства.
  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'); 
    
        }]); 
    
+0

Я делаю это, создавая токен на frontend, так что это не совсем так, как ваш метод. –

Смежные вопросы