CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
WordPress 开发入门/课时/安全加固
RawBlame
课时目录12 节

WordPress 开发入门

  1. 01WordPress 概览
  2. 02环境搭建
  3. 03主题开发
  4. 04区块编辑器
  5. 05插件生态
  6. 06Hooks 与 CPT
  7. 07PHP 架构
  8. 08数据库
  9. 09AJAX 与 Headless
  10. 10安全加固
  11. 11性能与运维
  12. 12综合练习

安全加固

角色、防火墙与审计。

30 分钟

安全加固

这一课你将能做到

  1. 建立「进站净化 → 存库参数化 → 出站转义」的全链路防线,对 XSS/SQL 注入/CSRF 三大攻击逐一落地防御
  2. 用 nonce + current_user_can 写出防伪造、防越权的表单与 AJAX 处理
  3. 交出一份生产环境加固清单:密钥、权限、登录保护、更新策略

为什么要学

WP 是全球攻击面最大的 PHP 应用之一:43% 的网站份额让它成为自动化扫描的头号目标。绝大多数被黑站点不是因为 WP 本身,而是插件漏洞 + 开发者不做转义/校验。安全不是选修课——你的站点挂了挂马、SEO 被拉黑,损失立竿见影。

人话:三类攻击各管一段路。SQL 注入管「进」(用户输入拼进了 SQL);XSS 管「出」(脏数据被原样输出,在别人浏览器里执行);CSRF 管「请求来源」(坏人页面替已登录用户发请求)。WP 给你的对策分别是 prepare、esc_* 家族、nonce + 能力检查。

先建立图像

用户输入 ──① sanitize 净化──→ 入库($wpdb->prepare / API)
                                    │
输出到页面 ←──③ esc_* 转义 ←───────┘
写操作请求 ──② nonce 验来源 + current_user_can 验身份──→ 执行

口诀:进净化、出转义、写校验。三道闸缺一不可。

跟着做

步骤 1:出站转义家族(escape)

// 纯文本内容
echo '<p>' . esc_html( $user_input ) . '</p>';

// HTML 属性值(引号安全)
echo '<div class="' . esc_attr( $meta['level'] ) . '">';

// URL(协议白名单,防 javascript: 注入)
echo '<a href="' . esc_url( $meta['link'] ) . '">查看</a>';

// 内联 JS 数据(配 wp_json_encode)
echo '<script>var CFG = ' . wp_json_encode( $cfg ) . ';</script>';

// 富文本:白名单保留标签
echo wp_kses_post( $user_html );   // 允许 a/p/strong 等,剔掉 script

原则是 late escaping:在输出点转义,而不是信任「入库时已净化」——数据来源可能变化,输出点才是最后一道闸。

步骤 2:入站净化家族(sanitize)

$course = array(
    'title'    => sanitize_text_field( wp_unslash( $_POST['title'] ?? '' ) ),
    'email'    => sanitize_email( wp_unslash( $_POST['email'] ?? '' ) ),
    'level'    => sanitize_key( wp_unslash( $_POST['level'] ?? '' ) ),   // 小写字母数字下划线
    'duration' => absint( wp_unslash( $_POST['duration'] ?? 0 ) ),
);
update_post_meta( $post_id, 'course_meta', $course );

wp_unslash 剥掉 WP 兜底的转义层(magic quotes 遗产),再交给对应 sanitize 函数。

步骤 3:nonce + 能力检查完整链(自定义字段保存)

add_action( 'add_meta_boxes', function () {
    add_meta_box( 'qrocmq_course_box', '课程设置', function ( WP_Post $post ) {
        wp_nonce_field( 'qrocmq_save_course', 'qrocmq_nonce' );   // ① 表单埋 nonce
        $level = get_post_meta( $post->ID, 'level', true );
        echo '<select name="qrocmq_level">';
        foreach ( array( 'beginner' => '入门', 'advanced' => '进阶' ) as $k => $v ) {
            printf( '<option value="%s" %s>%s</option>',
                esc_attr( $k ), selected( $level, $k, false ), esc_html( $v ) );
        }
        echo '</select>';
    }, 'course' );
} );

add_action( 'save_post', function ( int $post_id, WP_Post $post ): void {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
    if ( wp_is_post_revision( $post_id ) ) return;

    // ② 验 nonce:请求是不是从我们的表单发出的
    if ( ! isset( $_POST['qrocmq_nonce'] ) ||
         ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['qrocmq_nonce'] ) ), 'qrocmq_save_course' ) ) {
        return;
    }
    // ③ 验身份:当前用户有没有权改这篇文章
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }
    update_post_meta( $post_id, 'level', sanitize_key( wp_unslash( $_POST['qrocmq_level'] ?? '' ) ) );
}, 10, 2 );

AJAX 版本:处理函数开头 check_ajax_referer( 'qrocmq_action', 'nonce' ),再接 current_user_can。

步骤 4:SQL 注入防线

global $wpdb;
// 正确:占位符
$rows = $wpdb->get_results(
    $wpdb->prepare(
        "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s",
        $type,          // 用户可控的值 → 走占位符
        'publish'
    )
);
// 动态排序字段不能参数化 → 白名单
$allowed = array( 'date', 'title' );
$orderby = in_array( $_GET['orderby'] ?? '', $allowed, true ) ? $_GET['orderby'] : 'date';

