读《PHP安全之道》提纲挈领笔记
四、PHP项目中常见漏洞与防护
1、SQL注入漏洞
(1)SQL注入
在处理程序时,未对用户可控参数进行严格校验,如利用字符串拼接的方式构造SQL语句在数据库中执行,很容易埋下安全隐患。
SQL注入可以造成数据库信息泄露。
通过操作数据库对特定网页进行篡改,修改数据库一些字段值,嵌入恶意链接,进行挂马攻击,传播恶意软件。
服务器还容易被远程控制,修改操作系统以及破坏磁盘,使系统瘫痪。
SQL注入攻击方式:①报错注入②普通注入③隐式类型注入④盲注⑤宽字节注入⑥二次解码注入
(2)报错注入
test.php?name=zhangsan'errr
select * from test where name ='zhangsan'errr'
恶意攻击者使用特殊的方式使数据发生错误并产生报错信息,从而获得数据库和系统信息,方便攻击者进行下一步攻击。
要对传入参数进行严格处理。
数据库报错,攻击者可以通过这种方式来获取MySQL的各类信息。防止报错信息被攻击者直接看到,需设置display_errors=Off
(3)普通注入
test.php?name=zhangsan'or'a'='a //组合成万能查询语句
select * from test where name ='zhangsan'or'a'='a'
后面随意拼接,可能会导致数据库数据泄漏和数据删除,如果没有对数据进行备份,将对系统造成的伤害是毁灭性的。
(4) 隐式类型注入
select * from test where where email=0;
把查询语句误写,如果SQL语句中的字段类型和对应表中的字段数据类型不一致,MySQL查询优化器会将数据的类型进行隐式转换。其中 email 是 varchar 类型。
下表是MySQL类型转换规则:
(5)盲注
盲注容易被忽略。
攻击者可以利用延时等技术实现发现和利用注入漏洞。
select * from test where if((MID(version(),1,1)) LIKE 5,sleep(5),1) limit 0,1;
利用 BENCHMARK()
函数进行延时注入。
(IF (MID(version(),1,1) like 5,BENCHMARK(100000,SHA1('true')),false))
该请求会使MySQL的查询睡眠5秒,攻击者可以通过添加判断条件到SQL语句中。如果睡眠5秒,那么说明MySQL版本是5。
(6)宽字节注入
使用addslashes()函数可以使“’”变成“’”。输入 “%81’”
id=%81%27
数据库和程序编码都是GBK编码的需要注意。当输入的第一个字符的ASCII码大于128时。GBK是多字节编码,PHP认为两个字节代表一个汉字,所以输入%81和后面的反斜杠%5c变成一个汉字“乘”,造成反斜杠消失。
(7)二次解码注入
防止SQL注入,通常采用转义特殊字符。如单引号(’)、双引号(")转义成’"。有一个误区就是通过配置PHP的GPC开关进行自动转义。
?name=name%2527
当PHP自动URL解码,变成name%27,然后代码里再使用urldecode()
或rawurldecode()
函数进行解码时,%27就变成单引号。URL最终变成name=name’ 引发SQL注入。
2、SQL注入漏洞防护
SQL注入是最危险的漏洞之一,也是最好防护的漏洞之一。合理利用MySQL提供的预编译进行sql注入防护,在PHP中使用PHP数据对象或MySQL扩展连接数据库,并对SQL语句进行预编译处理。
如果在项目中无法使用预编译来防止SQL注入,可以采用传统方式来验证用户输入是否合法,严格控制输入参数的数据类型,过滤非法字符。
(1)MySQL预编译处理
MySQL预编译处理分为编译、执行、释放。语编译遵循指令和数据分离原则。
编译。
通过PREPARE stmt_name FROM preparable_stm
来编译一条SQL语句。
> PREPARE test FROM 'insert into test select ?,?,?,? ';
通过 EXECUTE stmt_name [USING @var_name,[,@var_name]…]的语法来执行预编译语句。
>set @name='zhangsan',@email='test@qq.com',@password='123456789',@status=1;
>execute test using @name,@email,@password,@ststus;
MySQL的预编译语句作用域是会话级,但可以通过max_prepared_stmt_count 变量来控制全局最大存储的预编译语句。
> set @@global.max_prepared_stmt_count=1;
当预编译条数达到阈值时,则报错。
如果要释放一条预编译的语句,则可以使用 {deallocate | drop} prepare stmt_name 的语法进行操作。
> deallocate prepare test;
同一个处理操作,SQL语句只编译一次。
(2)PHP使用MySQL的预编译处理
SQL使用预编译处理:增强系统安全性;提高系统的执行效率。
PHP 可以通过PDO 模块进行SQL预编译处理。
可参照手册学习:https://www.php.net/manual/zh/book.pdo.php
其中,在默认的情况下,PDO没有让MySQL数据库执行真正的预处理语句。应禁止PDO模拟预处理语句。
添加 ATTR_EMULATE_PREPARES (false)
,ATTR_ERRMODE ( ERRMODE_EXCEPTION)
属性 。
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
客户端执行SQL 只需传参数,预编译处理分离了参数与SQL语句。
(3)校验和过滤
如果无法使用MySQL预编译处理,防止前面几种注入方式,需要对输入的数据进行有效的检验和过滤。
以下是常用的检验变量函数:
除了函数,也可以使用正则处理。
过滤方式需要结合业务,使用不当可能会对现有业务造成影响。
(4)宽字节注入防护
在SQL查询前,使用 intval 对变量进行强制转换。
可以使用 mysql_real_escape_string
(php5.5起已废弃,php7开始移除)进行防御,在使用前需要 mysql_set_charset
指定字符集才能生效。
还可以将 character_set_client
设置成 binary
,设置成二进制格式就不存在宽字节或多字节问题。
(5) 禁用魔术引号
关闭魔术引号并不能有效解决SQL注入,但可以提高性能。
magic_quotes_gpc=Off;//关闭魔术引号
3、XML注入漏洞防护
XML解析依赖于libxml库,默认支持并开启外部实体的引用。XML漏洞被使用可读取服务器任意文件、执行代码、发起DDos攻击。
(1)对用户的输入过滤 如 <、>、’、"、&等。
(2)XML解析方法有 DOMDocument
、SimpleXM
L、XMLReader
。在PHP解析XML文件之前使用libxml_disable_entity_loader(true)
来禁止加载外部实体。使用 libxml_use_internal_errors()
禁止报错。
4、邮件安全
(1)邮件注入
注意%0A。SMTP区分消息头部和消息主题是依据%0A%0A双换行符决定的,消息头里的属性是以%0A区分的,因此用户如果自行将%0A%0A或%0A写入到变量,会直接控制消息体控制被发送对象以及内容。
接收用户的请求后要进行严格的过滤,防止用户恶意注入。
(2)防止邮件注入
使用filter_var()
函数。
(PHP 5 >= 5.2.0, PHP 7)
filter_var — 使用特定的过滤器过滤一个变量。
邮箱过滤使用:
var_dump(filter_var('bob@example.com', FILTER_VALIDATE_EMAIL));
//其中,FILTER_SANITIZE_EMAIL Remove all characters except letters, digits and !#$%&'*+-=?^_`{|}~@.[].
对用户自定义的内容过滤。
5、PHP组件漏洞防护
PHP文件(框架,模块)总是以全部权限运行,如果使用了一个带有漏洞的组件,这种攻击可能对数据和服务器造成损失。防止此类漏洞,一定要时刻关注该软件的更新,使用最新的组件。
(1)RSS安全漏洞
聚合内容(really simple syndication,RSS)是在线共享内容的一中简易方式。
如果RSS是不受信任的信息来源,很可能被注入script或者html标签。这些恶意标签很可能攻击浏览器。
在转发前,必须使用可靠的过滤表进行过滤,或者它们必须过滤特定的字符集。
受攻击应用:
① 本地RSS阅读器。 如foxmal
②web RSS 阅读器。如 Google reader
(2) PHPMailer漏洞
在 5.2.18以前存在一个漏洞,攻击者利用该漏洞可在web服务器中执行任意远程代码。
在5.2.21及以前存在任意文件读取漏洞。
(3)OpenSSL漏洞
OpenSSL是一个安全套接字层密码库,包括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议。
①OpenSSL 1.0.1n 、 1.0.1o、 1.0.2b、 1.0.2c版本,crypto/x509/x509_vfy.c内的函数X509_verify_cert,在替换证书链过程识别中没有正确处理X.509 Base Constraints CA 值,存在安全漏洞。利用此漏洞发布无效证书。
②Heartbleed漏洞。由于未在 memcpy() 调用客户端输入内容作为长度参数之前正确进行边界检查,攻击者可追踪OpenSSL64k缓存。
(4)SSL v2.0协议漏洞
SSL v2.0主要存在以下问题。
①同一加密密钥用于消息身份验证和加密。
②弱消息认证代码结构和只支持不安全的MD5散列函数。
③ SSL 握手过程没有采取任何防护,这意味着非常容易遭遇中间人攻击。
④使用TCP连接关闭,以指示数据的末尾(没有明确的会话关闭通知)。这意味着截断攻击是可能的:攻击者只需伪造一个TCP FIN,使得接收方无法识别数据结束消息的合法性。
⑤仅能提供单一服务和绑定一个固定域名,这与Web服务器中的虚拟主机标准功能有冲突,这意味着许多网站无法使用SSL。
6、文件包含安全
通过PHP函数引用文件。
(1)文件包含漏洞
按文件包含形式分:简单文件包含漏洞、受限制的文件包含漏洞、Zip文件包含漏洞、远程文件包含漏洞等。
对客户端输入参数进行检验和过滤,限制文件类型。
(2)避免文件包含漏洞
include()
、include_once()
、require()
、requre_once()
、spl_autoload()
。
带 once 的函数 PHP会检查该文件是否已经被包含过,如果是则不会再次包含。
require在出错时产生E_COMPILE_ERROR
级别的错误。换句话说,就是require将导致脚本中止,而include只产生警告 (E_WARNING
),脚本会继续运行。
spl_autoload — __autoload()函数的默认实现,如果不使用任何参数调用
spl_autoload_register() 函数,则以后在进行 __autoload() 调用时会自动使用此函数。
在使用上述函数时要注意以下问题。
①保证接收的用户参数不可构造成文件路径。
②禁用远程访问,修改PHP配置。
③指定默认文件名称和路径,不允许用户自行传递文件名称。
④使用basename进行过滤。
7、系统命令注入
攻击者提交恶意的shell命令,通过PHP函数执行。
(1)易发生命令注入的函数
如exec()
、system()
,passthru()
、popen()
、shell_exec()
、pcntl_exec()
等。
(2)防御命令注入
①尽量避免使用此类函数,避免从用户端接收执行命令。
②如果必须使用,执行命令的参数应禁止外部获取,防止用户伪造。
③php.ini 设置 safe_mode=On
。使用 disable_functions
禁用这些函数
disable_functions= exec,system,passthru,popen,shell_exec,pcntl_exec
④使用自定义函数或函数库来代替外部命令函数
⑤结合使用escapeshellarg()
、escapeshellcmd()
函数来处理命令参数。
escapeshellarg()函数会将任何引起参数或命令结束的字符转义,单引号“’”替换成“’”,双引号“"”替换成“"”,分号“;”替换成“;”
⑥使用safe_mode_exec_dir
指定可执行文件的路径。
用safe_mode_exec_dir
指定可执行文件的路径,可以把会使用的命令提前放入此路径内。
END
如有问题请在下方留言。
或关注我的公众号“孙三苗”,输入“联系方式”。获得进一步帮助。