[翻译] ASP.NET MVC Tip #10 - 防止URL操作攻击

原文地址:http://weblogs.asp.net/stephenwalther/archive/2008/06/26/prevent-url-manipulation-attacks.aspx

摘要:在这个Tip中,Stephen Walther介绍了黑客如何通过操作URL从ASP.NET MVC网站中窃取敏感信息。Stephen Walther还探讨了如何构建单元测试来防止这类攻击。

在一个网站上,黑客可以通过URL操作攻击来简单地访问其他用户的数据。如果你通过记录的ID来获取数据记录,而又没有针对每个数据库请求检查是否是由正确的用户发起的请求,则任何人都可以读取其他用户的数据库记录。

ASP.NET MVC框架的一个优势在于,它可以暴露出直观的URL。不幸的是,这个优势也可能是危险的。黑客可以通过操作URL来从一个ASP.NET MVC网站中窃取数据。

我们来看一个简单的示例程序,它将面临URL操作攻击。假设你正在为医院创建网站。医院的病人可以登录网站来查看他们的病历。这个应用程序有四个视图。

当病人第一次向该应用程序发起请求时,他会看到图1所示的视图。该视图包含一个链接,病人单击这个链接可以看到他的病历。

图1 - Index.aspx

如果病人尚未登录,他将被重定向到如图2所示的Login视图。病人必须输入正确的凭证才能查看他的病历记录(凭证存放在Web.config中)。

图2 - Login.aspx

通过验证后,病人会看到图3所示的Summary视图。该视图显示了一个列表,给出了一组指向详细病历记录的链接。数据记录的获取是根据病人的用户名进行的。

图3 - Summary.aspx

最后,如果病人单击了一个病历记录链接,他就会看到如图4所示的Details视图。该视图显示了一条单独的记录。

图4 - Details.aspx

这里就是黑客能够通过URL操作攻击来窃取病人数据的地方了。注意图4中用于为Phil(病人名)获取详细数据的URL。该URL看上去是这样的:

http://localhost:48583/MedicalHistory/Details/6

该URL非常直观。请求这个URL可以获取数据库中Id是6的数据。由于这个URL是如此的直观,你可以很容易将其修改为另外一个编号——

http://localhost:48583/MedicalHistory/Details/4

修改了URL之后,Phil可以看到Rob的私人病历记录,如图5所示。这恐怕不好吧。

图5 - Phil可以看到Rob的私人记录

清单1列出了用于返回Summary和Details视图的控制器。这样编写控制器导致该医院网站为URL操作攻击敞开了大门。

清单1 - MedicalHistoryCotroller.cs

清单1 - MedicalHistoryCotroller.cs

MedicalHistoryController暴露了两个操作,名字分别是Summary和Details。两个操作都从MedicalHistory数据表中获取数据。

Summary操作并没有为URL操作攻击敞开大门。在获取数据库记录时,记录是针对当前病人的用户名检查过的。记录是通过下面的LINQ to SQL查询获取的:

var records = from r in _db.MedicalHistories
              
where r.PatientUserName == User.Identity.Name
              orderby r.EntryDate
              select 
new SummaryMedicalHistory {Id=r.Id, EntryDate=r.EntryDate, Subject=r.Subject};

不好的查询出现在Details操作中。当Details操作获取一条特定的数据库记录时,只使用了记录的Id:

var record = _db.MedicalHistories.SingleOrDefault(r => r.Id == id);

由于这样编写了查询,黑客只需简单地修改传给Details操作的Id就能看到其他病人的病历记录。

下面是编写查询的正确方法:

var record = _db.MedicalHistories.SingleOrDefault(r => r.Id == id && 
    r.PatientUserName 
== User.Identity.Name);

在这个修改过的查询中,只有同时匹配指定Id和当前病人用户名的记录会返回。这个数据库查询是安全的。

针对URL操作攻击创建单元测试

在构建ASP.NET MVC网站时很容易出现错误,使得你自己被暴露在URL操作攻击下。如何防止这种错误?编写单元测试是一种办法。

考虑清单2中的单元测试。

清单2 - MedicalHistoryControllerTest.cs

清单2 - MedicalHistoryControllerTest.cs

该单元测试允许你检查Details操作是否为URL操作攻击敞开了大门。这里是测试的工作流程。

首先,我创建了一个DataContext来呈现测试数据库。测试数据库中包含了两位假想病人(Phil和Rob)的病历记录。测试数据库是产品数据库的副本,但其中包含的是假数据。

接下来,我mock了ControllerContext。我必须mock一个ControllerContext,因为我想在调用Details操作时假装成是Phil。我希望测试当我被验证为是用户Phil时,是否能访问Rob的病历记录。

我是用了一个名为GetMockUserContext()的方法来mock这个ControllerContext。该法官法使用了名为Moq的Mock Object Framework。有关Moq的更多信息,请阅读下面这篇博客文章:

http://weblogs.asp.net/stephenwalther/archive/2008/06/11/tdd-introduction-to-moq.aspx

接下来,从测试数据库返回了一条Rob的病历记录。Rob的病历记录的Id是由Phil用户传递给Details操作的。

最后,会根据Details操作返回的记录是否为null产生一个断言。如果记录不是null,则测试会失败,Rob的记录可能会被Phil偷走。

小结

要小心URL操作攻击。如果你需要保护敏感数据——如病历记录和信用卡号——你需要特别小心这类攻击。在这个Tip中,我介绍了一种让你的网站更加安全的途径。请利用单元测试来针对URL操作攻击测试你的控制器操作。

如果你想试验本文中的代码,请单击下面的链接下载源代码。你需要修改MedicalHistoryControllTest文件中testDBPath的值,使其对应你机器上的测试用病历数据库。

此处下载源代码:http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip10/Tip10.zip

-----

广告:.NET正则表达式库,http://regex-lib.net

posted @ 2008-07-17 12:35  Anders Liu  阅读(3145)  评论(11编辑  收藏  举报