第14章 PHP安全
PHP的语法过于灵活,这也给安全工作带来了一些困扰。同时PHP也存在很多历史遗留的安全问题。
PHP语言的安全问题有其自身语言的一些特点。
14.1 文件包含漏洞
- 文件包含漏洞是“代码注入”的一种。
- 文件包含可能会出现在JSP、PHP、ASP等语言中。
- 常见的导致文件包含的函数如下:
- PHP: include(), include_once(), require(), require_once(), fopne(), readfile(), …
- JSP/Servlet: ava.io.File(), java.io.FileReader(), …
- ASP: include file, include virtual, …
- 文件包含是PHP的一种常见用法,主要由4个函数完成:
- include()
- require()
- include_once()
- require_once()
- 当使用这4个函数包含一个新的文件时,该文件将作为PHP代码执行,PHP内核并不会在意该被包含的文件是什么类型。
- 这一特性,在实施攻击时将非常有用。
- 要想成功利用文件包含漏洞,需要满足下面两个条件:
- include()等函数通过动态变量的方式引入需要包含的文件
- 用户能够控制该动态变量
14.1.1 本地文件包含
- 能够打开幷包含本地文件的漏洞,被称为本地文件包含漏洞(Local File Inclusion,简称LFI)
- 字符串截断的技巧,也是文件包含中最常见的技巧。
- 在连接字符串时,0字节(\x00)将作为字符串结束符。
- PHP内核是由C语言实现的,因此使用了C语言中的一些字符串处理函数。
- 在一般的Web应用中,0字节用户其实时不需要使用的,因此完全可以禁用0字节。
- 利用操作系统对目录最大长度的限制,可以不需要0字节而达到截断的目的。
- 在连接字符串时,0字节(\x00)将作为字符串结束符。
- 除了include()等4个函数外,PHP中能够对文件进行操作的额函数都有可能出现漏洞。能够读取敏感文件带来的后果也是比较严重的。
- fopen()
- fread()
- 文件包含漏洞能够读取敏感文件或者服务器端脚本的源代码,从而为攻击者实施进一步攻击奠定基础。
- 目录遍历(Path Traversal)漏洞
- 跨越目录读取文件
- 当PHP配置了open_basedir时,将很好地保护服务器,使得这种攻击无效。
- open_basedir的作用是限制在某个特定目录下PHP能打开的文件,其作用与safe_mode是否开启无关。
- 要解决文件包含漏洞,应该尽量避免动态的变量,尤其是用户可以控制的变量。
- 一种变通方式:使用枚举。
14.1.2 远程文件包含
- 如果PHP的配置选项allow_url_include为ON的话,则include/require函数是可以加载远程文件的。这种漏洞被称为远程文件包含漏洞(Remote File Inclusion,简称RFI)。
- 远程文件包含漏洞可以直接用来执行任意命令。
14.1.3 本地文件包含的利用技巧
- 本地文件包含漏洞,其实也是有机会执行PHP代码的,这取决于一些条件。
- 远程文件包含漏洞之所以能够执行命令,就是因为攻击者能够自定义被包含的文件内容。因此本地文件包含漏洞想要执行命令,也需要找到一个攻击者能够控制内容的本地文件。
- 一些常用的技巧,用于本地文件包含后执行PHP代码:
- 包含用户上传的文件
- 包含data://或php://input等伪协议
- 包含Session文件
- 包含日志文件,比如Web Server的access log
- 包含/proc/self/environ文件
- 包含上传的临时文件(RFC1867)
- 包含其他应用创建的文件,比如数据库文件、缓存文件、应用日志等,需要具体文件具体分析。
- 上述技巧的具体讨论
14.2 变量覆盖漏洞
- 安全建议:
- 确保register_globals=OFF。若不能自定义php.ini,则应该在代码中控制。
- 熟悉可能造成变量覆盖的函数和方法,检查用户能否控制变量的来源。
- 养成初始化变量的好习惯。
14.2.1 全局变量覆盖
14.2.2 extract()变量覆盖
14.2.3 遍历初始化变量
14.2.4 import_request_variable变量覆盖
14.2.5 parse_str()变量覆盖
14.3 代码执行漏洞
- PHP终端代码执行情况非常灵活,但是依然离不开两个关键条件:
- 用户能够控制的函数输入
- 存在可以执行代码的危险函数
14.3.1 “危险函数”执行代码
PHP中,能够执行代码的方式远不止文件包含漏洞一种:
- 比如危险函数popen()、system()、passthru()、exec()等都可以执行系统命令。
- eval()函数可以执行PHP代码
- 允许用户上传PHP代码
- 应用写入到服务器的文件内容和文件类型可以由用户控制
几个真实案例
14.3.2 “文件写入”执行代码
14.3.3 其他执行代码方式
- 直接执行代码的函数
- eval()
- assert()
- system()
- exec()
- shell_exec()
- passthru()
- escapeshellcmd()
- pcntl_exec()
- 文件包含
- include()
- include_once()
- require()
- require_once()
- 本地文件写入
- preg_replace()代码执行
- 动态函数执行
- Curly Syntax
- 回调函数执行代码
- unserialize()导致代码执行
14.4 定制安全的PHP环境
- 熟悉各种PHP漏洞
- 配置php.ini来加固PHP的运行环境
- 推荐php.ini中一些安全相关参数的配置
- register_globals
- open_basedir
- allow_url_include
- display_errors
- log_errors
- magic_quotes_gpc
- cgi.fix_pathinfo
- session.cookie_httponly
- session.cookie_secure
- safe_mode
- disable_functions
- 推荐禁用的类
- XMLWriter
- DOMDocument
- DOMNotation
- DOMXPath
- SQLiteDatabase
- SQLiteResult
- SQLiteUnbuffered
- SQLiteException
- 推荐php.ini中一些安全相关参数的配置
14.5 小结
本章先后介绍了PHP中一些特别的安全问题,比如文件包含漏洞、代码执行漏洞,最后对如何定制一个安全的PHP环境给出了建议。