ASP.NET 安全

概述

安全在web领域是一个永远都不会过时的话题,今天我们就来看一看一些在开发ASP.NET MVC应用程序时一些值得我们注意的安全问题。本篇主要包括以下几个内容 :

  1. 认证
  2. 授权
  3. XSS跨站脚本攻击
  4. 跨站请求伪造

 

认证

所谓认证,简单的来说就是验证一个用户的身份。这取决于我们开发的站点的类型,是否允许匿名访问,是否是属于管理员或者其它角色的用户等等。也就是说我们的整个程序或者某些功能是针对某些特定的用户开发的,那么我们可能就要进行认证来确定用户的身份。需要注意的是,认证与授权是是完全不一样的概念,我们要区别对待。打个比方,在ASP.NET MVC里面允许某一类用户访问某个Action就是授权。

ASP.NET MVC中主要有两种认证机制

  1. Forms 认证
  2. Windows 认证

Forms 认证

从字面上我们就可以得到一些信息,基于表单的认证提供给用户一个表单可以输入用户名和密码,然后我们可以在我们的程序中写自己的逻辑去验证这些信息。ASP.NET MVC为Forms认证提供了很多支持,并且有很强自定义性。从通过表单登录到用户信息存储在什么地方,到怎么样去验证这些用户信息。Forms认证默认是依靠cookie技术实现的,一旦某个用户登录站点,那么用户所使用的这个浏览器就会得到一个cookie并且在后面所有与这个站点的其它请求中都会将这个cookie包含在http的头中。ASP.NET能够检测到这个cookie,这个cookie中包含了用户的认证信息,那么后面就不需要再重复的认证用户了。

Windows认证

Windows 认证也就是大家熟悉的集成身份认证,因为它使用了集成在Windows操作系统中的用户组件来认证用户。一旦某个用户登录到域中,Windows能够在应用程序中自动认证他们。Windows认证一般在企业局域网内比较常用,一般企业局域网中所有的用户都需要用域身份来登录,这个有点像单点登录的体验,一旦进入域中就可以就可以很方便的同时登录域内的其它应用程序。

配置Forms认证

首先我们需要更改web.config中的authentication结点。

这个配置信息很简单,首先我们要使用的authentication类型是Forms认证。通过loginUrl指定我们认证用户的页面。这个Account Controller和 Login View还有一些允许用户注册的View都被ASP.NET MVC的internet模板默认实现了。我们可以轻而易举在在ASP.NET MVC中实现Forms认证。

打开Visual Studio 2010 > New Project > Select ASP.NET MVC 4 Web Application 点击确认。

然后选择Internet Application点击确认,Forms认证所需要的Controller 和View等等都会默认包含在我们的项目里面了。

Authorize 属性

Authorize不关注我们如何认证用户,我们既可以用Forms认证也可以用Windows认证。Authorize会去检测当前用户是否有身份信息。如果我们在Index上加上Authorize属性那么匿名用户就不能访问我们的Index Action了。他们会被跳转到Account/Login,也就是我们上面在web.config中配置的loginUrl。

 

如何配置Windows认证

和Forms认证一样,首先我们需要更改一下web.config中的authentication结点。

然后同样地,应用Authorize属性到我们的Index Action上。

我们可以将Authorize应用到一个单独的Action上,也可以应用到一个Controller上。当我们在某一个Controller上应用Authorize属性时,也就意味着这个Controller下所有的Action都必须是经过认证的用户才允许访问 。

如果使用IIS Express的话,我们需要更改配置信息来启用Windows认证。否则我们就会得到以下错误页面。

我们可以到IIS Express的配置中去启用Windows认证,打开Windows Explorer进入我的文档> IIS Express > config > applicationhost.config。然后将windowsAuthentication enabled设置为true。

然后我们就可以拿到一些用户的信息。

 

授权

授权允许我们传递一些参数去设置规则,我们可以告诉Authroize属性只有某些具体用户才可以访问某个Action。

同时 ,我们还可以为Authorize属性指定 Roles。这些Roles默认匹配到我们web服务器的Windows Group或者是域管理器里面的用户组。

