我对如何进行CSRF保护感到困惑。我有单独的前端(angularjs)和后端(Spring)。它们部署在完全独立的地方,并通过REST进行通信。
我的问题如下。 Angular拒绝发送我的CSRF cookie跨域 - 我可以发送的是CSRF头。我尝试将withCredentials
添加到我后端的角度和CORS过滤器,并按照here under Usage所述设置xsrf标头和Cookie。
任何想法我可能做错了什么?如果您需要我的代码的某些特定部分,请发布并发送。
@添加相关代码:
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 :(得分: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 :(得分:-1)
使用以下代码为我工作
客户端,我必须编写一个 $ 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');
}]);