ASP.NET MVC Tip #46 – 勿使用Delete链接,会造成安全漏洞

 

          看到篇文章,里面说了个不知道的事儿,很粗糙生硬的翻了一下,记在这里,回头研究。

          路过的朋友,如感兴趣,请直接看原文,以免耽误了您的时间。

           原文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-- 一个数据库记录表格

clip_image002

        这里就是缺陷。你不应该用链接来删除一条记录。使用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 οnclick="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 视图

clip_image004

        但是,我不想依赖于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使用图片

clip_image006


        为使图片正确对齐,我为表格单元格增加了垂直对齐的样式。我使用了下面的样式:

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">并添加样式表来改进外观。


     

       

    



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值