重新整理 .net core 實踐篇————cookie 安全問題[三十八]

敖毛毛發表於2021-07-08

前言

簡單整理一下cookie的跨站攻擊,這個其實現在不常見,因為很多公司都明確宣告不再用cookie儲存重要資訊,不過對於老站點還是有的。

正文

攻擊原理:

這種攻擊要達到3個條件:

  1. 使用者訪問了我們的站點。

  2. 使用者通過cookie儲存和傳遞身份資訊

  3. 使用者訪問了壞站點

1和3根本控制不了,那麼控制的就只有2,就是不使用cookie。

但是有些站點改動太大,那麼是否還有其他方式呢?

防禦方式:

  1. 不使用cookie儲存和傳輸身份認證

  2. 使用antiforgerytoken,anti-forgery 防偽。

  3. 避免使用Get作為業務操作的請求方式

那麼從上面看可以在2上做文章,因為3只是說讓低階黑客被阻擋。

同樣antiforgerytoken 方式也有兩種:

  1. validateAntiForgeryToken
  2. 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等其他的才會驗證。

如果你怕這個把握不住的話,那麼你可以設定屬性:

[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme+","+CookieAuthenticationDefaults.AuthenticationScheme)]
public IActionResult Pay()
{
	return Content(User.FindFirst("name").Value+"買買買");
}

直接在方法上加入ValidateAntiForgeryToken即可。

那麼我們還是按照前面的方法進行演示一下。

這裡表示的就是400了,bad request。

先這樣,細節篇會介紹是具體是如何驗證的,在DefaultAntiforgery這個類裡面。

下一節重定向攻擊。

相關文章