步骤 5:环境加固清单

wp-config.php:

define( 'DISALLOW_FILE_EDIT', true );  // 关闭后台在线改代码
define( 'FORCE_SSL_ADMIN', true );     // 后台强制 HTTPS

运维动作(部分给命令,详见下一课 WP-CLI):

  • 文件权限:目录 755、文件 644,wp-config.php 600
  • 登录保护:限流插件(如 Limit Login Attempts)防爆破
  • 更新纪律:核心与插件及时更新(多数漏洞有补丁),不装盗版主题
  • 隐藏版本号:remove_action( 'wp_head', 'wp_generator' )
  • 定期改盐值可在事故后让全部登录态失效

讲清楚要点

  1. sanitize 与 escape 方向相反:净化管入口(格式合法化),转义管出口(上下文安全化)。只做其一都留缺口。
  2. nonce 不是权限系统:它证明请求来自你的表单(防 CSRF),不证明用户是谁、能不能做——身份判断永远交给 current_user_can。
  3. 最小权限原则:给编辑的账号别给管理员;代码里的能力检查用最窄的能力(edit_post 而非 manage_options)。
  4. WP 的攻击面排序:过时插件 > 弱口令 > 主题漏洞 > 核心漏洞。更新纪律比任何防火墙插件都有效。
  5. unserialize 反序列化是高危:不 unserialize 任何用户可控输入(对象注入),配置传输用 JSON。

你可能会懵的地方

  • nonce 为什么会失效? 有效期 24 小时(两拍),且与用户、动作绑定。页面放久了提交失败属正常——失败后让用户刷新重试,别把 nonce 当持久令牌。

  • esc_html 和 wp_kses_post 怎么选? 输出应为纯文本(标题、名字)→ esc_html(全部 HTML 变实体);输出确需富文本(用户写的简介)→ wp_kses_post 白名单过滤。

  • nonce 校验通过了还查 current_user_can,是不是重复劳动? 不是。nonce 防「陌生人替你提交」,能力检查防「登录用户做超出角色的事」——两个不同维度的威胁。

  • 密码忘了进不去后台怎么办? wp user update admin --user_pass=新密码(WP-CLI),或数据库层面重置 user_pass 字段(MD5 函数会让 WP 自动升级哈希)。

  • 被黑后的恢复顺序? 隔离(关站/断写)→ 换全部密钥与密码 → 与干净备份/官方核心比对找后门 → 重装核心与可信插件 → 排查数据库注入残留 → 复盘更新策略。

错法对照

错法问题正解
$_GET 直接拼进 SQLSQL 注入$wpdb->prepare 占位符
只在入库时 strip_tags,输出不管数据源变化即 XSSlate escaping 输出点转义
校验了 nonce 却不查能力低权限用户伪造请求成功nonce + current_user_can 成对出现
把密钥写进代码库密钥随仓库泄露wp-config/环境变量,仓库排除
unserialize( $_POST['data'] )对象注入 RCE用 JSON + 白名单字段校验

卡点急救

  1. 后台无限跳回登录页:浏览器 cookie 被拒或站点 URL 与实际域不符;检查 wp-config 的 WP_HOME/WP_SITEURL 与 HTTPS 混用问题。
  2. 提交表单总提示 nonce 失效:缓存层把 A 用户的页面连 nonce 缓存给了 B——带 nonce 的表单页面必须排除在页面缓存外。
  3. 某插件被曝漏洞但不能立即升级:先停用该插件功能入口,用应用密码收紧 API,监控日志;尽快在测试环境验证升级。

面试常问

  • WP 中防御 XSS 的完整方案? 输出点按上下文转义:esc_html/esc_attr/esc_url/wp_kses_post,late escaping 原则;配合入口净化 sanitize_* 与 CSP 兜底。

  • nonce 的工作原理与局限? 基于动作、用户、时间(24h)生成的哈希令牌,随表单提交并在服务端 wp_verify_nonce 验证,防 CSRF;它不验证用户身份与权限,也不适用于公开缓存页面。

  • 如何评估一个第三方插件是否安全? 看更新时间与活跃安装量、官方仓库而非第三方下载、查已知漏洞库(Patchstack/WPVulnDB)、代码层面检查是否 prepare/转义/能力检查、上线前在测试环境审查。

自测三问

  1. 写出「进净化、出转义、写校验」分别对应的函数族与典型 API。
  2. nonce 与 current_user_can 各防什么威胁?为什么缺一不可?
  3. 动态 ORDER BY 字段为什么不能进 prepare?正确做法是什么?

小结一句话

安全 = 方向正确的三道闸:入口 sanitize、出口 esc_*(late escaping)、写操作 nonce+能力检查,再加更新纪律与最小权限——自动化攻击扫不动纪律严明的站点。

延伸

  • WP 安全 API 手册(数据验证)
  • 官方安全文档
  • 下一课:性能与运维——三层缓存、CDN 与 WP-CLI 日常运维。
加载本节练习…
← 上一课AJAX 与 Headless下一课 →性能与运维