ASP.NET MVC Tip #46 – 勿使用Delete链接,会造成安全漏洞
来源:互联网 发布:吉吉写作软件mac 编辑:程序博客网 时间:2024/06/06 22:16
看到篇文章,里面说了个不知道的事儿,很粗糙生硬的翻了一下,记在这里,回头研究。
路过的朋友,如感兴趣,请直接看原文,以免耽误了您的时间。
原文ASP.NET MVC Tip #46 – Don’t use Delete Links because they create Security Holes
正文
我创建了一个 ASP.NET MVC 示例程序, 打算公布在 http://ww.ASP.net/mvc . 当 ASP.NET MVC 专题小组对该程序进行代码审查时 , 一个惊人的缺陷显现出来.
该程序极其简单。它包含了一个呈现数据库记录列表的视图。每条记录下面是一个Edit链接和一个Delete链接(见图1)。很标准的东西。或者,所以我想...
图1-- 一个数据库记录表格
这里就是缺陷。你不应该用链接来删除一条记录。使用Delete链接就打开了一个安全漏洞。
安全缺陷
某人可以发一封包含一个image的邮件给你。该image通过如下标签被嵌入到邮件中:
<img src=”http://www.theApp.com/Home/Delete/23” _fcksavedurl=””http://www.theApp.com/Home/Delete/23”” />
注意,src属性指向了Home控制器类的Delete() 方法。打开邮件(并且允许邮件客户端显示image)将在无警告的情况下删除23号记录。这是不好的。这是安全漏洞。
我曾经遇到过这种安全问题,但没有多想。REST纯粹主义者会捍卫Get请求不应该改变程序状态的想法。换句话说,执行Get请求应该是安全的,无副作用的。
比如,您不会希望搜索引擎在抓取您网站的时候删除您程序中的所有记录。执行Get请求不应该对您的程序有持久性影响。
删除一条记录的时候,适当的Http操作是 HTTP DELETE。HTTP 协议支持如下HTTP操作:
.OPITIONS - 返回可用的通信可选项的信息(幂等)。
.GET - 返回请求的任何信息(幂等)。
.HEAD - 与 GET 执行同样的操作,只是没有消息体(幂等)。
.POST - 发布新信息或更新已有信息(非幂等)。
.PUT - 发布新信息或更新已有信息(幂等)。
.DELETE - 删除信息(幂等)。
.TRACE - 执行一个消息循环 (幂等)。
.CONNECT - 用于SSL通道。
这些操作被定义为HTTP 1.1 标准的一部分,您可以在这里http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html看到.
注意,对 HTTP POST 和 HTTP PUT 的描述是相同的。为了解 POST 和 PUT 的区别,你需要明白幂等的含义。一个幂等操作无论执行多少次,都有相同的结果。比如,您执行一个POST操作来创建一条新的数据库记录,那么您每次都可以创建一条新的数据库记录。POST操作是非幂等的,因为每次操作的执行都会对您的程序有不同影响。
另一方面,如果您执行PUT操作,那么操作的执行都必须创建相同的数据库记录。PUT 操作是幂等的,应为执行PUT操作1000次与执行1次是一样的。
注意,HTTP DELETE 操作也是幂等的。多次执行HTTP DELETE 操作对您程序的影响应该是相同的。比如,请求 /Home/Delete/23 应该删除23号数据库记录,而不是其他记录,无论请求执行多少次。
HTML 只支持 GET和POST
所以,删除一条数据库记录时,恰当的做法是执行HTTP DELETE操作。执行一个HTTP DELETE操作不会打开安全漏洞,且不违背REST原则。
不幸的是,标准的HTML不支持GET、POST外的其他操作。链接总是执行GET操作,表单能执行GET或POST操作。HTML不支持其他的HTTP操作。
根据HTML 3.1 规范,HTML只支持GET和POST。参见http://www.w3.org/TR/REC-html32.html#form。此外,IE只支持GET和POST。参见http://msdn.microsoft.com/en-us/library/ms534167(VS.85).aspx。
执行 Ajax Deletes
如果您想超越标准HTML,您可以利用Ajax来执行HTTP DELETE操作。XmlHttpRequest对象支持任何HTTP操作。因此,如果您愿意您的程序依赖于Javascript,您可以以正确的方式做一切。
清单1中的Home 控制器包含Index()和Delete()方法。Index()方法从Movies数据库返回所有的movies,Delete()方法通过特定的Id删除特定的movie(此控制器使用Entity Framework)。
清单1 - ControllersHomeController.cs
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using Tip46.Models; namespace Tip46.Controllers{ [HandleError] public class HomeController : Controller { private MoviesDBEntities _entities = new MoviesDBEntities(); public ActionResult Index() { ViewData.Model = _entities.MovieSet.ToList(); return View(); } [AcceptVerbs(HttpVerbs.Delete)] public ActionResult Delete(int id) { var movieToDelete = (from m in _entities.MovieSet where m.Id == id select m).FirstOrDefault(); _entities.DeleteObject(movieToDelete); _entities.SaveChanges(); return RedirectToAction("Index"); } }}
注意,Delete()方法声明了AcceptVerbs特性。Delete()方法只能被HTTP DELETE操作调用。
清单2中的Index视图通过HTML table显示来自Movies数据库表中的数据。每个movie记录下呈现一个Delete链接。
清单2 - ViewsHomeIndex.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<Tip46.Models.Movie>>" %><asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server"> <script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script> <script type="text/javascript"> function deleteRecord(recordId) { // Perform delete var action = "/Home/Delete/" + recordId; var request = new Sys.Net.WebRequest(); request.set_httpVerb("DELETE"); request.set_url(action); request.add_completed(deleteCompleted); request.invoke(); } function deleteCompleted() { // Reload page window.location.reload(); } </script> <h2>Index</h2> <table> <% foreach (var item in Model) { %> <tr> <td> <%-- Ajax Delete --%> <a onclick="deleteRecord(<%= item.Id %>)" href="JavaScript:void(0)">Delete</a> <%-- GET Delete: Security Hole <%= Html.ActionLink("Delete", "Delete", new { id=item.Id })%>--%> </td> <td> <%= Html.Encode(item.Id) %> </td> <td> <%= Html.Encode(item.Title) %> </td> <td> <%= Html.Encode(item.Director) %> </td> <td> <%= Html.Encode(item.DateReleased) %> </td> </tr> <% } %> </table> </asp:Content>
Delete通过Ajax调用执行。Delete链接调用Javascript deleteRecord()函数。该函数使用 Microsoft ASP.NET AJAX WebRequest 对象来执行Ajax调用。该WebRequest对象执行HTTP DELETE操作。
Delete操作执行完毕后,Javascript deleteCompleted()方法被调用。该方法重新加载了当前页面(这里将来会有一个更优雅的方式是使用下一个ASP.NET Ajax版本带来的ASP.NET Ajax模板功能,那样的话就只更新表格,而不用重新加载整个页面了)。
图2 - Index 视图
但是,我不想依赖于Javascript
很多开发人员不想自己的网站依赖于Javacript。回句话说,他们希望Javascript被关闭时,他们的网站一样可以工作。他们的需求是有些合理性的。不是所有移动设备都支持Javascript(尽管大多数做到了),并且Javascript还有可访问性问题(尽管Aria应该修正这些可访问性问题)。
如果您想自己的网站在Javascript被禁用时能工作,那么删除记录时,不能执行HTTP DELETE操作。而应该执行HTTP POST操作,HTTP POST不会像HTTP GET那样暴露出安全漏洞。
您可以使用AcceptVerbs特性来防止控制器动作被HTTP POST之外的操作调用。所以, Delete()动作看起来应该是这样的:
[AcceptVerbs(HttpVerbs.Post)]public ActionResult Delete(int id){ var movieToDelete = (from m in _entities.MovieSet where m.Id == id select m).FirstOrDefault(); _entities.DeleteObject(movieToDelete); _entities.SaveChanges(); return RedirectToAction("Index");}
不幸的是,通过标准HTML来执行HTTP POST操作的唯一途径是使用<form>标签。并且,您必须还用一个<input type="submit">,<input type="image">,或者<input type="button">标签来为删除记录创建一个按钮。
这里最好的选择是<input type="image">。那样的话,您可以在展示数据库记录表格时,使得Edit和Delete链接看起来一样。因为我不想我的示例程序依赖于Javascript,所以我打算采用此方式。
清单3中是无Javascript依赖的Index视图。
清单3 - ViewsHomeIndex.aspx (无 JavaScript)
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<Tip46.Models.Movie>>" %><asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server"><h2>Index</h2> <table> <% foreach (var item in Model) { %> <tr> <td> <a href='<%= Url.Action("Edit", "Home", new { id = item.Id })%>'><img src="Content/Edit.png" alt="edit" border="0" /></a> </td> <td> <% using (Html.BeginForm("Delete", "Home", new { id = item.Id })) { %> <input type="image" src="Content/Delete.png" /> <% } %> </td> <td> <%= Html.Encode(item.Id) %> </td> <td> <%= Html.Encode(item.Title) %> </td> <td> <%= Html.Encode(item.Director) %> </td> <td> <%= Html.Encode(item.DateReleased) %> </td> </tr> <% } %> </table> <p> <%= Html.ActionLink("Create New", "Create") %> </p> </asp:Content>
我从Visual Studio 图片库中获取了为Edit和Delete链接使用的图片(见图3)。您可以在您硬盘的这个位置拿到这些图片集:
C:Program FilesMicrosoft Visual Studio 9.0Common7VS2008ImageLibrary
图3 - 为Edit和Delete使用图片
为使图片正确对齐,我为表格单元格增加了垂直对齐的样式。我使用了下面的样式:
table{ border-collapse:collapse;} td{ vertical-align:top; padding:10px; border-bottom: solid 1px black;}
结论
不要使用Delete链接来删除数据库记录。潜在的,可能有人在您未知情的情况下通过执行GET请求来删除。
最好的选择是使用Javascript来执行HTTP DELETE 操作。使用Javascript能让您避开安全漏洞。使用Javascript可以让您最终HTTP协议的语义。
如果您不想您的程序依赖于Javascript,那第二个最好的选择执行HTTP POST来替代HTTP DELETE。执行HTML POST 需要您使用HTML表单。这个是丑陋的,然而,您可以通过使用<input type="image">并添加样式表来改进外观。
- ASP.NET MVC Tip #46 – 勿使用Delete链接,会造成安全漏洞
- asp.net MVC中的tip
- ASP.NET MVC Tip #7 – 使用Html.Encode避免JavaScript注入攻击
- ASP.NET MVC - Tip: System.Web.Mvc.dll引用
- Asp.net mvc 在使用 jquery ajax Delete 时 404 Not found
- ASP.NET MVC HandleErrorAttribute 和 远程链接
- ASP.NET虚拟主机安全漏洞解决方案
- ASP.NET虚拟主机安全漏洞解决方案
- ASP.NET虚拟主机安全漏洞解决方案
- ASP.NET虚拟主机安全漏洞解决方案
- ASP.NET虚拟主机安全漏洞解决方案
- ASP.NET虚拟主机安全漏洞解决方案
- ASP.NET虚拟主机安全漏洞解决方案
- Tip #7 使用ASP.NET Ajax的trace
- ASP.NET MVC 3 (Implementing Edit, Details, and Delete Views)
- ASP.NET MVC 5 - 查询Details和Delete方法
- ASP.NET MVC - how to make users confirm the delete
- ASP.NET MVC 5 - 查询Details和Delete方法
- mvn 创建项目
- C# 获取文件大小,创建时间,文件信息,FileInfo类的属性表
- crash 调试vmcore
- TinyP2P如何运行 How tinyp2p works!
- asp.net中form的Post和Get方法详讲
- ASP.NET MVC Tip #46 – 勿使用Delete链接,会造成安全漏洞
- 状态模式(State Pattern)
- 小心你的网站让百度蜘蛛抓取不到
- poj 1093 Formatting Text (DP)
- Android学习笔记:Activity-Spinner
- C语言中源文件和头文件的那些事
- 开源爬虫larbin分析
- oracle中job的执行间隔的设置
- DataTable输出到Excel