api.sample.com
背后有一个API,app.sample.com
背后有一个基于浏览器的网络应用程序(单页,Javascript驱动)以及其他非基于浏览器的客户端。
API支持基本和基于令牌的身份验证,并将令牌打包到浏览器的cookie中。
要使设置适用于浏览器,必须启用CORS并设置withCredentials:
var cors = new EnableCorsAttribute("https://app.sample.com", "*", "GET,POST,PUT,DELETE");
cors.SupportsCredentials = true;
cors.PreflightMaxAge = 3600;
config.EnableCors(cors);
现在我使用了CSRF漏洞并发现AJAX被原始设置阻止,<img>
或<a>
没有看到任何响应数据,但<form>
- 提交已开启利用。
为了保护表单,我在混音中添加了一个防伪标记,并针对没有设置AllowAnonymousAttribute
的控制器操作的每个请求验证它。
我想使用System.Web.Helpers.AntiForgery
类,但它需要设置主体标识,而不是在登录时验证凭据时的情况 - 仅在后续请求中。
所以我想到了浏览器客户端在验证凭据后立即请求CSRF-Token的想法,在每个请求上将令牌作为标头发送,服务器在FilterAttribute中验证令牌。 E.g。
[HttpGet, Route("xsrf"), AllowAnonymous]
public IHttpActionResult GetCSRFToken()
{
// AllowAnonymous is set to allow requests without csrf token to reach this action
if (User.Identity.IsAuthenticated)
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
// or set a cookie
return Ok(cookieToken + ":" + formToken);
}
return Unauthorized();
}
和
public class ValidateXSRFTokenAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
// apply only if cookie-authentication and no AllowAnonymousAttribute set on the action
if (actionContext.RequestContext.Principal.Identity.AuthenticationType == DefaultAuthenticationTypes.ApplicationCookie &&
!actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
{
try
{
string cookieToken = "";
string formToken = "";
IEnumerable<string> tokenHeaders;
if (actionContext.Request.Headers.TryGetValues("X-XSRF-Token", out tokenHeaders))
{
var tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
catch
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{
ReasonPhrase = "Anti-XSRF Validation Failed."
};
}
}
base.OnAuthorization(actionContext);
}
}
此实施是否正确?有什么需要改进的吗?
仍然可以使服务器使用<form>
或<iframe>
提交来发送CSRF令牌,因此保护取决于无法根据同源策略读取javascript中的响应
或者更好地滚动我自己的CSRF-Token,可以在登录时直接设置?