PHP安全开发SQL注入与XSS防护
安全问题说大不大说小不小。很多开发者觉得自己的网站没人攻击,等被黑了就晚了。SQL注入和XSS是最常见的两种攻击方式。今天说说怎么在PHP中防护。
SQL注入是通过拼接SQL语句来执行恶意操作。防护方法只有一个——用预处理语句。
```php
// 不安全
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = $id";
$result = $pdo->query($sql);
// 如果id传 1 OR 1=1,全部用户数据就泄露了
// 安全
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);
$user = $stmt->fetch();
?>
```
XSS攻击是通过注入恶意脚本来窃取用户数据。防护方法是在输出时做转义。
```php
// 不安全
$name = $_GET['name'];
echo "欢迎你,$name";
// 如果name传 ,浏览器会执行这个脚本
// 安全
echo "欢迎你," . htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
// 在Blade模板里,{{ $name }} 自动转义
// {!! $name !!} 是原始输出,要慎用
// 不同上下文的转义方式
$jsValue = json_encode($name, JSON_HEX_TAG | JSON_HEX_AMP);
$urlValue = rawurlencode($name);
?>
```
文件上传也是个高危区域。攻击者可能上传PHP脚本,然后直接执行。
```php
function handleUpload(array $file): string
{
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
$allowedExts = ['jpg', 'jpeg', 'png', 'gif'];
$maxSize = 5 * 1024 * 1024;
if ($file['error'] !== UPLOAD_ERR_OK) {
throw new RuntimeException("上传错误");
}
if ($file['size'] > $maxSize) {
throw new RuntimeException("文件太大");
}
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowedExts)) {
throw new RuntimeException("不支持的文件类型");
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mime, $allowedTypes)) {
throw new RuntimeException("MIME类型不匹配");
}
$newName = bin2hex(random_bytes(16)) . '.' . $ext;
$dest = __DIR__ . '/uploads/' . $newName;
move_uploaded_file($file['tmp_name'], $dest);
return $newName;
}
?>
```
CSRF攻击利用用户已登录的身份执行非授权操作。防护方法是在表单里加token验证。
```php
session_start();
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
?>
提交
if (isset($_POST['_token'])) {
if (!hash_equals($_SESSION['csrf_token'], $_POST['_token'])) {
die("CSRF验证失败");
}
}
?>
```
密码存储用password_hash。password_hash自动加盐,每次生成的哈希都不一样,比md5安全得多。
```php
$password = 'UserPassword123!';
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
if (password_verify($password, $hash)) {
echo "密码正确\n";
}
// 检查是否需要重新哈希
if (password_needs_rehash($hash, PASSWORD_ARGON2ID)) {
$newHash = password_hash($password, PASSWORD_ARGON2ID);
}
?>
```
安全开发的核心原则:不要信任用户输入,输出一定要转义,敏感信息要加密存储,关键操作要验证token和权限。把这些规则记牢了,大部分安全问题都能避免。