六、防止JavaScript注入攻击
这篇文章主要介绍在ASP.NET MVC应用程序中如何防止JavaScript注入攻击。这篇文章讨论了两种防止JavaScript攻击的方法:
在显示数据的时候,通过使用Encoding来防止攻击
在接收到数据的时候,通过使用Encoding防止攻
一、什么是JavaScript注入攻击(原创:灰灰虫的家 http://hi.baidu.com/grayworm)
在我们接收用户输入或在页面显示用户输入的数据时,我们的网站默认是向JavaScript注入攻击敞开了大门。让我们看看我们的Web应用程序如何被JavaScript攻击。
假设我们创建了一个用户反馈的网站,用户可以访问该网站并填写产品使用的反馈信息。当用户填写完反馈信息后,程序会把用户的反馈信息显示在页面上。
![](http://hiphotos.baidu.com/grayworm/pic/item/11931382211030be0df4d208.jpg)
《图1 》
用户反馈网站的控制器代码如下所示,其中包含两个动作Index()和Create()
Listing 1 – HomeController.cs
using System;
using System.Web.Mvc;
using CustomerFeedback.Models;
namespace CustomerFeedback.Controllers
{
[HandleError]
public class HomeController : Controller
{
private FeedbackDataContext db = new FeedbackDataContext();
public ActionResult Index()
{
return View(db.Feedbacks);
}
public ActionResult Create(string message)
{
// Add feedback
var newFeedback = new Feedback();
newFeedback.Message = message;
newFeedback.EntryDate = DateTime.Now;
db.Feedbacks.InsertOnSubmit(newFeedback);
db.SubmitChanges();
// Redirect
return RedirectToAction("Index");
}
}
}
Index()方法用来向视图传递显示数据的,这个方法检索表中所有的用户反馈信息,并传递给页面显示。
Create()创建一个新的用户返回,并把它加到数据库中。用户在表单中输入的数据会以message参数被传递到Create()方法中。我们通过调用DataContext.SubmitChanges()方法把页面返回数据送到数据库中去。最后调用Index()动作,显示所有的用户反馈数据。
Index视图代码如下
Listing 2 – Index.aspx
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="CustomerFeedback.Views.Home.Index"%>
<%@ Import Namespace="CustomerFeedback.Models" %>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h1>Customer Feedback</h1>
<p>Please use the following form to enter feedback about our product.</p>
<form method="post" action="/Home/Create">
<label for="message">Message:</label>
<br />
<textarea name="message" cols="50" rows="2"></textarea>
<br /><br />
<input type="submit" value="Submit Feedback" />
</form>
<% foreach (Feedback feedback in ViewData.Model) {%>
<p>
<%=feedback.EntryDate.ToShortTimeString()%>--<%=feedback.Message%>
</p>
<% }%>
</asp:Content>
Index视图代码可以分成两部分来看待,上面的部分是用户输入的表单,下面的部分是foreach…loop循环,迭代所有的用户返馈信息,显示EntryDate和Message两个字段的信息。
用户返馈网站很简单,但这个网站很容易被JavaScript攻击。
如果用户输入的反馈内容是
<script>alert(“Boo!”)</script>
那这段文本在显示的时候会在页面上弹出一个消息框,以后任何人在访问这个网站的时候,在显示用户的反馈信息时都会弹出这个对话框。
![](http://hiphotos.baidu.com/grayworm/pic/item/6d6fed8616ba9f2266096e16.jpg)
《图2》
可能你对上面的JavaScript注入感觉没什么大不了的,好像它只能对我们的界面显示产生影响,它并不会给我们的网站的安全性还来真正可怕的影响。不幸的是,一个黑客足可以使用JavaScript攻击来给你的网站带来可怕的事情。它可以使用JavaScript注入执行Cross-Site Scripting攻击。Cross-Site Scripting攻击就是盗窃用来的机密信息并发送到别的网站上去。比如:黑客可以使用JavaScript注入攻击从客户机的Cookie中盗取敏感信息(密码、信用卡号、社保账号等),或者把其它用户表单中输入的机密数据发送到其它网站上去。
二、解决方法一:在视图中使用Html.Encode显示数据(原创:灰灰虫的家 http://hi.baidu.com/grayworm)
防止JavaScript注入攻击的一个简单方法就是当显示用户输入的数据时,使用Html.Encode()方法把数据库中的数据编码显示。我们修改后的Index视图代码如下:
Listing 3 – Index.aspx (HTML Encoded)
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="CustomerFeedback.Views.Home.Index"%>
<%@ Import Namespace="CustomerFeedback.Models" %>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h1>Customer Feedback</h1>
<p>Please use the following form to enter feedback about our product. </p>
<form method="post" action="/Home/Create">
<label for="message">Message:</label>
<br />
<textarea name="message" cols="50" rows="2"></textarea>
<br /><br />
<input type="submit" value="Submit Feedback" />
</form>
<% foreach (Feedback feedback in ViewData.Model) {%>
<p>
<%=feedback.EntryDate.ToShortTimeString()%>--<%=Html.Encode(feedback.Message)%>
</p>
<% }%>
</asp:Content>
我们注意到,在上面的代码中,我们把feedback.Message信息使用Html.Encode()方法进行了编码。
<%=Html.Encode(feedback.Message)%>
HTML Encode的意思就是,把一些危险的字符如<和>替换为<和>。所以当<script>alert("Boo!")</script>被Encode后,就会变为<script>alert("Boo!")</script>。被编码后的字符串就不再是一个可执行的JavaScript代码了,它会正常显示在页面上。如下图所示
![](http://hiphotos.baidu.com/grayworm/pic/item/e95cf8fb18a48d3e4e4aea13.jpg)
《图3》
上面的代码中我们只对feedback.Message进行了编码,而feedback.EntryDate没有进行编码。因为feedback.EntryDate是控制器生成的,不会有危险性,而feedback.Message是用户输入的,所以为了安全其见我们需要对其进行编码
三、解决方法二:在控制器中对HTML数据进行编码(原创:灰灰虫的家 http://hi.baidu.com/grayworm)
除了在显示的时候对用户反馈的信息进行编码显示,我们还可以在控制器接收到用户提交的数据后进行编码,然后把编码后的数据送到数据库中。
下面的代码,是我们在控制器中对用户输入的数据进行编码
Listing 4 – HomeController.cs (HTML Encoded)
using System;
using System.Web.Mvc;
using CustomerFeedback.Models;
namespace CustomerFeedback.Controllers
{
[HandleError]
public class HomeController : Controller
{
private FeedbackDataContext db = new FeedbackDataContext();
public ActionResult Index()
{
return View(db.Feedbacks);
}
public ActionResult Create(string message)
{
// Add feedback
var newFeedback = new Feedback();
newFeedback.Message = Server.HtmlEncode(message);
newFeedback.EntryDate = DateTime.Now;
db.Feedbacks.InsertOnSubmit(newFeedback);
db.SubmitChanges();
// Redirect
return RedirectToAction("Index");
}
}
}
从上面的代码中我们看出,Create()动作在向数据库插入数据之前,对用户输入的数据进行编码。当在界面中显示数据库数据的时候,用户的输入已经被编码了,就不会造成危险。
一般我们更推荐上面讨论的第一种解决方案,因为第二种方案会在数据库产生编码后的HTML标记,即会产生一些古怪难以理解的数据,这些数据在WEB页面中显示会变得正常,但在WinForms应用程序中显示时会搞成一团糟。
总结
这篇文章主要讨论了JavaScript攻击,并提出在ASP.NET MVC应用程序中两种解决方案:
在显示数据的时候,通过使用Encoding来防止攻击
在接收到数据的时候,通过使用Encoding防止攻