在Forms认证中, ASP.NET为我们提供了一个角色管理器(role provider)我们可以通过它来方便和将我们的角色信息存储到SQL中,并且进行管理。我们只需要点击一个按钮即可:

点击上面这个按钮之后,它会帮我们运行ASP.NET configuration tool。这个站点只能在本地运行,我们可以在这个站点管理我们的角色,这个站点默认使用的数据连接就是我们配置在web.config中的连接字符串。

 

XSS跨站脚本攻击

在web领域,有几个比较常见的安全隐患,其中一个比较流行的就是跨站脚本攻击。一些恶意的用户通过一些手段让我们的站点加载一些恶意的脚本,那么如果其它用户访问到这些脚本就有可能成为受害者。除了脚本,包括active-x控件,甚至一些恶意的Html都可以成为XSS的武器。XSS可以做到哪里事情 ?

  1. 窃取cookie
  2. 更改用户设置
  3. 下载恶意软件
  4. 更改内容
  5. 账户劫持

简单的说,我们可以通过XSS访问用户的个人信息以及身份信息。

XSS示例

这是一个简单的录入员工信息的页面,我们输入一些html代码然后保存页面。ASP.NET默认会去检测我们的request,发现类似html代码会直接拒绝我们的请求。

当然,有些时候我们需要允许用户输入html,那么只要在我们的Action上打上ValidateInput(false)即可。

这样我们就可以成功的提交 我们的请求了。

如上图所示,这样我们又遇到了另外一个问题。在ASP.NET MVC中razor默认会对所有输出进行html编码。这是ASP.NET MVC针对XSS攻击的另一道防火墙。通过为属性打上AllowHtml属性,我们可以允许某一个属性包含html的值,这样我们就可以移除Action上的ValidateInput属性。通过Html.Raw 我们可以将html输出到客户端。

 

Anti XSS library

如果我们允许用户输入html的话,有些人可能会尝试输入一些脚本 (不要说你没有想过在博客园输入一些脚本来玩玩?)

幸运的是,Microsoft为我们提供了一个组件,我们可以通过nugget或者Library Package Manager Console( Visual Studio > Tools > Library Package Manager > Package Manager Console 输入 Install-Package AntiXss回车 )

只需要简单的一句话,就可以移除所有的有害代码,是不是感觉又被Microsoft搞蠢了?

 

 

CSRF跨站请求伪造

跨站请求伪造也是一种危险的主流攻击。试想一下,某个用户登录到网站想修改一些个人信息,如果服务器端使用了Forms认证,那么在这个用户登录之后就会得到一个包含身份信息的cookie并且在后面所有这个站点下的请求中传递。当然这个并没有错,毕竟如果每次都去验证用户名和密码是一次不小的开销,验证一次之后将登录信息保存到cookie中,至少在用户不关闭浏览器之前,我们不用再重新去验证用户。

安全隐患在哪里?

如果浏览器端依然保留着我的身份信息,那在我访问其他恶意的站点的时候。这些恶意的站点就可以自己封装一个表单并提交到我们的服务器,虽然这个请求时恶意站点伪造的,但是因为它带有用户的身份,所以服务器是会正常处理的。小到更改用户资料,大到转走用户的账户余额都成为可能。

所以我们在处理请求的时候,不仅仅需要验证用户身份信息,还需要确保发送数据的表单是由我们服务器产生的。这样就可以避免其他恶意用户伪造表单发送数据。

CSRF示例

这里有一段很常见的代码,通过Edit Action来编辑用户信息。我们已经为Edit 打上了Authorize属性,也就是说用户是需要登录才能访问这个Action的。从普通开发的角度来看,这个程序是不会有什么问题的,我们首先通过正常渠道添加了一个用户。

接下来,很雷很雷的事情发生了。你收到一封邮件说你中奖了,给了你一个链接,或者在某个网站上本身就嵌入了一些恶意代码,而你不幸手一抖,就点了。接下来结果有可能是这样滴。

你的数据很轻松就被篡改了。如果账号是有余额的,你就哭吧。来看看这个页面 是如何实现的。

非常的简单,我们只需要将form的action指向实际的action就可以了。这个页面一旦被加载,这个表单就会自动提交,那我们的数据就被黑了,一切都是那么的简单。

 

如何避免?

