在页面之间传递数据的最佳实践

时间:2010-07-08 20:17:14

标签: asp.net encryption query-string webforms

问题

在我们在项目之间重用的堆栈中,我们在会话中放置了一些过多的数据,用于在页面之间传递数据。这在理论上是好的,因为它可以防止篡改,重放攻击等,但它会产生尽可能多的问题。

会话丢失本身就是一个问题,尽管它通过实现会话状态服务器(或使用SQL Server)来处理 。更重要的是,使后退按钮正常工作非常棘手,而且创建一种情况也是额外的工作,用户可以在三个选项卡中打开相同的屏幕来处理不同的记录。

这只是冰山一角。

大多数问题都有解决方法,但是当我磨砺掉时,所有这些摩擦让我觉得使用session在页面之间传递数据是错误的方向。

我真正想要做的是提出一个最佳实践,我的商店可以一直使用它来在页面之间传递数据,然后,对于新的应用程序,替换当前依赖于Session的堆栈的关键部分。

如果最终的解决方案没有导致大量的样板管道代码,那也很好。

提议的解决方案

会话

如上所述,严重依赖Session 似乎似乎是一个好主意,但它打破了后退按钮并导致其他一些问题。

可能有办法解决所有问题,但似乎还有很多额外的工作。

使用会话非常好的一件事是篡改不是问题。与通过未加密的QueryString传递所有内容相比,您最终编写的守护代码更少。

跨页面发布

事实上,我几乎没有考虑过这个选择。我有一个问题,它如何使页面紧密耦合 - 如果我开始做PreviousPage.FindControl(“SomeTextBox”),这似乎是一个维护问题,如果我想从另一个页面可能没有到达此页面一个名为SomeTextBox的控件。

它似乎也在其他方面受到限制。也许我想通过链接访问该页面,例如。

查询字符串

我现在正倾向于这种策略,就像在过去一样。但我可能希望我的QueryString被加密以使其更难以篡改,我也想处理重放攻击的问题。

来自Rolla的4个人,there's an article about this

但是,应该可以创建一个HttpModule来处理所有这些并从页面中删除所有加密香肠。果然,Mads Kristensen has an article where he released one.然而,这些评论听起来似乎有极其常见的情况。

其他选项

当然,这不是对选项的详尽看法,而是我正在考虑的主要选项。 This link包含更完整的列表。我没有提到的那些如Cookies和Cache不适合在页面之间传递数据。

结束......

那么,您如何处理页面间传递数据的问题?你需要解决什么隐藏的陷阱,是否有任何预先存在的工具可以完美地解决它们? 你觉得你有一个你完全满意的解决方案吗?

提前致谢!

更新:以防万一我不够清楚,通过“在页面之间传递数据”我正在讨论,例如,将CustomerID密钥从CustomerSearch.aspx页面传递给Customers.aspx,客户将被打开并进行编辑。

6 个答案:

答案 0 :(得分:41)

首先,您处理的问题与在无状态环境中处理状态有关。您所遇到的困难并不是新的,这可能是使Web开发比Windows开发或可执行文件开发更难的事情之一。

