ASP.NET重播攻击再次检测
好的,ASP.NET WebForms现在已经是一种古老的技术了,但在可预见的未来,我们将继续使用它,我只是想看看我是否可以加入一个非常基本的重播攻击预防机制。基本上,我们希望确保在用户明确注销后,没有人可以重播旧请求,并根据授权cookie对其进行自动身份验证,但我们仍然希望允许不想重新登录每个新浏览器会话的用户使用永久授权cookie。 根据我在后一种情况下观察到的情况,如果退出浏览器并启动新会话,cookie头中没有会话ID,只有表单授权cookie。在这种情况下,我们希望允许自动身份验证(至少对于GET请求)。 在“replay”的情况下,cookie标头中有一个会话ID,但ASP.NET实际上会使用相同的ID自动重新创建一个新会话。幸运的是,我们可以检测到这一点(通过检查session.IsNewSession和Request.headers[“cookie”]-但奇怪的是,Request.cookie有时包含会话cookie(即使客户端未发送会话cookie),因此,如果客户端已发送会话ID,且用户注销时会话已关闭/放弃,则可以强制重新登录 但是…如果重播攻击故意忽略会话ID,那么当授权cookie是永久cookie时,就没有真正的方法将其与合法的浏览器请求区分开来。在这种情况下,您至少可以阻止“POST”请求,但重播攻击可能只是发出GET first以建立新会话,然后再发出POST。 我真正想要的是一种方法来确定,一旦用户注销,用于授权该会话的cookie值就不再有效了——我想这将需要在DB中存储一些东西(很可能是当前有效cookie值的列表),这似乎比目前所能证明的更费劲。但似乎其他任何东西(例如,在表单上使用隐藏的nonce字段)都倾向于使用脚本来模拟用户使用永久授权cookie重新建立新会话,在这种情况下,他们将能够确定所需的nonce字段值等。因此,假设我正确地认为必须在服务器端存储一些东西来跟踪(基本上)哪些授权cookie值仍然有效,是否有任何已知的低占用空间/众所周知的库可以做到这一点ASP.NET重播攻击再次检测,asp.net,security,session,cookies,replay,Asp.net,Security,Session,Cookies,Replay,好的,ASP.NET WebForms现在已经是一种古老的技术了,但在可预见的未来,我们将继续使用它,我只是想看看我是否可以加入一个非常基本的重播攻击预防机制。基本上,我们希望确保在用户明确注销后,没有人可以重播旧请求,并根据授权cookie对其进行自动身份验证,但我们仍然希望允许不想重新登录每个新浏览器会话的用户使用永久授权cookie。 根据我在后一种情况下观察到的情况,如果退出浏览器并启动新会话,cookie头中没有会话ID,只有表单授权cookie。在这种情况下,我们希望允许自动身份验证
(顺便说一句,现在我已经使用了身份验证票证上的IsPersistent标志-如果这是错误的,即用户明确选择只使用每个会话cookie,知道他们将重新登录到下一个浏览器会话,那么我可以可靠地阻止重播攻击。但是如果是真的,我只阻止“POST”重播,这不会提供太多真实的p保护,但至少担心更复杂的重播攻击的用户可以通过始终选择每次会话身份验证来防止这些攻击)。即使是ASP.NET身份验证也明确表示,您必须进行二次检查,以确认该用户是否仍然是活动的登录用户(例如,我们可以阻止用户,用户可能更改了密码),表单身份验证票证不提供任何针对这些内容的安全性 UserSession与ASP.NET MVC会话无关,它只是这里的一个名称 我实施的解决方案是
UserSessionID(PK,Identity)UserID(FK)DateCreated,DateUpdated在数据库中创建一个UserSessions
表
public void DoLogin(){
// do not call this ...
// FormsAuthentication.SetAuthCookie(....
DateTime dateIssued = DateTime.UtcNow;
var sessionID = db.CreateSession(UserID);
var ticket = new FormsAuthenticationTicket(
userName,
dateIssued,
dateIssued.Add(FormsAuthentication.Timeout),
iSpersistent,
// userData
sessionID.ToString());
HttpCookie cookie = new HttpCookie(
FormsAuthentication.CookieName,
FormsAuthentication.Encrypt(ticket));
cookie.Expires = ticket.Expires;
if(FormsAuthentication.CookieDomain!=null)
cookie.Domain = FormsAuthentication.CookieDomain;
cookie.Path = FormsAuthentication.CookiePath;
Response.Cookies.Add(cookie);
}
授权用户
Global.asax类允许挂接到授权
public void Application_Authorize(object sender, EventArgs e){
var user = Context.User;
if(user == null)
return;
FormsIdentity formsIdentity = user.Identity as FormsIdentity;
long userSessionID = long.Parse(formsIdentity.UserData);
string cacheKey = "US-" + userSessionID;
// caching to improve performance
object result = HttpRuntime.Cache[cacheKey];
if(result!=null){
// if we had cached that user is alright, we return..
return;
}
// hit the database and check if session is alright
// If user has logged out, then all UserSessions should have been
// deleted for this user
UserSession session = db.UserSessions
.FirstOrDefault(x=>x.UserSessionID == userSessionID);
if(session != null){
// update session and mark last date
// this helps you in tracking and you
// can also delete sessions which were not
// updated since long time...
session.DateUpdated = DateTime.UtcNow;
db.SaveChanges();
// ok user is good to login
HttpRuntime.Cache.Add(cacheKey, "OK",
// set expiration for 5 mins
DateTime.UtcNow.AddMinutes(5)..)
// I am setting cache for 5 mins to avoid
// hitting database for all session validation
return;
}
// ok validation is wrong....
throw new UnauthorizedException("Access denied");
}
当用户注销时
public void Logout(){
// get the ticket..
FormsIdentity f = Context.User.Identity as FormsIdentity;
long sessionID = long.Parse(f.UserData);
var session = db.UserSessions.First(x=>x.UserSessionID = sessionID);
db.UserSession.Remove(session);
db.SaveChanges();
FormsAuthentication.Signout();
}
**当用户更改密码或用户被阻止或用户被删除时**
public void ChangePassword(){
// get the ticket..
FormsIdentity f = Context.User.Identity as FormsIdentity;
long sessionID = long.Parse(f.UserData);
var session = db.UserSessions.First(x=>x.UserSessionID = sessionID);
// delete all sessions for the same user id
// this will force user to relogin on all other
// devices...
db.Database.ExecuteSql(
"DELETE FROM UserSessions WHERE UserID=@UserID",
new SqlParameter("@UserID", session.UserID));
}
当您重复相同的请求数据时,会话重播攻击会发生
一个非常简单的解决方案是使用一次性密码(OTP)。也就是说,将您的会话链接到OTP。一旦收到请求,请使OTP无效。是的,类似的解决方案肯定会更彻底,只是令人惊讶的是,它不是ASP.NET内置的,或者我找不到任何为您处理它的知名库(即使它确实需要外部存储)。我以为ASP.NET成员资格就可以了,但我不这么认为,你必须自己建立这个,在用户会话中,我们还存储设备、用户代理等,这有利于分析和记录。@AkashKava,这是一个很好的解决方案,当用户在会话结束前注销时,你可以处理重播攻击。但是,w关于以下场景:
如果黑客在用户登录时以某种方式窃取了表单身份验证cookie,然后在实际用户会话未过期时使用从另一台笔记本电脑窃取的cookie进行模拟。您的代码将允许在第二种场景中访问黑客。我是对的还是我失踪了有什么?@Sunil,世界上没有办法阻止窃取表单身份验证cookie。因为cookie是我们唯一的秘密。谷歌/微软等尝试根据IP地址/浏览器用户代理识别GPS以识别黑客,但如果黑客使用同一IP子网和同一用户代理,则无法识别同一网络上的黑客。你可以延迟UserSession,并提供更多详细信息和验证。@AkashKava,我同意。我认为只能尽量减少黑客攻击,但在使用cookie进行身份验证时,不可能想出防弹解决方案。我将在表单身份验证cookie中存储guid,并存储(用户名、guid)在web服务器上的本地SQLite数据库中。对于AuthenticateRequest事件中的每个请求,我从SQLite数据库中获取guid,并将其与authentication coo中的guid进行匹配