作者 :Stephen Walther

防止发生 JavaScript 注入攻击和跨站点脚本攻击。 在本教程中,Stephen Walther 介绍了如何通过 HTML 编码内容轻松击败这些类型的攻击。

本教程的目的是说明如何在 ASP.NET MVC 应用程序中防止 JavaScript 注入攻击。 本教程讨论了两种保护网站免受 JavaScript 注入攻击的方法。 你将了解如何通过对显示的数据进行编码来防止 JavaScript 注入攻击。 你还将了解如何通过对接受的数据进行编码来防止 JavaScript 注入攻击。

什么是 JavaScript 注入攻击?

每当接受用户输入并重新显示用户输入时,您都打开网站以接受 JavaScript 注入攻击。 让我们来看看一个对 JavaScript 注入攻击开放的具体应用程序。

假设你创建了一个客户反馈网站, (请参阅图 1) 。 客户可以访问该网站,并输入有关其使用产品体验的反馈。 当客户提交反馈时,反馈将重新显示在反馈页上。

图 01 :客户反馈网站 ( 单击以查看全尺寸图像 )

客户反馈网站使用 controller 清单 1 中的 。 这 controller 包含两个名为 Index() Create() 的操作。

列表 1 – HomeController.vb

Public Class HomeController
     Inherits System.Web.Mvc.Controller
     Private db As New FeedbackDataContext()
     Function Index()
          Return View(db.Feedbacks)
     End Function
     Function Create(ByVal message As String)
          ' Add feedback
          Dim newFeedback As New Feedback()
          newFeedback.Message = Server.HtmlEncode(message)
          newFeedback.EntryDate = DateTime.Now
          db.Feedbacks.InsertOnSubmit(newFeedback)
          db.SubmitChanges()
          ' Redirect
          Return RedirectToAction("Index")
     End Function
End Class