ASP.NET MVC 为我们提供了Html.AntiForgeryToken() 方法,我们只需要在form中添加这句话。MVC 会为我们生成一个唯一标识放在form中的一个隐藏域中,该标识还会被存放到cookie中在客户端和服务器的请求中传输。另外我们要做的就是为我们的Action打上ValidateAntiForgeryToken的属性。

如果请求不包含这个cookie,那服务器就会拒绝这个请求,从而避免CSRF的攻击。

 

ASP.NET 安全,首发于博客 – 伯乐在线

Streets of London

你看见过一个老人在关闭的市场里,用他破旧的鞋子踢打报纸吗?他的眼光里没有骄傲,腋下夹着昨日的报纸讲着昨日的故事.
你怎么能说你就是孤单的,你的太阳并没有照耀着你?让我拉着你的手领你到伦敦的街头,那里有些人和事会改变你的看法.
午夜十一点半的咖啡馆,老人孤独地坐着,透过茶杯他凝视这世界,一杯茶捱过一个时辰,末了他一个人蹒跚回家....
这首名为《Streets of London》(伦敦街头)的歌,写的是伦敦街头的老人,以及歌手对老人的悲悯情怀,苍凉的画面、忧郁的吉他声,安抚着无数落寞的心。不知道几十年后我们不再年轻,那时回想起来会是何种心情,是否像歌里唱得那样,当繁华落尽,等待我们的将会是什么。雨带着一点怜悯在哭泣,为不止一个被忘却的英雄和不在乎他们的世界……

Ralph McTell – Streets of London 倫敦的街道

1370531339667263_21

Sprial Staircase


Have you seen the old man
你可曾见过那么老的一个人
In the closed down market
在市场打烊后
Kicking up the papers with his worn out shoes
拖着他破烂了的鞋子踢着被丢弃的报纸
In his eyes you see no pride
在他的眼睛里你看不到自豪的神采
Hands held loosely at his side
双手散漫的垂在身旁
Yesterday’s paper, telling yesterday’s news
过期的报纸上,写着过期的故事
So how can you tell me you’re lonely
你怎么能告诉我你是孤独的呢
And say for you that the sun don’t shine
说太阳都不肯为你散发光亮
Let me take you by the hand
让我牵着你的手
And lead you through the streets of London
带着你走过伦敦的街头吧
I’ll show you something
让你看看那儿发生的事情
To make you change your mind
它们会把你来改变
Have you seen the old girl
你可见过那年华不再的女子
Who walks the streets of London
姗姗走过伦敦的街头
Dirt in her hair and her clothes in rags
披着她灰脏的头发和碎破的衣服
She’s no time for talking
她没有空儿停下来聊点什么
She just keeps right on walking
就只是一劲儿向前走着
Carrying her home in two carrier bags
带着两只旅行包,那是她的全部家当
So how can you tell me you’re lonely
所以你怎么能告诉我你是孤独的呢
And say for you that the sun don’t shine
说连太阳都不肯把你照耀
Let me take you by the hand
让我牵着你的手
And lead you through the streets of London
带着你走过伦敦的街头吧
I’ll show you something
让你看看那儿发生的事情
To make you change your mind
它们会把你来改变
In the old night cafe at a quarter past eleven
十一点一刻,古旧的咖啡夜店里
The same old man sitting there on his own
还是那同一个老人独自坐在那儿
Looking at the world over the rim of his teacup
越过他茶杯的边缘看着这个世界
Each tea lasts an hour, and he wanders home alone
每一杯他都会喝一个小时,然后拖着孤独的脚步回家去
Have you seen the old man
你见过那些老去的男人们吗
Outside the seaman’s mission
他们结束了海上的漂泊使命
Memory fading with the minor ribands that he wears
记忆和他佩戴的缎带一起慢慢褪色
In our winter city the rain cries little pity
在这个城市的冬季,细雨呜咽出辛酸的惋惜
For one more forgotten hero
为着被遗忘的英雄
And a world which doesn’t care
和这毫不为此牵挂的世界
So how can you tell me you’re lonely
所以你怎么能告诉我你是孤独的呢
And say for you that the sun don’t shine
说连太阳都不肯把你照耀
Let me take you by the hand
让我牵着你的手
And lead you through the streets of London
带着你走过伦敦的街头吧
I’ll show you something
让你看看那儿发生的事情
To make you change your mind
它们会把你来改变

