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 上添加 &lt;httpCookies httpOnlyCookies="true" requireSSL="true" /&gt; 将您的 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 会话越界/混淆的主要内容,如果未能解决你的问题,请参考以下文章

asp.net 内容混淆

ASP.NET Identity DbContext 混淆

ASP.NET RadioButton 与名称(组名)混淆

ASP.NET身份DbContext混淆

ASP.NET 中的会话劫持对策

ASP.NET Web Api 中的 Swashbuckle 被嵌套控制器混淆