什么是代码审计
代码审计(Code audit)是一种以发现程序错误,安全漏洞和违反程序规范为目标的源代码分析。它是防御性编程范例的一个组成部分,旨在程序发布之前减少错误。
C和C ++源代码是最常见的审计代码,因为其他高级语言,例如Python,它的底层语言是c语言,很多实现是先都经过处理,再被python封装,易受到攻击的功能相较减少(例如,不检查边界的函数漏洞在python中几乎不需要考虑)。
代码审计对象包括但不限于对Windows和对Linux系统下的以下语言进行审核:java,C,C#,ASP,PHP,JSP,.NET。
本系列代码审计文章主要针对PHP语言展开,第一课首先为大家讲解一些PHP基础。
php网页工作原理
要学习PHP代码审计,需要先了解PHP网页由哪些部分组成。
网页中有动态语言和静态语言,具体区别看下表。
动态语言 | 静态语言 |
---|---|
PHP语言 | html,文本文件,图片文件 |
需要专门解释器才能被服务器识别 | 可以被服务器(Nginx,apache等web服务器)直接识别 |
对于每个客户端的返回有可能不一样的 | 返回固定 |
CGI协议,即公共网关接口,是工作在Web Server与 Web Application之间,实现数据交换,使得解释器和服务器可以通信的一种接口协议,可以理解为是处理动态语言的协议或者一种程序,但是CGI处理一个请求需要fork一个进程,处理结束关闭进程,非常浪费CPU资源。
于是引进了FastCGI,实现了master进程worker进程常驻,优化了效率。
Web服务器和CGI接口另外设置了一些环境变量,用来向CGI程序传递一些重要的参数。
下面是一些常用的CGI环境变量:
变量名 | 描述 |
---|---|
CONTENT_TYPE | 这个环境变量的值指示所传递来的信息的MIME类型。目前,环境变量CONTENT_TYPE一般都是:application/x-www-form-urlencoded,表示数据来自于HTML表单。 |
CONTENT_LENGTH | 如果服务器与CGI程序信息的传递方式是POST,这个环境变量即使从标准输入STDIN中可以读到的有效数据的字节数。这个环境变量在读取所输入的数据时必须使用。 |
HTTP_COOKIE | 客户机内的 COOKIE 内容。 |
HTTP_USER_AGENT | 提供包含了版本数或其他专有数据的客户浏览器信息。 |
PATH_INFO | 这个环境变量的值表示紧接在CGI程序名之后的其他路径信息。它常常作为CGI程序的参数出现。 |
QUERY_STRING | 如果服务器与CGI程序信息的传递方式是GET,这个环境变量的值即使所传递的信息。这个信息跟在CGI程序名的后面,两者中间用一个问号'?'分隔。 |
REMOTE_ADDR | 这个环境变量的值是发送请求的客户机的IP地址,例如上面的192.168.1.67。这个值总是存在的。而且它是Web客户机需要提供给Web服务器的唯一标识,可以在CGI程序中用它来区分不同的Web客户机。 |
REMOTE_HOST | 这个环境变量的值包含发送CGI请求的客户机的主机名。如果不支持你想查询,则无需定义此环境变量。 |
REQUEST_METHOD | 提供脚本被调用的方法。对于使用 HTTP/1.0 协议的脚本,仅 GET 和 POST 有意义。 |
SCRIPT_FILENAME | CGI脚本的完整路径 |
SCRIPT_NAME | CGI脚本的的名称 |
SERVER_NAME | 这是你的 WEB 服务器的主机名、别名或IP地址。 |
SERVER_SOFTWARE | 这个环境变量的值包含了调用CGI程序的HTTP服务器的名称和版本号。例如,上面的值为Apache/2.2.14(Unix) |
Web Server | 特点 | 优点 | 缺点 |
---|---|---|---|
Nginx | 基于事件驱动 | 性能、负载均衡,稳定性高,支持热部署 | / |
Apache | 基于进程驱动 | 支持几乎所有平台,组件多 | 系统压力大,不支持热部署 |
IIS | 最适合ASP.NET、ASP | 产品相较成熟 | 只能运行在Windows平台 |
php核心配置
代码在不同环境下执行结果会有不同,不同的版本会指令的变更,因此代码审计需要能够很熟悉各个版本配置文件的核心指令,以达到以下两个基本目的:
php配置中的几种模式及含义如下:
模式 | 含义 |
---|---|
PHP_INI_USER | 可在用户脚本(例如ini_set())或者windows注册表以及.user.ini中设定 |
PHP_INI_PERDIR | 可在php.ini, .htaccess或httpd.conf设定 |
PHP_INI_SYSTEM | 可在php.ini或https.conf中设定 |
PHP_INI_ALL | 可在任何地方设定 |
.user.ini是用户自定义的小型php.ini,会影响到PHP_INI_USER, PHP_INI_PERDIR模式下的配置,除了主php.ini之外,PHP还会在每个目录下扫描.user.ini文件,且.user.ini能被动态加载,修改后不需要重启服务器。
审计中的常涉及的配置
隐患:直接用户get,post提交上来的参数注册成全局变量并初始化值为参数对应的值,使参数可以直接在脚本中使用。
开启后会对get, post,cookie变量中的单引号,双引号,反斜杠,以及空字符(NULL)前面加上反斜杠。
隐患:若在代码层再一次对单引号等进行特殊的转换过滤,可能造成过滤失效。
与magic_quotes_gpc相同,是在单引号,双引号,反斜杠以及空字符前面加反斜杠,区别是magic_quotes_runtime是对从数据库或者文件中获取的数据进行过滤。
隐患:当该项开启时,若在代码层再一次对单引号等进行特殊的转换过滤,可能造成过滤失败。
magic_quotes_gpc也开启时,会覆盖掉magic_quotes_qpc的配置,同样对get ,post,cookie进行处理,但是仅仅是把转移空字符和单引号变成双单引号(用单引号来转义单引号)。
Magic_quotes_sybase=0时,addslashes将对'' ’ \进行\转义操作;
Magic_quotes_sybase=1时,addslashes将对‘进行’‘转义操作。
隐患:当该项与magic_quotes_gpc同时开启时,可能造成参数意外闭合。
实例如下:
PHP的安全模式是个非常重要的内嵌的安全机制,能够控制一些PHP中的函数是否能够正常使用,比如system(),同时对很多文件操作函数进行权限控制,也不允许对某些关键文件进行操作,比如/etc/passwd,除非使用safe_mode_include_dir和safe_mode_exec_dir指定一个可被包含和存放了外部脚本的安全目录。
隐患:当该项开启时,如果本身敏感操作的拼接就为双引号,可能造成参数意外闭合。
使include(), include_once(),require(),require_once()都可以引入url type的协议,允许其包含远程文件,
allow_url_fopen :是否允许将URL(如http://或ftp://)作为文件处理。
allow_url_include :是否允许include/require打开URL(如http://或ftp://)作为文件处理。
隐患:从PHP5.2开始allow_url_include就默认为Off,而allow_url_fopen一直是On的。在文件包含漏洞中,会产生远程文件包含的动作,增加攻击面。
Open_basedir是PHP设置中为了防御PHP跨目录进行文件(目录)读写的方法,所有PHP中有关文件读、写的函数都会经过open_basedir的检查。将PHP所有能打开的文件限制在指定的目录树,包括文件本身。
本指令不受安全模式打开或者关闭的影响。
隐患:虽然在PHP5.3以后很少有能够绕过open_basedir读写文件的方法,但是有很多可以绕过open_basedir的限制对其进行输出目录的绕过方法。
可以禁止一些敏感函数的使用。
一个目前推荐的禁用函数列表有:
函数 | 作用 |
---|---|
dl() | 载入指定参数的 PHP 扩展 |
exec() | 执行一个外部程序 |
system() | 执行外部程序,并且显示输出 |
passthru() | 同 exec() 函数类似, passthru() 函数 也是用来执行外部命令的 |
proc_open() | 执行一个命令,并且打开用来输入/输出的文件指针 |
pcntl_exec() | 在当前进程空间执行指定程序,可指定参数 |
shell_exec() | 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回 |
mail() | 发送邮件 |
imap_open() | 打开 IMAP 流,参数 username 为用户帐号。参数 password 为用户的密码 |
imap_mail() | 通过IMAP发送邮件 |
Putenv() | 添加 setting 到服务器环境变量。环境变量仅存活于当前请求期间。在请求结束时环境会恢复到初始状态 |
ini_set() | 设置指定配置选项的值。这个选项会在脚本运行时保持新的值,并在脚本结束时恢复 |
apache_setenv() | 设置 Apache 子进程环境变量 |
symlink() | 建立符号连接 |
link() | 建立一个硬连接 |
在调试PHP时,打开错误信息,设置错误显示级别。
隐患:敏感信息泄露,在审计中,开启错误信息显示可以更加直观的了解数据产生的错误提示,会暴露目录,绝对路径,造成SQL注入的错误信息。
自动在文件的尾或头处通过require引入特定的文件。
隐患:通过上传.user.ini可能造成上传白名单扩展名的文件也能getshell。