简单整理一下cookie的跨站攻击,这个其实现在不常见,因为很多公司都明确声明不再用cookie存储重要信息,不过对于老站点还是有的。
正文攻击原理:
这种攻击要达到3个条件:
用户访问了我们的站点。
用户通过cookie存储和传递身份信息
用户访问了坏站点
1和3根本控制不了,那么控制的就只有2,就是不使用cookie。
但是有些站点改动太大,那么是否还有其他方式呢?
防御方式:
不使用cookie存储和传输身份认证
使用antiforgerytoken,anti-forgery 防伪。
避免使用Get作为业务操作的请求方式
那么从上面看可以在2上做文章,因为3只是说让低级黑客被阻挡。
同样antiforgerytoken 方式也有两种:
validateAntiForgeryToken
AntoValidateAntiforgeryToken
那么就来演示一下:
下面问我们的站点,提供的cookie身份认证方式:
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["SecurityKey"])); services.AddSingleton(securityKey); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { }).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ClockSkew = TimeSpan.FromSeconds(30), ValidateIssuerSigningKey = true, ValidAudience = "localhost", ValidIssuer = "localhost", IssuerSigningKey = securityKey }; });上面是cookie和jwt的认证方式哈。
那么下面这个是cookie登录:
[HttpGet] public async Task<IActionResult> CookieLogin(string userName) { var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); identity.AddClaim(new Claim("Name", userName)); await this.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,new ClaimsPrincipal(identity)); return Content("login"); }那么有一个接口是购买接口:
[ApiController] [Route("[controller]")] public class OrderController : Controller { [HttpPost] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme+","+CookieAuthenticationDefaults.AuthenticationScheme)] public IActionResult Pay() { return Content(User.FindFirst("name").Value+"买买买"); } }上面认证方式有cookie和jwt了。
那么我们先使用cookie进行登录一下,调用CookieLogin这个接口。
https://localhost:5001/Account?username=aomaomao以看到前台有cookie信息。
在另外一个站点有这样一个东西:
<form action="https://localhost:5001/order" method="post"> <label>打折购买:</label> <label>1块钱</label> <input type="submit" value="提交"/> </form>那么点击一下这个提交后:
可以看到这样就被攻击了。
那么可能有人就会说,如果自己看了一眼网站地址应该就不会出现问题吧。
其实一般攻击站点一般不给你点击的机会。
<script> document.form[0].submit() </script>这一段代码在进入网站的时候就自动帮你点击了。
services.AddAntiforgery(options => { options.HeaderName = "X-CSRF-TOKEN"; }); services.AddMvc(options => options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));上面这个代码进行Antiforgery验证,通过header里面的X-CSRF-TOKEN的值。options => options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()是自我判断是否应该验证的机制。
可以看下AutoValidateAntiforgeryTokenAttribute的源码:
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) { return (IFilterMetadata) serviceProvider.GetRequiredService<AutoValidateAntiforgeryTokenAuthorizationFilter>(); }那么继续看AutoValidateAntiforgeryTokenAuthorizationFilter:
internal class AutoValidateAntiforgeryTokenAuthorizationFilter : ValidateAntiforgeryTokenAuthorizationFilter { public AutoValidateAntiforgeryTokenAuthorizationFilter( IAntiforgery antiforgery, ILoggerFactory loggerFactory) : base(antiforgery, loggerFactory) { } protected override bool ShouldValidate(AuthorizationFilterContext context) { if (context == null) throw new ArgumentNullException(nameof (context)); string method = context.HttpContext.Request.Method; return !string.Equals("GET", method, StringComparison.OrdinalIgnoreCase) && !string.Equals("HEAD", method, StringComparison.OrdinalIgnoreCase) && (!string.Equals("TRACE", method, StringComparison.OrdinalIgnoreCase) && !string.Equals("OPTIONS", method, StringComparison.OrdinalIgnoreCase)); } }看ShouldValidate,里面说明了get,head,trace,options都不会进行验证,post delete等其他的才会验证。