关于 wp-security-review
面向 WordPress 主题、插件及自定义代码的安全审查技能,按输入、授权和输出三项原则检查漏洞。适用于代码审计,并按严重级别提供行号、CWE 及修复示例。
主要功能
- 01检查输入清理、授权与输出转义
- 02审查 SQL 注入、XSS 与 CSRF
- 03检查 REST API、AJAX 和上传处理
- 04按文件与严重级别整理发现
- 05提供行号、CWE 和修复代码
适用场景
- 01发布前审查 WordPress 插件
- 02调查主题或插件中的漏洞报告
- 03检查表单、AJAX 与 REST API 处理器
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jorgerosal/ wordpress-skills --skill wp-security-review安装后在 Claude Code 中输入/wp-security-review确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/auth-patterns.mdescaping-guide.mdnonce-csrf-guide.mdsanitization-guide.mdvulnerability-patterns.md详细介绍
WordPress 安全审查技能
概述
对 WordPress 主题、插件和自定义代码进行系统性的安全代码审查。**核心原则:**三大支柱安全模型:(1)尽早清理输入;(2)验证授权(nonce + 能力);(3)延迟转义输出。首先扫描严重问题(公开代码中的 SQL 注入、未转义输出导致的 XSS、状态变更操作缺少 nonce),然后检查警告,最后查看信息级改进。报告需包含行号、严重性、CWE 参考,以及 BAD/GOOD 代码对照。
适用场景
适用于:
- 审计 WordPress 主题或插件的安全性
- 上线前安全审查
- 调查已报告的漏洞
- 审查表单处理器、AJAX 端点或 REST API 路由
- 分析用户输入处理或输出渲染
- 审查存在可利用模式的代码(XSS、SQL 注入、CSRF、认证绕过)
不适用于:
- 服务器加固或基础设施安全(Apache/nginx 配置、防火墙规则)
- Web 应用防火墙(WAF)配置
- SSL/TLS 证书管理
- 仅针对性能的审计(使用 wp-performance-review)
- 通用的非 WordPress PHP 安全审查
代码审查工作流
- 识别文件类型,并应用下方相关检查
- 优先扫描严重模式(公开代码中的 SQL 注入、未转义输出导致的 XSS、状态变更操作缺少 nonce、文件上传未验证)
- 检查警告模式(授权问题、仅限管理端的 XSS、验证不完整)
- 记录信息级改进(纵深防御、安全常量、加固机会)
- 根据上下文调整严重性(仅限管理端的代码 = 较低严重性;面向公众的代码 = 最高严重性;WP-CLI/cron = 无需 nonce;REST API = 不同的授权模型)
- 按照下方输出格式提供包含行号的报告
按文件类型检查
插件/主题 PHP 文件(functions.php、plugin.php、*.php)
扫描以下内容:
- 顶部缺少
defined( 'ABSPATH' ) || exit;→ WARNING:可能直接访问文件 eval(→ CRITICAL:代码注入途径(CWE-95)exec(、shell_exec(、system(、passthru(→ CRITICAL:存在用户输入时可能导致命令注入(CWE-78)base64_decode( $_→ CRITICAL:可能执行编码后的载荷unserialize( $_→ CRITICAL:对象注入(CWE-502)$_GET[、$_POST[、$_REQUEST[未使用sanitize_*→ WARNING:未清理的输入(CWE-20)include $_、require $_、include_once $_、require_once $_→ CRITICAL:路径遍历/文件包含(CWE-22)- 直接使用
move_uploaded_file(→ CRITICAL:应改用wp_handle_upload()
表单处理器(admin-post.php、admin_post_* 钩子)
检查以下三步模式(必须全部具备):
- Nonce 验证 →
wp_verify_nonce( $_POST['nonce_field'], 'action_name' )或check_admin_referer()→ 缺少时为 CRITICAL(CWE-352) - 能力检查 →
current_user_can( 'capability' )→ 缺少时为 CRITICAL(CWE-862) - 输入清理 →
sanitize_text_field()、sanitize_email()等 → 缺少时为 WARNING(CWE-20)
缺少任意一步 = 存在漏洞。状态变更操作必须具备全部三步。
AJAX 处理器(wp_ajax_*、wp_ajax_nopriv_* 钩子)
扫描以下内容:
- 状态变更操作缺少
check_ajax_referer( 'action_name', 'nonce_field' )→ CRITICAL(CWE-352) - 需要能力检查时缺少
current_user_can( 'capability' )→ CRITICAL(CWE-862) wp_ajax_nopriv_*未设置 nonce → CRITICAL:公开端点没有 CSRF 防护$_POST输入未使用sanitize_*→ WARNING:未清理的输入(CWE-20)- 输出用户输入时,
wp_send_json()未进行转义 → WARNING:JSON 注入
REST API 端点(register_rest_route)
扫描以下内容:
- 缺少
permission_callback→ 严重:WordPress 5.5+ 要求提供此项(CWE-862) - 写操作中使用
'permission_callback' => '__return_true'→ 严重:允许未授权访问 - 公开读取端点使用
'permission_callback' => '__return_true'→ 正常(不是漏洞) - 权限回调中缺少
current_user_can()→ 警告:可能导致权限提升(CWE-863) - 未对
$request->get_param()的输入进行验证 → 警告:输入未经清理(CWE-20) - 通过
X-WP-Nonce标头或应用程序密码进行 REST 身份验证 → 信息:确认客户端发送了标头
数据库代码($wpdb->query、$wpdb->get_results 等)
扫描以下内容:
$wpdb->query(或$wpdb->get_results(使用字符串拼接或插值 → 严重:SQL 注入(CWE-89)- 存在用户输入时缺少
$wpdb->prepare()→ 严重:SQL 注入(CWE-89) $wpdb->prepare( "... LIKE '%{$term}%'" )→ 警告:应先使用$wpdb->esc_like()- 重复预处理(对已预处理的字符串再次预处理)→ 警告:会转义转义字符
- 在查询中直接使用
{$wpdb->prefix}→ 正常(不是漏洞,是标准模式)
安全模式:$wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE ID = %d", $post_id ) )
模板/输出文件(主题、模板中的 *.php)
扫描以下内容:
echo $或print $未使用转义函数 → 严重:XSS(CWE-79)<input value="<?php echo $→ 严重:使用esc_attr()(CWE-79)<a href="<?php echo $→ 严重:使用esc_url()(CWE-79)<script>块中包含 PHP 变量 → 严重:使用esc_js()或wp_localize_script()(CWE-79)- 富 HTML 输出未使用
wp_kses()或wp_kses_post()→ 警告:可能允许不安全的标签
安全模式:始终在输出时转义,而不是在输入或存储时转义。
文件上传处理程序
扫描以下内容:
- 使用
move_uploaded_file(而非wp_handle_upload()→ 严重:绕过 WP 安全检查(CWE-434) - 缺少
current_user_can()检查 → 严重:未授权文件上传(CWE-862) - 未验证 MIME 类型 → 警告:可能允许上传可执行文件
- 未限制文件大小 → 信息:可能导致 DoS
- 缺少
wp_check_filetype_and_ext()→ 警告:可能伪造文件类型(CWE-434)
JavaScript 文件(*.js、*.jsx)
扫描以下内容:
- 调用 REST API 的
fetch()或$.ajax()未携带X-WP-Nonce标头 → 警告:CSRF 漏洞(CWE-352) $.post( ajaxurl, ...)的数据中未包含 nonce → 警告:CSRF 漏洞(CWE-352)innerHTML = userInput或dangerouslySetInnerHTML→ 严重:基于 DOM 的 XSS(CWE-79)eval(→ 严重:代码注入向量(CWE-95)
快速检测的搜索模式(SEC-20)
# CRITICAL: SQL injection patterns
grep -rn "\$wpdb->query(" . | grep -v "prepare"
grep -rn "\$wpdb->get_results(" . | grep -v "prepare"
grep -rn "\$wpdb->get_var(" . | grep -v "prepare"
grep -rn "\$wpdb->get_row(" . | grep -v "prepare"
# CRITICAL: XSS (unescaped output)
grep -rn "echo \$_" .
grep -rn "print \$_" .
grep -rn "<?php echo \$" . | grep -v "esc_"
# CRITICAL: Dangerous functions
grep -rn "eval(" .
grep -rn "exec(" .
grep -rn "shell_exec(" .
grep -rn "system(" .
grep -rn "passthru(" .
grep -rn "base64_decode(\$_" .
grep -rn "unserialize(\$_" .
# CRITICAL: File upload without WP functions
grep -rn "move_uploaded_file(" .
# CRITICAL: REST API without permission_callback
grep -rn "register_rest_route" . | grep -v "permission_callback"
# CRITICAL: Path traversal / dynamic includes
grep -rn "include \$_" .
grep -rn "require \$_" .
grep -rn "include_once \$_" .
grep -rn "require_once \$_" .
# WARNING: Unsanitized input usage
grep -rn "\$_GET\[" . | grep -v "sanitize_"
grep -rn "\$_POST\[" . | grep -v "sanitize_"
grep -rn "\$_REQUEST\[" . | grep -v "sanitize_"
# WARNING: Missing nonces on public AJAX
grep -rn "wp_ajax_nopriv_" .
# WARNING: Missing capability checks
grep -rn "admin_post_" . | grep -v "current_user_can"
grep -rn "wp_ajax_" . | grep -v "current_user_can"
# INFO: Missing ABSPATH check
grep -rn "<?php" . | head -20 | grep -v "ABSPATH"
# INFO: Security constants missing (check wp-config.php)
grep -n "DISALLOW_FILE_EDIT" wp-config.php
grep -n "FORCE_SSL_ADMIN" wp-config.php
grep -n "DISALLOW_UNFILTERED_HTML" wp-config.php
平台背景
不同的托管环境提供不同层级的安全防护:
托管式 WordPress 主机(WP Engine、Pantheon、Pressable、WordPress VIP 等):
- 通常提供平台级 WAF 和恶意软件扫描
- 可能会在平台级阻止某些危险函数(eval、exec)
- 请查看主机文档,了解其他安全要求
- 为确保可移植性,代码仍应遵循安全编写规范
WordPress VIP:
- 代码审查要求严格
- 禁止使用许多危险函数
- 提供其他安全辅助函数(
wpcom_vip_*函数) - 关于必需模式,请参阅 VIP 文档
自托管/标准托管:
- 除代码本身实现的安全措施外,不提供平台级安全防护
- 需要更加重视安全编码实践
- 确保实现所有三项原则(清理、授权、转义)
快速参考:关键安全模式
XSS 防护(输出转义)
// ❌ CRITICAL: Unescaped output (CWE-79)
<h1><?php echo $_GET['title']; ?></h1>
<input value="<?php echo $user_input; ?>">
<a href="<?php echo $url; ?>">Link</a>
<script>var data = "<?php echo $json; ?>";</script>
// ✅ GOOD: Context-appropriate escaping
<h1><?php echo esc_html( $_GET['title'] ); ?></h1>
<input value="<?php echo esc_attr( $user_input ); ?>">
<a href="<?php echo esc_url( $url ); ?>">Link</a>
<script>var data = <?php echo wp_json_encode( $data ); ?>;</script>
// ❌ CRITICAL: Early escaping (wrong pattern)
$title = esc_html( $_POST['title'] );
update_post_meta( $post_id, 'title', $title ); // Stores HTML entities
// ✅ GOOD: Late escaping (correct pattern)
$title = sanitize_text_field( $_POST['title'] );
update_post_meta( $post_id, 'title', $title );
echo '<h1>' . esc_html( get_post_meta( $post_id, 'title', true ) ) . '</h1>';
// ✅ GOOD: Rich HTML with wp_kses_post() for trusted content
echo wp_kses_post( $content ); // Allows safe HTML tags only
// ✅ GOOD: Custom allowed tags with wp_kses()
$allowed_tags = array(
'a' => array( 'href' => array(), 'title' => array() ),
'br' => array(),
'strong' => array(),
);
echo wp_kses( $user_content, $allowed_tags );
SQL 注入防护
// ❌ CRITICAL: Direct interpolation (CWE-89)
$results = $wpdb->get_results( "SELECT * FROM {$wpdb->users} WHERE ID = $user_id" );
$results = $wpdb->query( "UPDATE {$wpdb->posts} SET post_title = '$title'" );
// ✅ GOOD: Use $wpdb->prepare() with placeholders
$results = $wpdb->get_results( $wpdb->prepare(
"SELECT * FROM {$wpdb->users} WHERE ID = %d",
$user_id
) );
$wpdb->query( $wpdb->prepare(
"UPDATE {$wpdb->posts} SET post_title = %s WHERE ID = %d",
$title,
$post_id
) );
// ✅ GOOD: Multiple placeholders
$wpdb->get_results( $wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_author = %d",
$status,
$author_id
) );
// ❌ WARNING: LIKE without esc_like()
$wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE post_title LIKE '%%{$term}%%'" );
// ✅ GOOD: Use esc_like() for LIKE queries
$like_term = '%' . $wpdb->esc_like( $term ) . '%';
$wpdb->get_results( $wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_title LIKE %s",
$like_term
) );
// ✅ OK: Hardcoded SQL without user input (admin context)
if ( current_user_can( 'manage_options' ) ) {
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name = 'temp_setting'" );
}
CSRF/Nonce 验证(三步模式)
// ❌ CRITICAL: Missing nonce verification (CWE-352)
add_action( 'admin_post_save_settings', function() {
update_option( 'my_setting', $_POST['value'] );
} );
// ❌ CRITICAL: Missing capability check (CWE-862)
add_action( 'admin_post_save_settings', function() {
check_admin_referer( 'save_settings_action', 'settings_nonce' );
update_option( 'my_setting', $_POST['value'] );
} );
// ✅ GOOD: Complete three-step pattern
add_action( 'admin_post_save_settings', function() {
// Step 1: Verify nonce
if ( ! isset( $_POST['settings_nonce'] ) ||
! wp_verify_nonce( $_POST['settings_nonce'], 'save_settings_action' ) ) {
wp_die( 'Invalid nonce' );
}
// Step 2: Check capability
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'Insufficient permissions' );
}
// Step 3: Sanitize input
$value = sanitize_text_field( $_POST['value'] );
update_option( 'my_setting', $value );
wp_redirect( admin_url( 'admin.php?page=settings&updated=true' ) );
exit;
} );
// ✅ GOOD: Form with nonce field
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<?php wp_nonce_field( 'save_settings_action', 'settings_nonce' ); ?>
<input type="hidden" name="action" value="save_settings">
<input type="text" name="value" value="<?php echo esc_attr( $current_value ); ?>">
<?php submit_button(); ?>
</form>
// ✅ GOOD: AJAX with nonce
add_action( 'wp_ajax_update_user_meta', function() {
check_ajax_referer( 'update_meta_nonce', 'nonce' );
if ( ! current_user_can( 'edit_users' ) ) {
wp_send_json_error( 'Insufficient permissions' );
}
$user_id = absint( $_POST['user_id'] );
$value = sanitize_text_field( $_POST['value'] );
update_user_meta( $user_id, 'custom_field', $value );
wp_send_json_success();
} );
// JavaScript for AJAX nonce
jQuery.post( ajaxurl, {
action: 'update_user_meta',
nonce: myAjax.nonce, // From wp_localize_script()
user_id: 123,
value: 'new value'
} );
授权/权限检查
// ❌ CRITICAL: No capability check (CWE-862)
add_action( 'admin_post_delete_user', function() {
wp_delete_user( $_POST['user_id'] );
} );
// ❌ WARNING: Incorrect capability (CWE-863)
add_action( 'admin_post_delete_user', function() {
if ( ! current_user_can( 'edit_posts' ) ) { // Wrong capability!
wp_die( 'Insufficient permissions' );
}
wp_delete_user( $_POST['user_id'] );
} );
// ✅ GOOD: Correct capability check
add_action( 'admin_post_delete_user', function() {
if ( ! current_user_can( 'delete_users' ) ) {
wp_die( 'Insufficient permissions' );
}
check_admin_referer( 'delete_user_action', 'delete_nonce' );
wp_delete_user( absint( $_POST['user_id'] ) );
} );
// ✅ GOOD: Check user can edit specific post
if ( ! current_user_can( 'edit_post', $post_id ) ) {
wp_die( 'You cannot edit this post' );
}
// ✅ GOOD: REST API permission callback
register_rest_route( 'myapp/v1', '/users/(?P<id>\d+)', array(
'methods' => 'DELETE',
'callback' => 'myapp_delete_user',
'permission_callback' => function( $request ) {
return current_user_can( 'delete_users' );
},
) );
// ✅ OK: __return_true for public read endpoint
register_rest_route( 'myapp/v1', '/posts', array(
'methods' => 'GET',
'callback' => 'myapp_get_posts',
'permission_callback' => '__return_true', // Public read is OK
) );
文件上传安全
// ❌ CRITICAL: Direct move_uploaded_file() (CWE-434)
if ( isset( $_FILES['upload'] ) ) {
move_uploaded_file( $_FILES['upload']['tmp_name'], '/uploads/' . $_FILES['upload']['name'] );
}
// ❌ CRITICAL: No capability check (CWE-862)
$file = wp_handle_upload( $_FILES['upload'], array( 'test_form' => false ) );
// ✅ GOOD: Complete file upload security
add_action( 'admin_post_upload_file', function() {
// Step 1: Verify nonce
check_admin_referer( 'upload_file_action', 'upload_nonce' );
// Step 2: Check capability
if ( ! current_user_can( 'upload_files' ) ) {
wp_die( 'Insufficient permissions' );
}
// Step 3: Validate file exists
if ( ! isset( $_FILES['upload'] ) || UPLOAD_ERR_OK !== $_FILES['upload']['error'] ) {
wp_die( 'File upload failed' );
}
// Step 4: Use wp_handle_upload() (validates MIME type, extension)
require_once( ABSPATH . 'wp-admin/includes/file.php' );
$file = wp_handle_upload( $_FILES['upload'], array( 'test_form' => false ) );
if ( isset( $file['error'] ) ) {
wp_die( 'Upload error: ' . esc_html( $file['error'] ) );
}
// Step 5: Store file info
$attachment_id = wp_insert_attachment( array(
'post_title' => sanitize_file_name( $_FILES['upload']['name'] ),
'post_mime_type' => $file['type'],
'post_status' => 'inherit',
), $file['file'] );
} );
// ✅ GOOD: Restrict allowed MIME types
add_filter( 'upload_mimes', function( $mimes ) {
// Remove potentially dangerous types
unset( $mimes['exe'] );
unset( $mimes['php'] );
// Add custom allowed types if needed
$mimes['svg'] = 'image/svg+xml';
return $mimes;
} );
// ✅ GOOD: File type validation
$filetype = wp_check_filetype_and_ext( $_FILES['upload']['tmp_name'], $_FILES['upload']['name'] );
if ( ! $filetype['ext'] ) {
wp_die( 'Invalid file type' );
}
对象注入防护
// ❌ CRITICAL: Unserialize user input (CWE-502)
$data = unserialize( $_POST['data'] );
// ❌ CRITICAL: Unserialize from cookie
$data = unserialize( $_COOKIE['cart_data'] );
// ✅ GOOD: Use JSON instead of serialize for user-facing data
$data = json_decode( $_POST['data'], true );
if ( JSON_ERROR_NONE !== json_last_error() ) {
wp_die( 'Invalid JSON' );
}
// ✅ OK: Unserialize trusted internal data only
$data = get_option( 'my_internal_setting' ); // Already unserialized by WP
$meta = get_post_meta( $post_id, 'my_field', true ); // Already unserialized by WP
危险函数
// ❌ CRITICAL: eval() with user input (CWE-95)
eval( $_POST['code'] );
// ❌ CRITICAL: exec() with user input (CWE-78)
exec( 'ls -la ' . $_GET['dir'] );
// ❌ CRITICAL: shell_exec() with user input
$output = shell_exec( 'grep ' . $_POST['search'] . ' file.txt' );
// ✅ GOOD: Avoid eval(), exec(), shell_exec() entirely in web context
// If absolutely necessary, whitelist input strictly
$allowed_dirs = array( 'uploads', 'cache' );
$dir = sanitize_key( $_GET['dir'] );
if ( in_array( $dir, $allowed_dirs, true ) ) {
exec( 'ls -la ' . escapeshellarg( $dir ), $output );
}
防止直接访问文件
// ❌ WARNING: Missing ABSPATH check
<?php
// Plugin code here without security check
// ✅ GOOD: ABSPATH check at top of every PHP file
<?php
defined( 'ABSPATH' ) || exit;
// Plugin code here
// ✅ GOOD: Alternative pattern
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access not permitted.' );
}
输入清理
// ❌ WARNING: No sanitization (CWE-20)
$title = $_POST['title'];
update_post_meta( $post_id, 'title', $title );
// ✅ GOOD: Sanitize based on expected data type
$title = sanitize_text_field( $_POST['title'] );
$email = sanitize_email( $_POST['email'] );
$url = sanitize_url( $_POST['url'] );
$filename = sanitize_file_name( $_POST['filename'] );
$key = sanitize_key( $_POST['key'] );
$int = absint( $_POST['count'] );
$float = floatval( $_POST['price'] );
$textarea = sanitize_textarea_field( $_POST['description'] );
// ✅ GOOD: Array of integers
$ids = array_map( 'absint', (array) $_POST['ids'] );
// ✅ GOOD: Validate against whitelist
$allowed_types = array( 'post', 'page', 'product' );
$type = sanitize_key( $_POST['type'] );
if ( ! in_array( $type, $allowed_types, true ) ) {
wp_die( 'Invalid type' );
}
严重性定义(SEC-22)
| 严重性 | 描述 |
|---|---|
| CRITICAL | 无需身份验证即可利用,或导致数据泄露、RCE 或权限提升。例如:公开端点上的 SQL 注入、未转义输出中的 XSS、管理操作缺少 nonce、文件上传未进行 capability 检查、使用用户输入调用 eval() |
| WARNING | 需要身份验证才能利用,或需要满足特定条件。例如:仅限管理员代码中的 XSS、已登录操作缺少 capability 检查、管理上下文中的 SQL 注入、输入清理不完整 |
| INFO | 纵深防御改进或加固机会。例如:缺少 ABSPATH 检查、wp-config.php 中未设置安全常量、缺少安全标头 |
上下文调整:
- 仅限管理员的代码:严重性降低一个级别(CRITICAL → WARNING)
- WP-CLI/cron 上下文:不需要 nonce(无 CSRF 风险)
- REST API:使用不同的身份验证模型(permission_callback,而非 nonce)
- 面向公众的代码:最高严重性(没有身份验证屏障)
输出格式(SEC-23)
按文件和严重性级别对发现的问题分组:
## Security Review: [filename]
### CRITICAL Issues
**Line X** | CWE-79 | XSS via unescaped output
- **Issue**: User input echoed directly without escaping
- **Risk**: Allows attackers to inject malicious JavaScript
- **Context**: Public-facing template (HIGH severity)
❌ **BAD:**
```php
<h1><?php echo $_GET['title']; ?></h1>
✅ 良好:
<h1><?php echo esc_html( $_GET['title'] ); ?></h1>
WARNING 问题
第 Y 行 | CWE-352 | 缺少 nonce 验证
- 问题:表单处理程序缺少 wp_verify_nonce()
- 风险:状态可能通过 CSRF 攻击被更改
- 上下文:仅限管理员(已降低严重性)
[不良/良好代码对照]
INFO 建议
第 Z 行 | 纵深防御 | 缺少 ABSPATH 检查
- 在文件顶部添加
defined( 'ABSPATH' ) || exit;
总结
- 总计:1 个 CRITICAL、1 个 WARNING、1 个 INFO
- 风险级别:HIGH(存在可利用的 CRITICAL 问题)
- 立即采取行动:修复第 X 行(未转义的输出)
## Common Mistakes (SEC-24)
When performing security reviews, avoid these false positives:
| Mistake | Why It's Wrong | Context |
|---------|----------------|---------|
| Flagging missing nonces in WP-CLI commands | WP-CLI runs server-side, no CSRF risk | Nonces only needed for HTTP requests |
| Flagging `wp_kses_post()` as insufficient escaping | `wp_kses_post()` is valid for trusted content with safe HTML | Allows `<p>`, `<a>`, `<strong>`, etc. - appropriate for post content |
| Flagging admin-only `$wpdb->query()` with hardcoded SQL | No user input = no injection risk | Review for user input presence, not just prepare() usage |
| Flagging `current_user_can()` inside REST `permission_callback` as redundant | This is the CORRECT location for capability checks in REST API | REST API auth model, not redundant |
| Flagging `esc_html( get_the_title() )` as double-escaping | WP core sanitizes on storage, but late escaping is still best practice | Not technically double-escaping, WP stores raw data |
| Flagging `update_option()` in admin context as vulnerability | Options API is safe for admin use | Check capability first, then update_option() is fine |
| Flagging REST API with `__return_true` permission on GET endpoints | Public read access is often intentional | Only flag on write operations (POST, PUT, DELETE) |
| Flagging `sanitize_callback` in `register_setting()` as missing sanitization | Settings API handles sanitization via callback | This is the correct pattern for Settings API |
## Security Constants Check (SEC-19)
Review `wp-config.php` for security hardening constants:
```php
// ✅ GOOD: Disable file editor (INFO-level recommendation)
define( 'DISALLOW_FILE_EDIT', true );
// ✅ GOOD: Force SSL for admin (INFO if SSL available)
define( 'FORCE_SSL_ADMIN', true );
// ✅ GOOD: Disable unfiltered HTML for all users including admins
define( 'DISALLOW_UNFILTERED_HTML', true );
// ✅ GOOD: Disable plugin/theme installation (high-security environments)
define( 'DISALLOW_FILE_MODS', true );
// ✅ GOOD: Custom authentication keys (CRITICAL if using defaults)
define( 'AUTH_KEY', 'put-unique-phrase-here' ); // Use https://api.wordpress.org/secret-key/1.1/salt/
define( 'SECURE_AUTH_KEY', 'put-unique-phrase-here' );
define( 'LOGGED_IN_KEY', 'put-unique-phrase-here' );
define( 'NONCE_KEY', 'put-unique-phrase-here' );
define( 'AUTH_SALT', 'put-unique-phrase-here' );
define( 'SECURE_AUTH_SALT', 'put-unique-phrase-here' );
define( 'LOGGED_IN_SALT', 'put-unique-phrase-here' );
define( 'NONCE_SALT', 'put-unique-phrase-here' );
深入参考资料
加载以下参考资料,了解全面的漏洞模式和示例:
| 任务 | 要加载的参考资料 |
|---|---|
| 包含 CWE 映射的全面漏洞目录 | references/vulnerability-patterns.md |
| 输出转义模式和特定上下文的函数 | references/escaping-guide.md |
| 按数据类型划分的输入清理模式 | references/sanitization-guide.md |
| 授权和 capability 检查模式 | references/auth-patterns.md |
| nonce 生成、验证和 CSRF 防护 | references/nonce-csrf-guide.md |
注意:对于标准安全审查,本 SKILL.md 已包含所需的全部模式。如需全面的 CWE 目录、边缘情况或特定平台的安全要求(WordPress VIP 等),请加载参考资料。