Dyl*_*son 5 asp.net security cookies session replay
好吧,ASP.NET WebForms 现在已经是旧技术了,但在可预见的未来我们将继续使用它,我只是想看看是否可以引入一个非常基本的重放攻击预防机制。基本上,我们希望确保用户明确注销后,没有人可以重放旧请求并根据授权 cookie 自动对其进行身份验证,但我们仍然希望允许不希望使用永久授权 cookie 的用户使用永久授权 cookie。重新登录每个新的浏览器会话。根据我在后一种情况下观察到的情况,如果您退出浏览器并启动新会话,则cookie 标头中没有会话 ID - 只有表单授权 cookie。在这种情况下,我们希望允许自动身份验证(至少对于 GET 请求)。在“重播”情况下,cookie 标头中有一个会话 ID - 但 ASP.NET 实际上会使用相同的 ID 自动重新创建一个新会话。幸运的是,我们可以检测到这一点(通过检查 Session.IsNewSession 和 Request.Headers["Cookie"] - 尽管不是,奇怪的是,Request.Cookies 有时会包含会话 cookie,即使它不是由客户端发送的),因此它是如果客户端发送了用户注销时已关闭/放弃的会话的会话 ID,则可以强制重新登录。
但是...如果重放攻击故意省略会话 ID,那么当授权 cookie 是永久 cookie 时,就没有真正的方法可以将其与合法的浏览器请求区分开来。在这种情况下,您至少可以阻止“POST”请求,但重放攻击可能只是先发出 GET 来建立新会话,然后再发出 POST。我真正想要的是一种方法来确定一旦用户注销,用于授权该会话的 cookie 值就不再有效 - 我想这需要在数据库中存储一些内容(很可能是当前有效的列表) cookie 值),这似乎比目前合理的努力要多。但似乎其他任何事情(例如在表单上使用隐藏的随机数字段)都容易出现一个脚本,该脚本只是模拟用户使用永久授权 cookie 重新建立新会话,在这种情况下,他们将能够确定所需的内容因此,假设我是正确的,必须将某些内容存储在服务器端以跟踪(本质上)哪些授权 cookie 值仍然有效,是否有任何已知的低占用空间/众所周知的库做这个?
(顺便说一句,现在我已经在身份验证票证上使用了 IsPersistent 标志 - 如果这是 false,即用户已明确选择仅使用每个会话 cookie,知道他们将重新登录下一个浏览器会话,那么我可以可靠地阻止重放攻击。但如果为真,我只会阻止“POST”重放,这并不能提供太多真正的保护,但至少担心更复杂的重放攻击的用户可以通过始终选择每个会话来阻止它们验证)。
即使 ASP.NET 身份验证明确表示您必须进行二次检查以确认用户是否仍然是活动的登录用户(例如,我们可以阻止用户,用户可能更改了密码),表单身份验证票证不提供针对这些事情的任何安全措施。
UserSession 与 ASP.NET MVC Session 无关,这里只是一个名称
我实施的解决方案是,
UserSessions在数据库中创建一个表UserSessionID (PK, Identity) UserID (FK) DateCreated, DateUpdated当用户登录时
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);
}
Run Code Online (Sandbox Code Playgroud)
授权用户
Global.asax 类可以挂钩到 Authorize
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");
}
Run Code Online (Sandbox Code Playgroud)
当用户注销时
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();
}
Run Code Online (Sandbox Code Playgroud)
** 当用户更改密码或用户被阻止或用户被删除时... **
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));
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3655 次 |
| 最近记录: |