安全脚本程序的编写 V1.0(2)

来源:岁月联盟 编辑:zhu 时间:2003-04-15
2.2 cookie的问题
2.2.1 概念介绍
按照Netscape官方文档中的定义,Cookie是在HTTP协议下,服务器或脚本可以维护客户工作站上信息的一种方式。Cookie是由Web服务器保
存在用户浏览器上的小广西文件,它可以包含有关用户的信息(如身份识别号码、密码、用户在Web站点购物的方式或用户访问该站点的次数)
。无论何时用户链接到服务器,Web站点都可以访问Cookie信息。

通俗地讲,浏览器用一个或多个限定的文件来支持Cookie。这些文件在使用Windows操作系统的机器上叫做Cookie文件,在Macintosh机器上
叫做magic Cookie 文件,这些文件被网站用来在上面存储Cookie数据。网站可以在这些Cookie文件中插入信息,这样对有些网络用户就有些
副作用。有些用户认为这造成了对个人隐私的侵犯,更糟的是,有些人认为Cookie是对个人空间的侵占,而且会对用户的计算机带来安全性的危
害。

目前有些Cookie是临时的,另一些则是持续的。临时的Cookie只在浏览器上保存一段规定的时间,一旦超过规定的时间该Cookie就会被系统清
除。例如在PHP中Cookie被用来跟踪用户进程直到用户离开网站。持续的Cookie则保存在用户的Cookie文件中,下一次用户返回时,仍然可以
对它进行调用。

要了解Cookie,必不可少地要知道它的工作原理。一般来说,Cookie通过HTTP Headers从服务器端返回到浏览器上。首先,服务器端在响应
中利用Set-Cookie header来创建一个Cookie,然后,浏览器在它的请求中通过Cookie header包含这个已经创建的Cookie,并且反它返回
至服务器,从而完成浏览器的论证。 例如,我们创建了一个名字为login的Cookie来包含访问者的信息,创建Cookie时,服务器端的Header如
下面所示,这里假设访问者的注册名是"Michael Jordan",同时还对所创建的Cookie的属性如path、domain、expires等进行了指定。

Set-Cookie:login=Michael Jordan;path=/;domain=msn.com;
expires=Monday,01-Mar-99 00:00:01 GMT

上面这个Header会自动在浏览器端计算机的Cookie文件中添加一条记录。浏览器将变量名为"login"的Cookie赋值为"Michael Jordon"。注意
,在实际传递过程中这个Cookie的值是经过了URLEncode方法的URL编码操作的。

这个含有Cookie值的HTTP Header被保存到浏览器的Cookie文件后,Header就通知浏览器将Cookie通过请求以忽略路径的方式返回到服务器
,完成浏览器的认证操作。

此外,我们使用了Cookie的一些属性来限定该Cookie的使用。例如Domain属性能够在浏览器端对Cookie发送进行限定,具体到上面的例子,
该Cookie只能传达室到指定的服务器上,而决不会跑到其他的如www.hp.com的Web站点上去。Expires属性则指定了该Cookie保存的时间期
限,例如上面的Cookie在浏览器上只保存到1999年3月1日1秒。当然,如果浏览器上Cookie太多,超过了系统所允许的范围,浏览器将自动对
它进行删除。至于属性Path,用来指定Cookie将被发送到服务器的哪一个目录路径下。

说明:浏览器创建了一个Cookie后,对于每一个针对该网站的请求,都会在Header中带着这个Cookie;不过,对于其他网站的请求Cookie是
绝对不会跟着发送的。而且浏览器会这样一直发送,直到Cookie过期为止。

2.2.2 要点方法
setcookie-----送出 Cookie 信息到浏览器。
语法: int setcookie(string name, string value, int expire, string path, string domain, int secure);
返回值: 整数
本函数会跟着标识 Header 送出一段小信息字符串到浏览器。使用本函数要在送出 HTML 数据前,实际上 cookie 也算标识的一部份。本函数的
参数除了第一个 name 之外,都是可以省略的。参数 name 表示 cookie 的名称;value 表示这个 cookie 的值,这个参数为空字符串则表示取
消浏览器中该 cookie 的数据;expire 表示该 cookie 的有效时间;path 为该 cookie 的相关路径;domain 表示 cookie 的网站;secure 则
需在 https 的安全传输时才有效。想得到更多的 cookie 信息可以到 http://www.netscape.com/newsref/std/cookie_spec.html,由
cookie 原创者 Netscape 所提供的完整信息。