CPU也可以有后门?

CPU的后门

人们普遍认为,任何一款软件都可以通过后门被破坏。举几个比较有代表性的例子如:Sony/BMG的安装程序,有个内置的后门禁止用户复制CD,这个后门也使得恶意的第三方能接管任何安装了该软件的机器;三星Galaxy,它有个后门允许调制解调器访问设备的文件系统,这也就允许了任何一个假基站来访问设备上的文件;以及Lotus Notes,它有个后门能使加密失败。

尽管后门多见于FPGA网络设备,但每当有人提起CPU上的后门程序是否可能的时候,大部分情况下大家都会断言这是不可能的。我不会断言CPU后门程序是存在的,但我会断言,如果有正确的访问权限,实现就很容易了。

比方说,你想制造出一个后门。你要怎么做呢?这要分三个环节:一个CPU后门能做什么,要怎样才能访问这个后门,需要什么样的让步才能安装该后门?

从第一个环节开始,后门能做什么?这就有很多很多的可能。最简单的就是提升权限:使CPU从ring3过渡到ring0或SMM,给正在运行的进程的内核级别的权限。因为它是负责运行的CPU嘛,完全可以无视硬件和软件虚拟化。你可以做很多更微妙或更具侵略性的事情,但权限提升不仅够简单,而且够强大,所以我就不再打算讨论其他的选项。

现在你知道了你想要借后门做什么,那么究竟应该如何触发后门呢?理想情况下,它应该既不会被人碰巧运行到,也无法通过暴力寻找到。即使有这样的限制,可能的触发状态空间仍旧是巨大的。

让我们来看一个特定的指令,fyl2x[1]。在正常操作下,它需要两个浮点寄存器作为输入,给您2*80=160位(bits)来隐藏一个触发器。如果你通过一对特定值来触发一个后门,可能相对于随机筛选更安全些。如果你真的很担心后门被人意外发现或暴力破解掉,你也可以检查两个正常输入寄存器以外的值(毕竟,你控制着整个CPU啊)。

这个触发器简单有效,但不足之处是要触发它很可能需要运行本机代码,但你其实不可能让Chrome或Firefox发出一个fyl2x指令。通过相对容易地令JavaScript引擎发出指令(像fadd),你可以尝试变通地去解决这个问题。与此相对的问题是,如果你想要patch一条add指令,并对它添加一些检查,它就会显著地变慢(尽管如此,如果你可以改写硬件,你应该能够无开销地完成它)。通过patch一个rep字符串指令,做一些事情来设置恰当的“key”,接在块拷贝(block copy)后面,或者idiv,也有可能可以创造一些难以检测并可以通过JavaScript来触发的后门。或者,如果你已经成功地得到了设计的副本,你也许可以想出一个办法,当任意一些JavaScript运行的时候,来使用调试逻辑触发器[2]或性能计数器去引发一个后门。

好了,现在你已经有一个后门了。那么你怎么植入该后门呢?在软件方面,你可以编辑源代码或二进制文件。在硬件方面,如果你有机会到访问到源,你可以在跟在软件中一样容易进行编辑。对硬件重编译源代码,建物理芯片,有着极高的固定成本;如果你试图让你的更改编入源代码,你要么牺牲设计[3],在一切被发送去生产之前就植入你的所有更改,要么牺牲生产过程,在最后一刻[4]偷偷植入你的更改。

如果这听起来太难了,你可以尝试牺牲补丁机制。多数现代的CPU配备了一个内置的补丁机制,允许事后的bug修复。你使用的CPU可能早就已经被修补过,也许从第一天开始就是,以作为固件更新的一部分的名目。你CPU补丁机制的细节是严格保密的。这很有可能是CPU上被蚀刻了一个公共密钥,这样它就只能接受已经签署了正确私钥的补丁。

这就是实际正在发生的事吗?我不知道。它可能发生吗?当然可能。有多大几率呢?唔,主要的挑战是非技术性的,所以我不是那个能给出这个问题答案的人。如果非要我猜的话,我会说不是,如果没有除了容易破坏其它设备以外的原因的话。