关于Web开发,据我所知,您有五种选择来处理特定于用户的状态,这些状态可以全部相互结合使用。你会发现没有一个解决方案适用于所有事情。相反,您需要确定何时使用每个解决方案:

  • 查询字符串 - 查询字符串适用于将指针传递给数据(例如主键值)或状态值。即使由于重放而加密,也不应假定查询字符串本身是安全的。此外,某些浏览器对网址的长度有限制。但是,查询字符串具有一些优点,例如可以将它们加入书签并通过电子邮件发送给人们,如果不与其他任何内容一起使用,则本质上是无状态的。

  • Cookie - Cookie适用于为特定用户存储极少量的信息。问题是cookie也有一个大小限制,之后它会简单地截断数据,因此你必须小心将自定义数据放入cookie中。此外,用户可以杀死cookie或停止使用cookie(尽管这也会阻止使用标准Session)。与查询字符串类似,除非数据很小,否则cookie对于指向数据的指针比对数据本身更好。

  • 表单数据 - 表单数据可能需要相当多的信息,但是会以发布时间为代价,在某些情况下会重新加载时间。 ASP.NET的ViewState使用隐藏的表单变量来维护信息。使用ViewState之类的页面之间传递数据的优点是可以使用后退按钮更好地工作,但可以轻松创建巨大的页面,从而减慢用户的体验。一般情况下,ASP.NET模型不适用于跨页面发布(尽管可能),但可以在帖子上工作,返回同一页面并从那里导航到下一页。

  • 会话 - 会话适用于与用户正在进行的流程或一般设置相关的信息。您可以以服务器内存或数据库加载时间为代价将相当多的信息存储到会话中。从概念上讲,Session通过从内存或从状态服务器一次性加载用户的整个数据来工作。这意味着,如果您拥有非常大的数据集,则可能不希望将其置于会话中。会话可以创建一些后退按钮问题,这些问题必须与用户实际尝试完成的内容进行权衡。通常,您会发现后退按钮可能是Web开发人员的祸根。

  • 数据库 - 最后一个解决方案(也可以与其他人一起使用)是您将数据存储在数据库中的适当模式中,并带有指示项目状态的列。例如,如果您正在处理订单的创建,则可以将订单存储在订单表中,并使用“状态”列确定订单是否为真实订单。您可以将订单标识符存储在查询字符串或会话中。该网站将继续将数据写入表中以更新各个部分和子项,直到最终用户能够声明它们已完成并且订单的状态被标记为真实订单。这会使报告和查询复杂化,因为它们都需要区分“真实”项目和正在处理的项目。

您之后链接中提到的其中一项是Application Cache。我不认为这是用户特定的,因为它是应用程序范围的。 (它显然可能是用户特定的,但我也不建议这样做)。除了将数据传递给处理程序或模块之外,我从来没有在HttpContext中存储数据,但我怀疑它与上述解决方案有什么不同。

一般来说,没有一种解决方案可以统治它们。最好的方法是在每个页面上假设用户可以从任何地方导航到该页面(而不是假设他们通过使用另一页面上的链接到达那里)。如果你这样做,后退按钮问题变得更容易处理(尽管仍然很痛苦)。在我的开发中,我广泛使用前四个,偶尔在需要时使用最后的解决方案。

答案 1 :(得分:9)

好的,所以我想用这个来补充我的答案;对于刚刚开始新人的人来说,托马斯显然拥有最准确,最全面的答案。这个答案根本不是一脉相承的。我的答案来自“业务开发人员”的立场。众所周知;有时花钱重新写一些已经存在并且“有效”的东西是不可行的......至少不是一次性完成。有时最好实施一个解决方案,让您随着时间的推移迁移到更好的替代方案。