对于一个网站会员而言,经常存在需要一次注册,多次认证的问题,例如我们经常接触到的论坛、社区等,一般采用手段为cookie或 input
type=hidden来传递认证参数。这里面有几点隐患:

I. setcookie内容必须完整包含帐号密码,或类似的完整安全信息,如果只携带帐号信息或用某种权限标志来认证,极容易造成非法入侵
例如某站点中的会员更新页面中携带的认证信息是两个,用户名和Uid(均为明文传送)已知Uid对于每个会员是唯一的。由于我们只需要知道对
方的帐号和Uid就可以更改对方信息(不需要知道密码!),只要攻击者知道Uid(攻击者可以通过暴力猜测的方法来得到Uid,有时候站点本身
也会泄露用户的Uid,例如在论坛等处)那么,攻击者就可以通过遍历攻击完成对任意一个帐号的信息更改。

II. 必须所有需要权限操作的页面都必须执行认证判断的操作。如果任何一页没有进行这种认证判断,都有可能给攻击者以恶意入侵的机会。

III. 很多网站为了方便,将用户名以及口令信息储存在Cookie中,有的甚至以明文方式保存口令。如果攻击者可以访问到用户的主机,就可能
通过保存的Cookie文件得到用户名和口令。

3. 脚本保护的问题
3.1 概念介绍
在程序编写时优秀的程序员都会知道,用有意义的变量名,文件名有助于增加程序的可读性,具有良好的程序风格。这个非常好但在脚本语言不太
适合,为了不让恶意用户猜到你的变量或数据库名等信息,必须改掉这些信息。动态的网页在服务器端执行后返回给客户的是执行后的代码,这可
以保护服务器端的很多不想叫或不能叫浏览者知道的信息。安全是相对的,每天都在有新的安全漏洞被发现,如果恶意的用户在你之前知道了一个
可以看你的脚本源代码的漏洞或这个漏洞一时间无法修补怎么办?
3.2 主意要点
建议用一些比较怪异的名字命名,删掉脚本中的注释。如果还需要保持程序的可读性的话,可以建立一个映射,你可以写个具有良好风格的脚本程
序,然后再做一个变量名映射建立一个具有较安全命名方法的脚本,去掉这个脚本中的注视和所有能去掉的信息,修改时作个同步就可以了
我们可以在程序的使用前对程序进行加密,以保护我们自己的程序再万一的情况下部被泄漏。
3.3 保护方法

我看到过很多的对脚本的加密方法,都很不错,有的是专门的加密软件,有的是通过一些技巧加上利用语言的特性进行加密的,例如随机生成一个
密匙,把密匙放在"不可见的"地方,通过一些算法对脚本进行加解密,就是由于某些系统漏洞导致你的脚本源代码泄漏,也无济于事。

4 .实例说明
下面这个例子是在网上经常被提到的,这是个非常经典的例子,所以在这里通过这个实例告诉大家可能存在的危险。
问题描述:
  大部分网站把密码放到数据库中,在登陆验证中用以下sql,(以asp为例)
sql="select * from user where username='"&username&"'and pass='"& pass &'"
  此时,您只要根据sql构造一个特殊的用户名和密码,如:ben' or '1'='1
就可以进入本来你没有特权的页面。再来看看上面那个语句吧:
sql="select * from user where username='"&username&"'and pass='"& pass&'"
  此时,您只要根据sql构造一个特殊的用户名和密码,如:ben' or '1'='1 这样,程序将会变成这样: sql="select*from username where
username="&ben'or'1'=1&"and pass="&pass&" or 是一个逻辑运算符,作用是在判断两个条件的时候,只要其中一个条件成立,那么等式
将会成立.而在语言中,是以1来代表真的(成立).那么在这行语句中,原语句的"and"验证将不再继续,而因为"1=1"和"or"令语句返回为真值.。

  另外我们也可以构造以下的用户名:
username='aa' or username<>'aa'
pass='aa' or pass<>'aa'
  相应的在浏览器端的用户名框内写入:aa' or username<>'aa 口令框内写入:aa' or pass<>'aa,注意这两个字符串两头是没有'的。这
样就可以成功的骗过系统而进入。

具体实施是这样的,首先我会到注册的地方去收集信息,了解尽可能多的信息,例如目标数据库中都有用户的什么样的信息,随便的填写信息然后
提交,当你要注册的用户名被注册的是有系统会提示你已被注册,有的网站做的更好的,就是他们专门给你设置的检测是否有已经被注册的功能,
通过这样就会非常容易的找到目标--那个提示已被注册的用户,让后你在这个注册页里填写一些特殊的字符,如',/,,等字符看系统如何提示,以
证明程序员是否注意到了应该过滤字符或懂得是否应该过滤那些字符,在这页进行尝试是因为有的网站在登录的时候他会记录你的ip地址,当然你
也可以找一个比你直接登录要快的代理服务器来做跳板。后面你要做的就是察看登录页的html源代码,看看是否有在客户端的字符过滤,看看这个
程序员是用什么风格来编写程序,尽可能多的了解程序编写风格,这对你以后的某些判断有好处。如果有在客户端的过滤也不怕,你要搞清是什么
样的过滤,能不能对攻击造成威胁,不要一看有过滤就害怕,可以尝试着用别的方法绕,就是使用自己精心打造的独立脚本,进行攻击。然后你要
看看form的action中的url是否可以直接提交,在浏览器地址栏里直接提交,看看返回什么,是否有来路检测。还有很多细小的地方,你也应该可
以注意到,例如那些地方程序员的整体的编写风格是什么,变量名定义的风格是什么等等,这个会帮我们"猜"到很多东西。还有别的其他什么,我
也记不太清楚了,临场发挥吧。通过这些了解我们有如下几种可能:
1.那个程序员非常善良相信全世界都是好人,什么都没做,根本没有任何检测机制,我们直接用username='aa' or username<>'aa',
pass='aa' or pass<>'aa'就可以搞定,现在这么善良的人少啦,可是你要是有耐心,找到这种人还是不难的。
2.这个程序员可能听别人提起过一些安全问题,毕竟现在这个那里都有人说,很多书中都有提及,但是做得不够好,他只进行了简单的输入过滤。
过滤有两种方式,一种是在客户端的过滤,一种是在服务器端的过滤。现在很多的程序员考虑到再服务器端进行过滤可能给服务器造成更多的负荷
,会把检测过程放在客户端。如果他在服务器端没做任何事情,那么还是可以对其进行攻击的,我可以将这个登录页的源代码COPY下来,然后自
己建立一个文件把这些代码PASTE进去,再对这个文件进行进一步的深加工,去掉原来页的过滤机制,或者直接将攻击代码写到这个文件中去,然
后将form中的action中的地址改成绝对地址,也就是将文件名改成"http://www.target.com/targer.php"这样,然后就可以提交啦。但是如
果服务器端加上了"来路检测",你就白玩了。如果这样还是不行,我再换一种方法,在浏览器的地址栏里用?来输入参数,就好像
"http://www.targer.com/targer.php?username='aa' or username<>'aa'&pass='aa' or pass<>'aa' "然后敲回车吧,其实应该先
尝试这种方法因为这用方法更简单,防护起来也很简单,这种提交方式不是post 而是get ,只要服务器端程序检测你的提交方法,就可以kill掉这
个阴谋。如果单纯的只检测了"来路",还是不太安全的,可以先正确的提交一次,在提交过程中马上停止,就是保存这个环境,然后再构造请求。
(我做过几次试验得到的结果都不太一样,应该是和终止的时机有关,欢迎大家来交流)。
3.一个很出色的程序员,安全意识非常高,他在服务器端做了如下检测:检测提交的方法;检测提交的"来路";检测提交内容的长度;全面检测提
交内容,这样我们就很难通过上面的方法对其进行攻击,那到保密的资料就已经不太可能了(如果各位还有什么好的办法,请一定来信告诉小弟,
小弟在这里先谢了)。但是我还想说的是攻击并不代表是非要入侵进去,拿到某些东西才叫入侵,对你的机器进行破坏也叫入侵啊,例如提交一些
错误的请求,脚本解释程序就会非常规矩的给你返回错误信息,最浅显的后果就是暴露物理路经,有的时候一些特殊的请求会使web服务宕掉,这
些个我认为绝对是属于攻击,绝对是危害,也许你认为暴露物理路径没有什么,是在单独看来没有什么,但要是在一个有计划的攻击里,这个就会
发挥很多作用,那时你可能还会莫名为什么他们找到了我的文件呢。也许有人认为这个是脚本解释程序的bug,也许有的是,但是返回错误信息绝
对不是脚本解释程序的错误,这个是每个解释程序都要做到的,在我看来这个应该是还是程序员的问题,程序员没有做好对错误的处理。每一本教
你如何编写程序的书籍里基本都会有错误处理之类的章节,并且每种语言基本都有错误处理函数和方法,只不过你没有想到罢了。至于究竟要怎么
处理那就要看你对cgi程序安全的熟悉程度了,那就要看你对这种脚本语言的特性熟悉多少了,说到底就是经验,唯一的办法就是多看多写多想多
交流。
4.非常优秀的程序员,以上那些做的都非常好(也许就是你啊,毕竟不难嘛,加上很少的代码就可以了),怎么办??怎么办?!怎么办!!在一
旁偷偷的佩服吧!哈哈。