我还没有讨论如何制作这样一个后门:即使有人能够访问你用来触发后门的软件,也还是很难发现它。这更难,但是一旦芯片开始使用内置TPM的话,它就应该有可能了。

如果你有兴趣听到更多关于CPU内部的工作原理,你可能会对这篇关于过去35年内CPU的新功能的帖子感兴趣。

更新

关于这个话题更多的讨论见这条twitter主题。那里有一些非常好的评论。我对其中的一部分进行了摘录和总结,不过该帖子的讨论仍在继续。

因为有太多评论的缘故,我在此不一一指出哪些评论是谁的,尽管如此,以下是评论的作者们:@hackerfantastic, Arrigo Triulzi, David Kanter, @solardiz, @4Dgifts, Alfredo Ortega, Marsh Ray, 以及Russ Cox。当然,要是弄错了的话就怪我咯。

AMD的K7和K8对他们的微码(microcode)补丁机制作出了牺牲,因此容许了本帖所提到的这种攻击。事实上,AMD并没有加密它的更新或者至少对它的checksum进行验证,这让你能轻松地更改它的更新,直到你得到一个能达成你目的的CPU。

下面是由Alfredo Ortega出于演示目的而创建的一个后门的例子

对于没有硬件背景的亲们,这里有个不错的关于如何用VHDL实现一个CPU的谈话,里面还有个章节是关于如何实现后门的

是否有可能通过提供恶意的随机结果来利用RDRAND制造后门?是的,有可能。我在本帖的第一稿提到过,但我后来删掉了这个方法。因为在我的印象中,人们不信任RDRAND而且结果中一般混合了其他熵(entropy)来源。这不会彻底使这个后门无效,但却也显著地降低了它的价值。

有没有可能来存储和释放AES-NI键?要偷偷闪存内容到一个芯片上而不被任何人注意到,这大概不太可行,但是,现代芯片的逻辑分析仪能让你对数据进行存储和转储。尽管如此,对它们的访问是通过一些秘密的机制,目前还不清楚要如何访问二进制文件,才能允许你对它们进行逆向工程。与此形成鲜明对比的是K8逆向工程,它是可能的,因为微码(microcode)补丁被包括在固件更新中。

要检查触发器的指令前缀是可能的。 x86允许你对指令添加多余(和矛盾)的前缀。使用过的前缀都被很好的定义过,所以你可以根据需要添加尽可能多(不超过前缀长度限制)的前缀,而不会造成任何问题。它的问题是,很难不牺牲该微码(microcode)补丁的性能,对前缀数目和长度的限制则意味着:若不跨指令追踪状态,你的有效密钥长度就会比较短,并且你只能通过本地代码来生成触发器。

我们都知道,上文所述的一切都基于推测,其实并没有人见过真实世界里被应用的CPU后门。

致谢

感谢Leah Hanson大量的评注,Aleksey Shipilev的建议,以及众多参与者在上文所提到的twitter主题贴中的讨论。此外,感谢Markus Siemens注意到在一些RSS阅读器中引发问题的bug,并提供了解决方法。这不是仅仅局限于本帖,但它出现于此。

[1]关于指令的选择是有点,但不是完全,武断的。你可能需要一个缓慢并且微编码过(microcoded)的指令,便于打一个微码补丁,而不会造成巨大的性能损失。本脚注的其余部分是关于对指令进行微编码(microcoded)到底意味着什么。这部分很长,而且并不是本帖的关键点,所以你可以跳过它。

指令被微编码过(microcoded)与在硬件实现间差别多少,其实有点随意/难说。 CPU有一个需要实现的指令集,你可以把它想成公共API。但是在内部,他们其实可以执行不同的指令集,你可以把它想成私有API。

在现代英特尔芯片上,转成四个(或更少)uops(私有API调用)的指令会由解码器直接译成uops。会引发更多uops的指令(从5到数百甚至数千)则由从CPU中小小的ROM或RAM的读取uops的微码引擎来解码。为什么是四而不是五?这个结果是出于权衡,而非基本真理。对此专用的术语尚未规范,但我认识的人会说,如果它的解码是由微码引擎完成的,那么指令是“微编码过的”(“microcoded”),如果它的解码是由标准解码器来处理的,那么就说它是“硬件实现的”。微码引擎有点像自有的CPU,因为它必须能够完成类似,对架构上不可见的临时寄存器进行读取和写入操作,对内部RAM读写需要超过少量寄存器暂存空间的指令,对微码引擎获取和解码的微码指令进行条件微码分支,如此等等。

