PHP 面试题:PHP 中 SQL 注入、XSS、CSRF 的防御方案

在 PHP 面试中,Web 安全几乎是绕不开的核心考点。SQL 注入、XSS、CSRF 作为最常见的三类安全漏洞,不仅考察候选人对攻击原理的理解,更考验其在真实项目中的防御实践能力。本文将从攻击原理出发,逐一剖析这三类漏洞的防御方案,并给出可直接落地的 PHP 代码示例。

一、SQL 注入的防御

攻击原理

SQL 注入的本质是用户输入被拼接进 SQL 语句后,改变了原有语义。例如:

$sql = "SELECT * FROM users WHERE name = '$_GET[name]'";

当用户传入 ' OR '1'='1 时,查询条件恒真,攻击者即可绕过认证甚至拖库。

防御方案

1. 首选:预处理语句(Prepared Statements)

使用 PDO 或 MySQLi 的预处理功能,将 SQL 结构与数据分离,是防御 SQL 注入最根本的手段。

$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // 使用真正的预处理

$stmt = $pdo->prepare('SELECT * FROM users WHERE name = :name AND status = :status');
$stmt->execute([':name' => $_GET['name'], ':status' => 1]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

关键点:关闭 ATTR_EMULATE_PREPARES,避免 PDO 在客户端模拟预处理导致转义失效。

2. 输入验证与类型约束

对于明确类型的参数(如 ID),强制类型转换是最简单有效的防线:

$id = (int)($_GET['id'] ?? 0);

3. 最小权限原则

数据库账号不应使用 root,只授予业务所需的 SELECT/INSERT/UPDATE/DELETE 权限,禁止 DROP、FILE 等危险操作。

4. 避免拼接,禁止使用 mysql_* 系列函数

mysql_query 等旧扩展已在 PHP 7 中移除,但仍需警惕项目中遗留的字符串拼接写法。表名、字段名无法用预处理时,应使用白名单校验。

二、XSS 的防御

攻击原理

XSS(跨站脚本)是攻击者将恶意脚本注入页面,在其他用户浏览器中执行。常见于评论区、用户昵称、搜索回显等位置。分为存储型、反射型和 DOM 型三类。

防御方案

1. 输出转义(核心原则:永远不要信任用户输入)

在将数据输出到 HTML 上下文时,使用 htmlspecialchars 转义:

echo htmlspecialchars($userInput, ENT_QUOTES | ENT_HTML5, 'UTF-8');

注意必须指定 ENT_QUOTES 同时转义单双引号,并显式指定字符集,防止宽字节绕过。

2. 区分上下文进行转义

不同上下文需要不同的转义策略:

  • HTML 内容:htmlspecialchars
  • HTML 属性:同样使用 htmlspecialchars,属性值必须加引号
  • JavaScript 变量:使用 json_encode($data, JSON_HEX_TAG | JSON_HEX_AMP)
  • URL 参数:使用 urlencode

3. 富文本场景使用 HTML Purifier

若业务允许用户提交富文本,不能简单转义,应使用白名单过滤库:

$purifier = new HTMLPurifier();
$clean = $purifier->purify($_POST['content']);

4. 设置 CSP 响应头

内容安全策略(CSP)作为纵深防御,可限制脚本来源:

header("Content-Security-Policy: default-src 'self'; script-src 'self'");

5. Cookie 设置 HttpOnly

防止脚本读取敏感 Cookie:

setcookie('session_id', $sid, ['httponly' => true, 'secure' => true, 'samesite' => 'Lax']);

三、CSRF 的防御

攻击原理

CSRF(跨站请求伪造)利用用户已登录的 Cookie,诱导其在不知情下向目标站点发起请求,如转账、改密等。攻击者无需获取 Cookie,只需构造请求即可。

防御方案

1. CSRF Token(主流方案)

在表单中嵌入一次性令牌,服务端校验:

session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['csrf_token'];

表单中:

<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($token) ?>">

服务端校验:

if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
    http_response_code(403);
    exit('CSRF token 校验失败');
}

使用 hash_equals 进行时间恒定比较,防止时序攻击。

2. SameSite Cookie 属性

将 Session Cookie 设置为 SameSite=LaxStrict,可有效阻断跨站请求携带 Cookie:

session_set_cookie_params([
    'samesite' => 'Lax',
    'httponly' => true,
    'secure'   => true,
]);

3. 校验 Referer / Origin

对敏感操作校验请求来源:

$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (!in_array($origin, ['https://example.com'], true)) {
    exit('非法来源');
}

4. 关键操作二次验证

对于转账、修改密码等高风险操作,要求输入密码或短信验证码,从根本上降低 CSRF 危害。

四、面试答题要点总结

漏洞 核心防御 补充措施
SQL 注入 预处理语句 + 参数绑定 最小权限、类型转换、白名单
XSS 输出转义(按上下文) CSP、HttpOnly、HTML Purifier
CSRF CSRF Token + SameSite Referer 校验、二次验证

面试中回答此类问题,建议遵循“原理 → 危害 → 防御 → 代码示例”的结构,并强调纵深防御思想:没有单一方案能解决所有问题,需要多层防护协同。同时要体现对细节的把握,例如 PDO::ATTR_EMULATE_PREPARES 的关闭、htmlspecialchars 的字符集参数、hash_equals 的时序安全比较,这些细节往往是区分候选人水平的关键。

掌握这三类漏洞的防御,不仅是面试加分项,更是每一位 PHP 工程师写出安全代码的基本功。

未经允许不得转载:任鹏个人博客 » PHP 面试题:PHP 中 SQL 注入、XSS、CSRF 的防御方案

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