如题,项目中用了ASP.Net MVC Form认证,是.Net Framework 4.7的,安全组发现通过下面操作发现安全问题。
在登录状态下用burp抓取请求的数据包,里面包含Cookies等信息。 然后再进行登出操作,注销掉sso。再利用之前的请求包去尝试请求Controler(账户注销后的Cookies仍有效)。
请求的Controler是继承了AuthorizeAttribute,在浏览器页面中访问时正常鉴权的,而且发现AuthorizeAttribute只对浏览器页面访问生效,用接口工具不能触发。
经过一番网上冲浪得知此问题是微软当初设计Form认证时留的坑,登出操作调用FormsAuthentication.SignOut();Session.Clear();Session.Abandon();只能清除客户端的cookies认证信息,并没有方法清除服务端里保存的认证信息,因此若获得了客户端的cookies(里面含有FormsAuthentication生成的ticket认证信息)就可以用如postman等的工具直接访问controler,直到服务端的ticket过期。
为了防止这个安全问题被利用只能自己填坑了,方案如下:
用SSL加密传输,这很普遍,不加阐述。
通过利用FormsAuthenticationTicket里的UserData,记录登录时的用户本地信息来确保请求同一个终端用户,代码片段如下:
在登录时记录用户本地信息到ticket的UserData,ticket的生成是经过加密后传给客户端的,所以UserData是安全的:
FormsAuthenticationTicket ticket;
var userData = string.Format("{0}{1}{2}{3}", Request.UserHostAddress, Request.UserHostName, Request.Browser.Platform, Request.Browser.Type);
ticket = new FormsAuthenticationTicket(1, userEntity.UserAccount, now, now.Add(expirationTimeSpan), false, userData, FormsAuthentication.FormsCookiePath);
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
在Application_AuthenticateRequest里与当前请求的用户本地信息做比对,从而防止其他终端用Cookies直接访问。
HttpCookie decryptedCookie =
Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(decryptedCookie.Value);
if (Request.IsAuthenticated)
{
var userHostInfo = string.Format("{0}{1}{2}{3}", Request.UserHostAddress, Request.UserHostName, Request.Browser.Platform, Request.Browser.Type);
if (!ticket.UserData.Equals(userHostInfo))
{
Request.Abort();
}
}
因为项目是web应用,只保证页面正常访问即可,到此问题解决。