方法 Index() 显示 Index 视图。 此方法通过使用 LINQ to SQL 查询) 从数据库 (检索反馈,将以前的所有客户 Index 反馈传递到视图。

方法 Create() 创建一个新的反馈项,并将其添加到数据库。 客户在窗体中输入的消息将 Create() 传递到 message 参数中的 方法。 将创建反馈项,并将消息分配给反馈项的 Message 属性。 反馈项通过 DataContext.SubmitChanges() 方法调用提交到数据库。 最后,访问者被重定向回 Index 显示所有反馈的视图。

Index 视图包含在清单 2 中。

清单 2 – Index.aspx

<%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="CustomerFeedback.Index"%>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
     <h1>Customer Feedback</h1>
          Please use the following form to enter feedback about our product.
     <form method="post" action="/Home/Create">
          <label for="message">Message:</label>
          <textarea name="message" cols="50" rows="2"></textarea>
          <br /><br />
          <input type="submit" value="Submit Feedback" />
     </form>
     <% For Each feedback As CustomerFeedback.Feedback In ViewData.Model%>
          <%=feedback.EntryDate.ToShortTimeString()%>
          <%=feedback.Message%>
     <% Next %>
</asp:Content>

视图 Index 有两个部分。 顶部包含实际的客户反馈表单。 底部部分包含 For.。循环访问所有以前的客户反馈项,并显示每个反馈项的 EntryDate 和 Message 属性。

客户反馈网站是一个简单的网站。 遗憾的是,该网站对 JavaScript 注入攻击开放。

假设在客户反馈表单中输入以下文本:

<script>alert("Boo!")</script>

此文本表示显示警报消息框的 JavaScript 脚本。 有人将此脚本提交到反馈表单后,消息“ 布!将来只要有人访问客户反馈网站,就会显示 (见图 2) 。

图 02:JavaScript 注入 (单击以查看全尺寸图像)

现在,你对 JavaScript 注入攻击的初始响应可能是冷漠的。 你可能认为 JavaScript 注入攻击只是一种 破坏 攻击。 你可能相信没有人能通过实施 JavaScript 注入攻击来做任何真正邪恶的事情。

不幸的是,黑客可以通过将 JavaScript 注入网站来做一些真正、非常邪恶的事情。 可以使用 JavaScript 注入攻击来执行跨站点脚本 (XSS) 攻击。 在跨站点脚本攻击中,你将窃取机密用户信息并将该信息发送到另一个网站。

例如,黑客可以使用 JavaScript 注入攻击从其他用户窃取浏览器 Cookie 的值。 如果敏感信息(如密码、信用卡号或社会安全号码)存储在浏览器 Cookie 中,则黑客可以使用 JavaScript 注入攻击来窃取此信息。 或者,如果用户在已受到 JavaScript 攻击的页面中包含的表单字段中输入敏感信息,则黑客可以使用注入的 JavaScript 来获取表单数据并将其发送到另一个网站。

请害怕。 认真对待 JavaScript 注入攻击并保护用户的机密信息。 在接下来的两部分中,我们将讨论两种技术,可用于保护 ASP.NET MVC 应用程序免受 JavaScript 注入攻击。

方法 1:视图中的 HTML 编码

防止 JavaScript 注入攻击的一种简单方法是在视图中重新显示数据时对网站用户输入的任何数据进行 HTML 编码。 清单 3 中更新 Index 的视图遵循此方法。

清单 3 - Index.aspx (HTML 编码)

<%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="CustomerFeedback.Index"%>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
     <h1>Customer Feedback</h1>
          Please use the following form to enter feedback about our product.
     <form method="post" action="/Home/Create">
          <label for="message">Message:</label>
          <textarea name="message" cols="50" rows="2"></textarea>
          <br /><br />
          <input type="submit" value="Submit Feedback" />
     </form>
     <% For Each feedback As CustomerFeedback.Feedback In ViewData.Model%>
          <%=feedback.EntryDate.ToShortTimeString()%>
          <%=Html.Encode(feedback.Message)%>
     <% Next %>
</asp:Content>

请注意, 的值 feedback.Message 在使用以下代码显示值之前进行了 HTML 编码:

<%=Html.Encode(feedback.Message)%>

HTML 编码字符串意味着什么? 对字符串进行 HTML 编码时,危险字符(如 <> )将替换为 HTML 实体引用,例如 &lt;&gt;。 因此,当字符串 <script>alert("Boo!")</script> 经过 HTML 编码时,它将转换为 &lt;script&gt;alert(&quot;Boo!&quot;)&lt;/script&gt;。 浏览器解释时,编码的字符串不再作为 JavaScript 脚本执行。 相反,你会得到图 3 中的无害页面。

图 03:失败的 JavaScript 攻击 (单击以查看全尺寸图像)

请注意,在 Index 清单 3 的视图中,仅对 的值 feedback.Message 进行编码。 的值 feedback.EntryDate 未编码。 只需对用户输入的数据进行编码。 由于 EntryDate 的值是在控制器中生成的,因此无需对此值进行 HTML 编码。

方法 2:控制器中的 HTML 编码

在将数据提交到数据库之前,您可以对数据进行 HTML 编码,而不是在视图中显示数据时使用 HTML 编码数据。 第二种方法适用于 controller 清单 4 中的 。

清单 4 - HomeController.cs (HTML 编码)

Public Class HomeController
     Inherits System.Web.Mvc.Controller
     Private db As New FeedbackDataContext()
     Function Index()
          Return View(db.Feedbacks)
     End Function
     Function Create(ByVal message As String)
          ' Add feedback
          Dim newFeedback As New Feedback()
          newFeedback.Message = Server.HtmlEncode(message)
          newFeedback.EntryDate = DateTime.Now
          db.Feedbacks.InsertOnSubmit(newFeedback)
          db.SubmitChanges()
          ' Redirect
          Return RedirectToAction("Index")
     End Function
End Class

请注意,在操作中 Create() 将值提交到数据库之前,Message 的值经过 HTML 编码。 当消息在视图中重新显示时,消息将进行 HTML 编码,并且不会执行在 Message 中注入的任何 JavaScript。

通常,应优先使用本教程中讨论的第一种方法,而不要使用第二种方法。 第二种方法的问题在于,最终数据库中会生成 HTML 编码数据。 换句话说,数据库数据被弄脏了有趣的字符。

为什么这样不好? 如果需要在网页以外的其他内容中显示数据库数据,则会出现问题。 例如,无法再轻松地在Windows 窗体应用程序中显示数据。

本教程的目的是让你担心 JavaScript 注入攻击的可能性。 本教程讨论了保护 ASP.NET MVC 应用程序免受 JavaScript 注入攻击的两种方法:可以在视图中对用户提交的数据进行 HTML 编码,也可以在控制器中对用户提交的数据进行 HTML 编码。