5. 其它注意事项:思路和方法

指导思想:
I.严格控制程序与用户交互的途径
II.严格控制程序与用户交互的内容
III.尽可能好的保护我们控制

基本思路:
I.为没一个功能写一个独立的程序,程序页
II.尽可能少的让客户了解你的服务器端信息
III.不要用"客户应该这么写"这个思路想问题
IV.尽可能多的想到不可能发生的事情

基本方法:
尽可能多的控制交互:
I.检测提交的方法,就是控制他的post还是get;
II.检测提交的"来路",就是检测一个环境变量HTTP_REFERER;
III.检测提交内容的长度;
IV全面检测提交内容;
积极-消极防护:
I.尽可能多的错误处理,例如当检测到了不正确的输入时,应该怎么做,是强制返回,还是发出警告;
II.充分发挥日志功用,例如在你检测到了不正确的提交时,就记录下客户端的信息,例如IP,系统配置,请求等等,毕竟现在是技术飞跃的时代,
不能保证可以想到每一种可能,这也是我在这篇文章里不止一次提到"尽可能"这个词的原因。充分的日志记录不全是为了抓住入侵者(如果入侵
使用了跳板,记录了IP也是没有用的),更重要的是为了能发现问题的所在,找到问题,改正问题,亡羊补牢,这个才是最重要的。
III.充分发挥你的想象力,用一种入侵者的思想考虑问题,用一种另类的思想考虑问题,尽可能想到不可能发生的事,把问题扼杀在萌芽里。


我们xundi哥说的好:掌握方法!!!现在脚本语言层出不穷,asp,perl,php,jsp等等,基本不可能精通每一种,(也许你厉害,都能精通,
我比较呆,会一个就不错啦),但是要是掌握了方法就不同了啊,各位网络的精英举一反三触类旁通,肯定是优秀的不得了。我写脚本一共也没多
少天,写这个东西我知道肯定是班门弄斧了,错误之处还请各位大虾抱着挽救和帮助的精神,告知小弟(方式、方法、态度不限),小弟我在这里
先谢了。写这个东西,我只是想说说小弟的一些小的心得,与大家共勉,我想告诉大家的就是"领会精神",嘿嘿,"领会精神"。大家要是有什么好
的方法,希望不要保留,充分发挥网络的自用共享,拿出来,大家交流交流,不胜感激。
袭来的,这种东西小弟不敢自己写(嘿嘿,实际还有不少懒的成分,哈哈),希望大家不要见怪。