■ 河南 郭建伟
编者按: 在很多Web应用中,外部可以利用传参的方式,来指定服务器中的文件名,例如在外界传入参数时指定一个模版文件名,通过读取模版文件的内容,将其内容在网页中展示等。这不可避免的会导致出现一些安全隐患,例如黑客可以非法访问Web服务器中的文件,来读取服务器中的敏感文件并展示在网页中等。黑客甚至还可以执行OS命令注入,在服务器上非法执行各种命令等。
当在Web应用中允许外界以参数的形式来指定服务器上的文件名时,如果没有对文件名进行充分的校验,就可能出现文件被非法浏览,甚至被恶意修改和删除等,这其实就是大家常说的目录遍历漏洞。
实际上,在网上有很多Web服务器都存在该漏洞,其危害是不可忽视的。该漏洞会泄露服务器中的重要信息,黑客利用该漏洞可以对网页进行篡改,发布各种恶意信息,利用非法链接将用户诱至预设的恶意网站,对服务器配置文件进行破坏,导致服务器运行异常。
黑客还可能修改重要的脚本文件,在服务器上执行各种恶意操作。可以说,凡是能够由外界指定文件名的页面,都可能存在该漏洞,其会对所有页面产生影响。
对该漏洞进行的防范的要点在于对外界指定的文件名进行严格的限制,例如禁止由外界指定文件名,如果必须指定的话,文件名中不能包含目录名,即只允许外界读取指定目录的内容,禁止随意切换目录,限制文件名中仅包含字母和数字,这样外界用户就无法切换目录,因为目录名必须包含斜杠等特殊字符。
对该漏洞进行防御,最根本的策略是避免由外界指定文件名,例如将文件名固定,将文件名保存在会话变量中,不直接指定文件名而使用编号等方法间接指定等。例如,在某网站中存在“”,“
”,“”等语句,来读取外部指定的模版文件,按照正常情况,当提交了合法的文件名后,就可以从指定的目录中读取该文件,将其内容显示在页面中。但当攻击者指定文件名,就会将密码文件显示出来,这对系统安全构成了威胁。将上述网页代码修改为“”,“if ($ tmp_no ==1)”,“{$tmpl = "1.html"}”,“else”,“{die(' 只支持文件编号!')}”等,根据编号来读取指定的文件,这样就避免了上述漏洞。如果必须由外界指定文件名,那么文件名中不能包括目录名(例如“../”等),就可以尽量降低目录遍历漏洞产生的风险。表示目录 的 字 符 包 括“/”,“”?等,根据系统的不同存在差异。例如将上述程序修改为”等,利用指定函数来读取传入文件名,对其分析后得到末尾的文件名。
这样,即使黑客传入了非法文件名,其中包含的杂乱信息会被自动过滤,Web程序只得到末尾的文件名,当进行读取时,只能得到错误信息。对传入文件名的内容进行限制,只允许包含字母和数字,也可以防御该漏洞。
在有些网站中,因为误操作等原因,在公开目录中有时可能存储着对外保密的文件。这样外界用户只要知道了文件地址,就可以轻松的浏览其内容。为防止出现上述情况,可以采取尽量不要在公开目录放置敏感文件,对文件设置合适的访问权限,或者直接禁用目录列表功能等方法来解决该问题。
如果目录列表功能没有被禁用,当使用URL指定目录名称时,网页上会显示目录中的所有文件。例如在Web服务器上执行“vim/etc/httpd/conf/httpd.conf”命令,可看到公开目录信息。在对应的“”等语句中显示对应公开目录的属性信息,其中“xxx”表示目录名称,在“Options”栏中应该只包含“Includes ExecCGI FollowSymLinks”内容,而禁止包含“Indexs”字样,这样该公开目录的列表功能就被禁用了。
此外,对公开目录中的敏感文件名要进行必要处理,例如禁止包含日期、用户名或连续数值等易于被猜到的内容,避免使用诸如“user.dat”、“data.txt”常用文件名等。关闭错误信息显示功能也可在一定程序上避免暴露敏感文件。例如,在PHP配置文件中输入“display_errors = off”行,关闭错误信息显示功能。
当然,最根本的方法还是在设计程序时将重要文件存放到安全目录,在租用服务器时确认可以使用非公开目录等。
在使用PHP等语言开发Web程序时,在很多情况下需要使用一些系统级的命令来实现某些功能。即通过Shell执行操作系统命令,或者在开发中用到的某个方法时,在其内部会利用Shell来执行OS命令等。
在执行这类操作时,就可能出现操作系统命令被任意执行的问题。例如在网页中需要发送邮件时,会使用Linux中 的“sendmail”命令来实现。
例如,在某网页中存在“$mail = $_Post['mail']”、“system("/usr/sbin/sendmail -i 对OS注入漏洞进行分析,可以发现在相关程序内部调用OS命令,多数是通过Shell来启动命令的。 Shell是用来操作OS的命令行界面,例如Windows中的“cmd.exe”,Linux中的“sh”、“bash”、“csh”等。通过Shell来启动命令,能够使得管道命令或者重定向等功能的使用变得更加快捷。但是,Shell提供的遍历功能有时却成为了OS注入的根源。 并且Shell提供了一次启动多个命令的语法,因此,黑客就可能在参数中添加非法内容,在原来命令的基础上让其他的命令被随意执行,这样OS命令注入就产生了。 在Shell中提供了在一个命令行启动多个程序的方法,其中OS命令注入就是恶意利用了该特性。在Windows中的“cmd.exe”程序可以利用“&”字符来连续执行多个命令。此外,还可以 使 用“|”、“&&”、“||”等字符来执行连续执行多个命令等。 在Shell中拥有特殊意义的上述字符被称为元字符,将其作为普通字符使用时需要进行转义。如果在指定的OS命令参数中的字符串中混入了Shell元字符,就会让攻击者非法添加的命令得以顺利执行。 而若要产生OS注入漏洞,则必须满足一些条件,例如包括使用了内部调用的Shell的函数(例如“system”、“open”等),将外界传入的参数传递给内部调用的Shell的函数,参数中的Shell的元字符没有被转义等。 为了防御OS注入漏洞,可以使用各种方法灵活应对。例如选择不调用OS命令的实现方法,即在程序中不通过Shell来调用系统命令。不要将外界输入的字符串传递给命令行参数,使用安全的函数对传递来的参数进行转义等。例如对于上述存在问题的代码来说,可以不使用系统提供的“senmail”命令来实现,使用PHP提供的“mb_send_mail”函数,也可以实现邮件发送功能。 例如将其修改为“”等语句,来执行邮件发送功能。这样,就彻底消除了OS注入漏洞的威胁,还消除了调用OS命令的系统开销,从多个方面提高了应用的性能。 如果必须通过Shell调用OS命令,或者并不了解所使用的函数内部是否使用了Shell的话,那么不将参数传递给命令行,是防御OS命令注入的有效对策。例如对于“sendmail”命令来说,可以使用“-t”参数,让收件人地址不在命令行中指定,而是从邮件的消息头中读取。这样就没有必要将外界输入的参数字符串指派给命令行,即消除了OS命令注入漏洞。 例如将上述代码修改为“ 注意,为了防止邮件头注入漏洞,这里对用户输入的邮箱地址进行了校验,禁止其包含非法内容。但是,在调用“sendmal”命令时,没有任何外界传入的参数,OS命令注入漏洞自然无法成立。 当然,对外界传入的OS命令的参数进行转义处理,也可以有效消除OS命令注入漏洞。例如将上述代码修改为“$mail= $_Post['mail']”、“system("/usr/sbin/sendmail -i OS注入漏洞产生的根源
防御OS注入的对策