Asp.net 会话越界/混淆
Posted
技术标签:
【中文标题】Asp.net 会话越界/混淆【英文标题】:Asp.net Sessions Getting Crossed / Mixed Up 【发布时间】:2011-07-31 06:29:47 【问题描述】:几周前,我们的一位客户联系我们说,有时当他创建活动时,它会以其他人的名义创建!
我们进行了一些故障排除,但找不到任何东西。我们要求用户在下次遇到这些问题时与我们联系。他确实联系了我们,我们得以与他进行了一次会面,并亲眼看到了这个问题。
不仅是活动,他在应用程序中被识别为其他人。他可以访问其他人应该可以访问的所有内容。那时我们意识到我们遇到了一个会话混淆问题。
关于我们的代码的一点点: 像任何其他应用程序一样,我们有一个简单的登录页面,用户输入电子邮件和密码,然后我们根据我们的数据库对它们进行身份验证,如果它们有效,我们调用 FormsAuthentication.SetAuthCookie() 将当前用户 ID 保存在 cookie 中,然后让他进入。
BL.User currentUser = BL.User.Authenticate(txtUsername.Text, txtPassword.Text);
if (currentUser != null)
this.Session["NumberOfLoginTried"] = "0";
FormsAuthentication.SetAuthCookie(currentUser.UserID.ToString(), chRememberMe.Checked);
Response.Redirect(FormsAuthentication.GetRedirectUrl(currentUser.UserID.ToString(), false));
我们还使用以下代码在我们的应用程序中获取登录用户 ID(当前用户)。
public static int GetCurrentUserID()
int userID = -1;
int.TryParse(HttpContext.Current.User.Identity.Name, out userID);
return userID;
是的,我们做了功课并用谷歌搜索了以下两个链接:
http://lionsden.co.il/codeden/?p=446ASP.NET Session Mix-up using StateServer (SCARY!)
我们为 .aspx 和 .ascx 文件禁用了内核模式缓存和用户模式缓存,这种情况仍在发生。
P.S- 该应用程序在带有 IIS 7.5 的 Windows 2008 R2 上运行。我们不使用无 cookie 会话。
【问题讨论】:
您的用户是否选中了“记住我”框?并且您的用户正在更改为哪个错误用户是否有任何模式(例如,它总是同一个用户吗?是同一位置的另一个用户(如果是的话,大提示),是另一个登录用户等)? @houda,您将会话存储在哪里? (sql, mem ?) 如果它在 sql 上,你是否每分钟运行一次自动清除脚本?另外,为什么错误出现在那个功能上?也许您还有其他一些错误。你使用 WebGarden 吗? (很多asp.net进程?) @houda 还有可能是页面存储在代理上,而你的用户看到以前用户代理存储的页面!您是否设置了“无缓存”?并且不允许代理?页面是 https 吗?您的客户是否在通过代理路由器连接到互联网的办公室? @houda,我对您的 GetCurrentUserID 例程感到困惑——您为什么希望 HttpContext.Current.User.Identity.Name 解析为 int?另外,如果 TryParse 失败,它会将 userID 设置为 0,而不是将其设置为 -1。 @Ken 这都是随机的,我找不到任何模式。 【参考方案1】:我们刚刚遇到了一个非常相似的问题,它是随机发生的,似乎不可重现。
问题出在 ASP.NET 的页面缓存机制上——在我们的例子中尤其是 <%@ OutputCache
标记。
我们曾经使用过一条线
<%@ OutputCache NoStore="true" Duration="1" %>
这基本上意味着如果两个用户在 1 秒内访问相同的页面,他们将看到相同的页面(包括另一个用户的登录用户名)。因此,如果他们刷新所述页面,他们就会得到正确的信息。
在我们的例子中,将所述行更改为
<%@ OutputCache NoStore="true" Duration="1" VaryByParam="*" %>
,在此链接中禁用 IIS 中的内核缓存 (http://lionsden.co.il/codeden/?p=446)
并将以下几行添加到相关页面的Page_Load
事件中:
Response.CacheControl = "private";
Response.ExpiresAbsolute = DateTime.Now.AddDays(-1d);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
似乎已经为我们解决了问题。希望这可以帮助遇到类似问题的其他人。
【讨论】:
我们遇到了类似的问题,但请求不一。VaryByParam="*"
是解决方案。
CacheControl = "private"
指示页面仅由用户的浏览器缓存,因此这很可能为您解决了问题。 SetCacheability(NoCache)
也没有受到伤害,因为那时不应该缓存任何东西;但这并不意味着所有服务器都表现良好。【参考方案2】:
我们遇到了同样的问题,这是由 IIS 中的 <clientCache/>
设置引起的,默认情况下无法添加 Cache-Control: private
HTTP 标头。缺少此标头意味着我们的表单身份验证 cookie 被下游代理服务器缓存!因此,当我们的网站繁忙时,突然间大量用户会突然以错误的用户身份登录!噩梦。
【讨论】:
【参考方案3】:如果完全删除<%@ OutputCache NoStore="true" Duration="1" VaryByParam="*"
(在从Master 到aspx 的行中的所有ascx 文件中!!!)防止跨会话。只加载了一个带有 outputcache 指令的 ascx,发生了跨会话。
在我的情况下,是否使用 sessionstat InProc ore StateServer 无关紧要,是否有无 cookie 或 cookie 会话。
【讨论】:
【参考方案4】:我们在我工作的公司遇到了同样的问题。我们也意识到这是由输出缓存引起的,导致将其他人的 SessionId 发送给错误的人。
我们现在已将以下 <caching>
元素添加到我们的 web.config 中。
<configuration>
[...]
<system.webServer>
[...]
<caching enabled="false" enableKernelCache="false">
</caching>
</system.webServer>
[..]
</configuration>
我们不能保证这会解决它,因为这个问题几乎不可能重现,但根据我们的研究,这应该可以解决。
奇怪的是,可以在 Internet 上找到描述此问题的 Microsoft 文章的链接给出了一些通用页面,好像该页面已被删除。
但是有这篇微软文章似乎描述了与 IIS 6 相同的问题:
An ASP.NET page is stored in the HTTP.sys kernel cache in IIS 6.0 when the ASP.NET page generates an HTTP header that contains a Set-Cookie response
将症状描述为:
考虑以下场景。 Microsoft ASP.NET 页面包含 指令。此外,ASP.NET 页生成一个包含 Set-Cookie 响应的 HTTP 标头。在这种情况下,ASP.NET 页存储在 Microsoft Internet 信息服务 (IIS) 6.0 中的 HTTP 协议堆栈 (HTTP.sys) 内核缓存中。因此,访问同一页面的多个用户可能会收到相同的 cookie。
更新
我在 Microsoft Premier Developer 博客上发现了这篇非常好的文章,它解释了很多:
ASP.Net Session Swapping – Why it happens and what can be done about it?
【讨论】:
【参考方案5】:因为你们都禁用了内核模式缓存,我想指出一些其他的想法。
1) 要正确使用HttpContext.Current.User.Identity.Name
,您首先需要使用User.Identity.IsAuthenticated
验证您的用户是否已登录
2) 在这一点上Session.Add("CurrentUser", currentUser);
你实际尝试保存什么?
现在我认为问题出在缓存上。这些页面存储在您的用户之间的某个位置,并且一个与另一个混合在一起。您可以将一些标头用于您的页面以避免中间代理计算机上的缓存。
Response.Cache.SetExpires(DateTime.UtcNow.AddYears(-2));
Response.Cache.SetNoStore();
Response.Cache.SetValidUntilExpires(false);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.ExpiresAbsolute = DateTime.Now.Subtract(new TimeSpan(1, 0, 0, 0));
Response.Expires = 0;
Response.CacheControl = "no-cache";
Response.AppendHeader("Pragma", "no-cache");
我还要说,如果您的页面包含您不希望在用户之间共享的数据,您需要使用安全 HTTPS 页面,并通过在 web.config 上添加 <httpCookies httpOnlyCookies="true" requireSSL="true" />
将您的 cookie 设置为仅在安全页面上可用
此外,请检查您是否将会话保存在您计划每 1 分钟运行一次清理路由的 SQL 服务器上。
为了能够找到更多信息,我建议在页面上存储一些隐藏文本,例如渲染的日期时间,可能是用户 ID 的最后 4 位,以及您可能需要的其他帮助你看看页面是否来自缓存。
【讨论】:
Session.Add("CurrentUser", currentUser) 是不再使用的旧代码行。 @Houda for the not allowed to proxy set this response that I have place on the answer here,也许你可以在网上或多或少找到,但我认为问题出在代理上办公室,只需通过此标题禁用页面上的缓存,我认为您会解决它。 @Houda 我还说如果您在 SQL 服务器上移动会话会更好地处理。至少在我的情况下,我在使用 InProc 时遇到了一些问题。 我将标题添加到所有页面。问题仍在发生,但不像以前那么严重。还有其他建议吗? 我将标题添加到所有页面。问题仍在发生,但没有以前那么严重。具有讽刺意味的是,我们在项目中经常使用 iFrame,而且您知道 iFrame 会被大量缓存;当客户端拉取所有 URL 时,我们在所有 URL 的末尾添加了一个随机字符串。还有其他建议吗?【参考方案6】:既然这似乎落入了极其神秘的问题领域,也许是时候飞跃了。
您可以停止使用 ASP.NET 会话来完全存储您的标识符。
您有很多选择可以将这些信息粘贴到何处。您可以选择将其加密到 Forms Authentication 票证的 UserData 属性中(我之前在生产中这样做过,它非常适合存储密钥、角色的 csv 甚至小型 json 对象)。通过表单身份验证票,您可以直接将信息写入您自己的 cookie。您也可以完全绕过 cookie。
如果您选择绕过 cookie,您基本上进入了无 cookie 的 ASP.NET 会话的类似领域。您有几个选择,您可以将用户标识符作为查询参数与每个 url 分开。另一种选择是创建一个 HttpModule,它将隐藏的表单输入添加到包含登录用户标识符的每个页面响应中。
如果您采用无 cookie 路径,请绝对确保您的网站不能用作 HTTP,并且每个请求都是 HTTPS。如果您使用查询参数方法,则更是如此。
【讨论】:
真正让我困惑的是,用户信息和 sessionid 都存储在 cookie 中而不是状态。基本上,当我调用 HttpContext.Current.User.Identity.Name 时,我正在访问一个 cookie。这些 cookie 是否被缓存在代理中?! 它们有可能被代理缓存了,你应该可以很容易地进行测试。这可能会让我想到将标识符直接添加到表单中作为绕过。它可能不会缓存它,当然它也可以缓存它。试图与破碎的系统作斗争总是一场失败的战斗。【参考方案7】:如果您检查输出缓存不是问题
这里已经有我的回答,但事实证明我的其他解决方案(禁用输出缓存)并没有真正解决我们的问题。
由于在问题中声明缓存已关闭,因此在我们的案例中,唯一可能产生此问题的 (AFAIK) 错误是真正的罪魁祸首:我们在 @987654323 中使用私有变量@。
由于这些是缓存的,以这种方式存储来自用户的私人/个人数据也可能导致会话混淆!
这个答案描述了我们的问题是什么以及如何解决它:
https://***.com/a/8937793/1864395
另外,我认为值得一提的是,我们能够通过同时为多个用户运行Apache JMeter 来重现该问题。这是一个非常好的工具(虽然不是真正的用户友好/直观),用于(除其他外)压力测试。 这可能是诊断会话混淆的唯一方法!
【讨论】:
以上是关于Asp.net 会话越界/混淆的主要内容,如果未能解决你的问题,请参考以下文章