目录

PHP安全编码指南

PHP安全编码指南

一、基础安全建议

  • 拒绝信任原则:所有用户输入及第三方数据源(包括$_GET$_POST$_FILES$_COOKIE$_SERVER部分参数)均不可信
  • 统一编码规范:HTML/PHP/MySQL均使用UTF-8编码
  • SQL构造规范:使用单引号包裹参数
  • 参数对比规范:正确使用===(严格相等)和==(松散相等)
  • 过滤策略选择:优先采用白名单过滤而非黑名单

二、SQL注入防护

1. 注入原理示例

原始SQL语义:

$_GET['name'] = 'abc';
$name = $_GET['name'];
SELECT * FROM admin WHERE user_name = '$name';

生成SQL:

SELECT * FROM admin WHERE user_name = 'abc';

注入后语义:

$_GET['name'] = "abc' OR 'a'='a";

生成恶意SQL:

SELECT * FROM admin WHERE user_name = 'abc' OR 'a'='a';

2. 防护方案

(1)预编译处理(推荐)

使用PDO/mysqli预处理语句:

$stmt = $pdo->prepare('SELECT * FROM admin WHERE user_name = ?');
$stmt->execute([$name]);

(2)传统防护方案

  • 字符转义:UTF-8环境下使用addslashes()替代mysql_real_escape_string()
  • 类型过滤:整型参数使用intval()强制转换

⚠️ GKB编码风险:0xbf27addslashes()转义为0xbf5c27,数据库可能解析为有效单引号

三、XSS防护

1. 攻击原理示例

原始HTML:

<a href="http://www.xxx.com/?page=<?= $_GET['page'] ?>">xss</a>

注入示例:

$_GET['page'] = '1" onclick="alert(1)';

生成恶意代码:

<a href="http://www.xxx.com/?page=1" onclick="alert(1)">xss</a>

2. 防护方案

(1)HTML输出过滤

echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');

(2)URL参数验证

if (!filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED)) {
    $url = 'https://' . $url; // 自动补全协议
}

(3)富文本处理

采用HTML Purifier等白名单过滤器:

$purifier = new HTMLPurifier();
echo $purifier->purify($rich_text);

四、CSRF防护

1. 攻击示例

<img src="http://b.com?action=del&id=1">

用户在登录状态下访问恶意站点a.com时,浏览器会携带b.com的Cookie执行删除操作

2. 防护方案

生成防伪令牌:

$token = bin2hex(random_bytes(50));
$_SESSION['csrf_token'] = $token;

验证逻辑:

if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    die('非法请求');
}
unset($_SESSION['csrf_token']); // 一次性使用

五、文件上传防护

1. 漏洞示例

$allowExt = ['image/jpeg', 'image/x-png', 'image/gif'];
if (in_array($_FILES['file']['type'], $allowExt)) {
    move_uploaded_file($_FILES['file']['tmp_name'], $_FILES['file']['name']);
}

2. 防护方案

// 严格白名单验证
$allowed = ['jpg' => 'image/jpeg', 'png' => 'image/png'];
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));

if (!isset($allowed[$ext])) die('禁止的文件类型');

// 二次验证MIME类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);

if ($allowed[$ext] !== $mime) die('MIME类型不匹配');

// 安全存储
$secure_name = uniqid() . '.' . $ext;
move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $secure_name);

六、代码执行漏洞防护

1. 危险函数示例

eval($_GET['code']); // 直接执行用户输入

2. 防护方案

  • 禁用危险函数:在php.ini中设置
    disable_functions = eval,assert,passthru,exec,system,proc_open
    
  • 正则替换替代方案
    // 使用preg_replace_callback替代/e修饰符
    preg_replace_callback('/pattern/', function($match) {
        return strtoupper($match[0]);
    }, $input);
    

七、重放攻击防护

1. 攻击示例

http://www.abc.com/index.php?action=alterPassword&uid=123456&token=123

2. 防护方案

// 生成带时效的签名
$timestamp = time();
$signature = hash_hmac('sha256', 
    "$action|$uid|$timestamp", 
    $secret_key
);

// 验证逻辑
if (time() - $timestamp > 300) die('请求超时'); // 5分钟有效期
if (!hash_equals($expected, $signature)) die('签名不匹配');

⚠️ 安全建议:定期使用Seay源代码审计系统进行代码扫描,推荐参考《白帽子讲Web安全》(吴翰清著)系统学习安全知识。所有安全措施应形成组合防护,单一防护手段无法应对复杂攻击场景。