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编码风险:
0xbf27经addslashes()转义为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安全》(吴翰清著)系统学习安全知识。所有安全措施应形成组合防护,单一防护手段无法应对复杂攻击场景。