我唯一想说托马斯遗失的是;客户端javascript状态。在我工作的地方,我们发现客户越来越期望“Web 2.0”类型的应用程序。我们还发现这些类型的应用程序通常会带来更高的用户满意度。通过一些练习,以及一些非常棒的javascript库(如jQuery(我们甚至开始使用GWT并发现它很棒)的帮助,与WCF中实现的基于JSON的REST服务进行通信可能是微不足道的。这种方法还提供了一种非常好的方式来开始转向基于SOA的体系结构,并清理UI和业务逻辑的分离。

但我离题了。

听起来好像你已经拥有了一个应用程序,并且你已经扩展了ASP.NET内置会话状态管理的限制。所以......这是我的建议(假设您已经尝试过ASP.NET的进程外会话管理,它比进程内/机上会话管理更好地扩展,听起来就像你提到的那样,因为你提到过它); NCACHE。

NCache为您提供ASP.NET会话管理选项的“插入式”替换。它实现起来非常简单,并且可以“帮助”您的应用程序,而不仅仅是让您完成 - 无需立即重构现有代码库。

您可以利用额外的时间和金钱,通过将新开发重点放在具有直接业务价值的事物上来开始减少技术债务 - 使用新方法(例如其他答案中提供的任何替代方案,或我的方法)。

只是我的想法。

答案 2 :(得分:8)

几个月后,我想我会用最后的技术来更新这个问题,因为它已经很好用了。

在玩更多涉及的会话状态处理(导致大量折断按钮等)后,我最终滚动自己的代码来处理加密的QueryStrings。这是一个巨大的胜利 - 我的所有问题场景(后退按钮,同时打开多个选项卡,丢失会话状态等)都得到了解决,并且复杂性很小,因为使用非常熟悉。

对于所有事情来说,这仍然不是一个神奇的子弹,但我认为这对你遇到的大约90%的场景都有好处。

<强>详情

我构建了一个名为CorePage的类,它继承自Page。它有一些名为SecureRequest和SecureRedirect的方法。

所以你可以打电话:

 SecureRedirect(String.Format("Orders.aspx?ClientID={0}&OrderID={1}, ClientID, OrderID)

CorePage解析出QueryString并将其加密为名为CoreSecure的QueryString变量。所以实际的请求看起来像这样:

Orders.aspx?CoreSecure = 1IHXaPzUCYrdmWPkkkuThEes%2fIs4l6grKaznFGAeDDI%3D

如果可用,当前登录的UserID将添加到加密密钥中,因此重放攻击不会成为问题。

从那里,您可以致电:

X = SecureRequest("ClientID")

<强>结论

使用熟悉的语法,一切都能无缝地工作。

在过去几个月中,我还调整了此代码以处理边缘情况,例如触发下载的超链接 - 有时您需要在具有安全QueryString的客户端上生成超链接。这非常有效。

如果您想查看此代码,请告诉我,我会将其放在某处。

最后一个想法:接受我自己对其他人在这里提出的一些非常有思想的帖子的答案很奇怪,但这似乎确实是我问题的最终答案。感谢所有帮助我的人。

答案 3 :(得分:5)

完成上述所有方案和答案之后,此链接Data pasing methods我的最终建议是:

COOKIES

  • 加密[userId's]
  • ENCRYPT [productId]
  • ENCRYPT [xyzIds ..]
  • ENCRYPT [等]

DATABASE

  • 数据集BY COOKIE ID
  • 数据表BY COOKIE ID
  • 所有其他大块BY COOKIE ID

我的建议还取决于以下统计信息和此链接详情Data pasing methods

enter image description here

答案 4 :(得分:3)

我永远不会这样做。我从未在数据库中存储所有会话数据,根据用户cookie加载它时遇到任何问题。就任何事情而言,这是一个会话,但我保持对它的控制。不要放弃对Web服务器的会话数据的控制......

通过一些工作,您可以支持子会话,并允许在不同的选项卡/窗口中进行多任务处理。

答案 5 :(得分:2)

作为一个起点,我发现使用关键数据元素(例如客户ID)最好放入查询字符串进行处理。您可以轻松跟踪/过滤来自这些元素的不良数据,还可以与电子邮件或其他相关站点/应用程序进行某些集成。

在以前的应用程序中,查看员工或涉及他们的请求记录的唯一方法是登录应用程序,搜索员工或搜索最近的记录以查找相关记录。当相关部门的某个人为了审计目的而需要对记录进行简单的查看时,这就变成了问题并且耗费了大量时间。

在重写中,我同时创建了员工ID,并通过“ViewEmployee.aspx?Id = XXX”和“ViewRequest.aspx?Id = XXX”的基本URL请求ID。应用程序设置为A)过滤掉坏ID并且B)在允许用户访问这些页面之前对用户进行身份验证和授权。这使主要应用程序用户能够做的是通过电子邮件中的URL向审计员发送简单的电子邮件。当他们处于匆忙之中时,他们处于批量处理时间,他们只需点击一个URL列表并进行适当的处​​理。

其他与会话相关的数据,例如修改日期和维护用户与应用程序交互的“状态”变得有点复杂,但希望这为您提供了一个起步点。