PHP利用PCRE回溯次数限制绕过某些安全限制
字数 1261 2025-08-18 11:37:45

PCRE回溯次数限制绕过安全限制的深入分析与防御

1. 背景与问题描述

在PHP安全开发中,经常使用正则表达式来检测恶意输入,如PHP代码或SQL注入。然而,由于PCRE(Perl Compatible Regular Expressions)库的实现机制,存在一种通过触发回溯次数限制来绕过安全检测的攻击方式。

典型漏洞代码示例:

function is_php($data) {
    return preg_match('/<\?.*[(`;?>].*/is', $data);
}

if(!is_php($input)) {
    // fwrite($f, $input); // 写入文件操作
}

2. PCRE正则引擎原理

2.1 有限状态自动机

正则表达式属于可以被"有限状态自动机"接受的语言类,分为两种类型:

  • DFA (确定性有限状态自动机)

    • 从起始状态开始,逐个字符读取输入
    • 根据正则确定转移状态
    • 直到匹配不上或走完整个输入
    • 性能较好但不支持复杂功能
  • NFA (非确定性有限状态自动机)

    • 从起始状态开始,逐个字符读取输入
    • 与正则表达式进行匹配
    • 如果匹配不上,则进行回溯尝试其他状态
    • 支持更多功能但性能较差

PHP使用的PCRE库基于NFA实现。

2.2 回溯过程分析

以正则/<\?.*[(;?>].*/is匹配输入<?php phpinfo();//aaaaa`为例:

  1. <\?匹配<?php phpinfo();//aaaaa的开头<?
  2. .*贪婪匹配剩余所有字符php phpinfo();//aaaaa
  3. 尝试匹配字符类[(;?>]`但已到字符串末尾
  4. 开始回溯:每次回退一个字符尝试匹配
  5. 直到回退到;位置,完成匹配

此过程涉及大量回溯操作,具体次数取决于输入字符串长度。

3. PHP的PCRE回溯限制

3.1 pcre.backtrack_limit设置

PHP为防止正则表达式拒绝服务攻击(ReDoS),设置了回溯次数上限:

var_dump(ini_get('pcre.backtrack_limit')); // 默认1000000(100万)

3.2 超过限制的后果

当回溯次数超过限制时:

  • preg_match()返回false而非1(匹配成功)或0(匹配失败)
  • 可通过preg_last_error()检测错误类型:
    var_dump(preg_last_error() === PREG_BACKTRACK_LIMIT_ERROR); // true
    

3.3 漏洞利用方法

通过构造超长输入使正则匹配达到回溯限制:

import requests
from io import BytesIO

files = {
    'file': BytesIO(b'aaa<?php eval($_POST[txt]);?>' + b'a' * 1000000)
}
res = requests.post('http://target.com/index.php', files=files)

4. 其他常见漏洞场景

4.1 SQL注入WAF绕过

漏洞代码示例

if(preg_match('/SELECT.+FROM.+/is', $input)) {
    die('SQL Injection');
}

利用方式:通过超长字符串触发回溯限制

4.2 非贪婪模式漏洞

漏洞代码示例

if(preg_match('/UNION.+?SELECT/is', $input)) {
    die('SQL Injection');
}

利用原理

  1. 输入UNION/*aaaaa*/SELECT
  2. .+?非贪婪匹配遇到/停止
  3. S尝试匹配*失败
  4. 回溯使.+?匹配*
  5. 重复此过程导致大量回溯

5. 修复方案

5.1 严格检查返回值

必须使用全等(===)判断preg_match返回值:

if(is_php($input) === 0) {
    // 安全处理逻辑
}

5.2 其他防御措施

  1. 限制输入长度

    if(strlen($input) > 1000) {
        die('Input too long');
    }
    
  2. 使用更高效的正则

    • 避免过度使用.*.+
    • 使用更精确的字符类
  3. 组合防御策略

    function is_php($data) {
        if(strlen($data) > 1000) return true;
        return preg_match('/<\?.*[(`;?>].*/is', $data) === 1;
    }
    

6. 总结

  • PCRE的NFA实现存在回溯特性
  • PHP默认回溯限制为100万次
  • 不正确的返回值检查会导致安全绕过
  • 防御关键在于:
    • 严格使用===判断返回值
    • 限制输入长度
    • 优化正则表达式模式

通过深入理解PCRE工作原理和PHP的实现细节,开发者可以更有效地防御此类安全漏洞。

PCRE回溯次数限制绕过安全限制的深入分析与防御 1. 背景与问题描述 在PHP安全开发中,经常使用正则表达式来检测恶意输入,如PHP代码或SQL注入。然而,由于PCRE(Perl Compatible Regular Expressions)库的实现机制,存在一种通过触发回溯次数限制来绕过安全检测的攻击方式。 典型漏洞代码示例: 2. PCRE正则引擎原理 2.1 有限状态自动机 正则表达式属于可以被"有限状态自动机"接受的语言类,分为两种类型: DFA (确定性有限状态自动机) : 从起始状态开始,逐个字符读取输入 根据正则确定转移状态 直到匹配不上或走完整个输入 性能较好但不支持复杂功能 NFA (非确定性有限状态自动机) : 从起始状态开始,逐个字符读取输入 与正则表达式进行匹配 如果匹配不上,则进行回溯尝试其他状态 支持更多功能但性能较差 PHP使用的PCRE库基于NFA实现。 2.2 回溯过程分析 以正则 /<\?.*[( ;?>].* /is 匹配输入 <?php phpinfo();//aaaaa ` 为例: <\? 匹配 <?php phpinfo();//aaaaa 的开头 <? .* 贪婪匹配剩余所有字符 php phpinfo();//aaaaa 尝试匹配字符类 [( ;?>] ` 但已到字符串末尾 开始回溯:每次回退一个字符尝试匹配 直到回退到 ; 位置,完成匹配 此过程涉及大量回溯操作,具体次数取决于输入字符串长度。 3. PHP的PCRE回溯限制 3.1 pcre.backtrack_ limit设置 PHP为防止正则表达式拒绝服务攻击(ReDoS),设置了回溯次数上限: 3.2 超过限制的后果 当回溯次数超过限制时: preg_match() 返回 false 而非 1 (匹配成功)或 0 (匹配失败) 可通过 preg_last_error() 检测错误类型: 3.3 漏洞利用方法 通过构造超长输入使正则匹配达到回溯限制: 4. 其他常见漏洞场景 4.1 SQL注入WAF绕过 漏洞代码示例 : 利用方式 :通过超长字符串触发回溯限制 4.2 非贪婪模式漏洞 漏洞代码示例 : 利用原理 : 输入 UNION/*aaaaa*/SELECT .+? 非贪婪匹配遇到 / 停止 S 尝试匹配 * 失败 回溯使 .+? 匹配 * 重复此过程导致大量回溯 5. 修复方案 5.1 严格检查返回值 必须使用全等( === )判断 preg_match 返回值: 5.2 其他防御措施 限制输入长度 : 使用更高效的正则 : 避免过度使用 .* 和 .+ 使用更精确的字符类 组合防御策略 : 6. 总结 PCRE的NFA实现存在回溯特性 PHP默认回溯限制为100万次 不正确的返回值检查会导致安全绕过 防御关键在于: 严格使用 === 判断返回值 限制输入长度 优化正则表达式模式 通过深入理解PCRE工作原理和PHP的实现细节,开发者可以更有效地防御此类安全漏洞。