实现细节往往变化无常(并且通常是机密)。但无论如何实现,你可以把微码引擎想象成CPU启动时加载写有微码(microcode)的RAM,之后从该RAM获取及解码出相应微码(microcode)指令的部件。通过打微码补丁来改变开机加载执行的微码就很容易了。

为了更快的调试周转期,假设英特尔存在这样的机制:使得他们能迫使非微码(non-microcoded)的指令在微码RAM外执行,以使CPU能打微码补丁,是合理甚至可能的。但是,即使这不是这种情况,牺牲微码补丁机制来修改一个微代码指令应该也足够安装一个后门上去了。

[2] 这里的大部分内容都未曾记载于官方文档,但是你可以得到一个高层次的概述:关于英特尔在他们几代前的芯片上制作什么样的调试触发器,于《Intel Technology Journal》第128页,第4卷,第3期

[3] 在过去的几年中,一直有关于大型企业是否受到了损害以及这可不可能的争论。在冷战期间,政府在各方面的机构受到了长时间不同程度的损害,尽管它能获得不向现在的任何企业开放的对策(外国公民避免,“强化审讯技术”等)。我不确定,我们是否永远都不会知道企业是否受到了损害,但危及当今的企业比起危及在冷战期间的政府机构来说肯定还是容易多了,并且这显然是可行的。

[4]这又是一个关于minutia的超长注脚!特别是,它是关于制造过程的。你可能想要跳过它!如果你没有跳过的话,请别怪我没提醒你哦。

事实证明,在制造完成之前修改芯片是比较容易的,从设计角度来说。要解释为什么的话,我们就得看看芯片是如何制成的。

英特尔芯片的横截面,22纳米制作工艺

“英特尔芯片的横截面,22纳米制作工艺”

当你观察一个芯片横截面的时候,可以看到硅门在底部,形成类似与非门的逻辑原语,在它以上有一系列(标记着M1到M8)的金属层,形成连接不同门的线路。制造过程的卡通模型类似于传统的石版印刷:芯片由底部向上,一次一层,每层通过沉积一些材料,然后使用掩模蚀刻出来。非卡通的版本则相当复杂的:据Todd Fernendez估计,大约需要500个步骤来制造出“M1”下面的芯片层。除此之外,所需精密程度之高使得用于蚀刻的光在设备上造成了太大的磨损。你通常可能不会认为透镜会因为被光线穿过而磨损,但在制作一个晶体管所需的数百个步骤要求的精度水平下,这就是一个严重的问题了。放心吧,你并不是唯一那个会对此觉得惊讶的人。上世纪90年代的ITRS路线图预测出,到2016年,我们在9纳米工艺(越小越好)上会达到接近30GHz(越高越好),芯片消耗将近300瓦。相反,现在5GHz就被认为相当快了,直至2016年初,任何非英特尔的厂商能在14纳米制作工艺上获得高产出都算是好运气了。制造芯片比任何人猜测的都更难。

一个现代芯片要有足够的芯片层,它从制作开始到结束需要三个月左右的时间。这使bug的出现成为了非常糟糕的状况,因为要修复一个bug,就需要对最底下一个需要三个月来制造芯片层做改动。为了减少bug修复所用的周转时间,典型的做法是在硅片各处散布未使用的逻辑门,那么修复小的bug只需要修改几个接近顶部的芯片层就可以了。由于芯片制作是流水线工艺,在任何时间点,都有几批部分完成的芯片。如果你只需要修改顶部金属层中的一个,那么你可以改动这些半成品,使周转时间从几个月缩短到几周。

由于芯片设计要求易于修改,如果有人能够在它被制造之前访问到芯片的设计(如制造商),那么他就可以使用相对较小的改动引发较大的变化。

CPU也可以有后门?,首发于博客 – 伯乐在线