# 插件安全 # 清理数据 不受信任的数据来自许多来源(用户、第三方网站,甚至您自己的数据库!),所有这些数据在使用之前都需要进行检查。 请记住:即使管理员也是用户,用户也会有意或无意地输入错误的数据。你的工作就是保护他们免受伤害。 *清理*输入是保护/清理/过滤输入数据的过程。验证优于清理,因为验证更具体。但当“更具体”不可能时,消毒就是下一个最好的选择。 #### 例子 假设我们有一个名为的输入字段`title`: ```php ``` 我们不能在这里使用验证,因为文本字段太笼统:它可以是任何东西。因此,我们使用以下函数清理输入数据`sanitize_text_field()` ```php $title = sanitize_text_field( $_POST['title'] ); update_post_meta( $post->ID, 'title', $title ); ``` 在后台,`sanitize_text_field()`执行以下操作: 1. 检查无效的 UTF-8 2. 将单个小于号 (<) 转换为实体 3. 删除所有标签 4. 删除换行符、制表符和多余的空白 5. 剥离八位字节 #### 消除功能 有许多功能可以帮助您清理数据。 - `sanitize_email()` - `sanitize_file_name()` - `sanitize_hex_color()` - `sanitize_hex_color_no_hash()` - `sanitize_html_class()` - `sanitize_key()` - `sanitize_meta()` - `sanitize_mime_type()` - `sanitize_option()` - `sanitize_sql_orderby()` - `sanitize_term()` - `sanitize_term_field()` - `sanitize_text_field()` - `sanitize_textarea_field()` - `sanitize_title()` - `sanitize_title_for_query()` - `sanitize_title_with_dashes()` - `sanitize_user()` - `sanitize_url()` - `wp_kses()` - `wp_kses_post()`
# New Page # 验证数据 不受信任的数据来自许多来源(用户、第三方网站,甚至您自己的数据库!),所有这些数据在使用之前都需要进行检查。 请记住:即使管理员也是用户,用户也会有意或无意地输入错误的数据。你的工作就是保护他们免受伤害。 *验证*输入是根据预定义模式(或多个模式)测试数据的过程,并得出明确的结果:有效或无效。与清理相比,验证是一种更具体的方法,但两者都有其作用。 简单的验证示例: - 检查必填字段是否未留空 - 检查输入的电话号码是否仅包含数字和标点符号 - 检查请求的字符串是否是五个有效选项之一 - 检查数量字段是否大于 0 **应尽早进行数据验证。**这意味着在执行任何操作之前验证数据。 #### 验证理念 关于如何进行验证有几种不同的理念。每个都适合不同的场景。 #### 安全名单 仅接受来自已知和可信值的有限列表的数据。 将不受信任的数据与安全列表进行比较时,确保使用严格的类型检查非常重要。否则,攻击者可能会以某种方式制作输入,使其通过安全列表,但仍然具有恶意效果。 #### 比较运算符 ```php $untrusted_input = '1 malicious string'; // will evaluate to integer 1 during loose comparisons if ( 1 === $untrusted_input ) { // == would have evaluated to true, but === evaluates to false echo '

Valid data'; } else { wp_die( 'Invalid data' ); } ``` #### in\_array() ```php $untrusted_input = '1 malicious string'; // will evaluate to integer 1 during loose comparisons $safe_values = array( 1, 5, 7 ); if ( in_array( $untrusted_input, $safe_values, true ) ) { // `true` enables strict type checking echo '

Valid data'; } else { wp_die( 'Invalid data' ); } ``` #### switch() ```php $untrusted_input = '1 malicious string'; // will evaluate to integer 1 during loose comparisons switch ( true ) { case 1 === $untrusted_input: // do your own strict comparison instead of relying on switch()'s loose comparison echo '

Valid data'; break; default: wp_die( 'Invalid data' ); } ``` #### 黑名单 拒绝来自已知不可信值的有限列表中的数据。这很少是一个好主意。 #### 格式检测 测试数据的格式是否正确。如果是的话,只接受它。 ```php if ( ! ctype_alnum( $data ) ) { wp_die( "Invalid format" ); } if ( preg_match( "/[^0-9.-]/", $data ) ) { wp_die( "Invalid format" ); } ``` #### 格式修正 接受大部分数据,但删除或更改危险部分。 ```php $trusted_integer = (int) $untrusted_integer; $trusted_alpha = preg_replace( '/[^a-z]/i', "", $untrusted_alpha ); $trusted_slug = sanitize_title( $untrusted_slug ); ``` #### 实例一 假设我们有一个输入字段旨在接受美国邮政编码: ```php ``` 在这里,我们告诉浏览器最多只允许输入十个字符……但是对于可以输入**字符没有限制。他们可以输入`11221``eval()` 这就是验证的用武之地。在处理表单时,我们编写代码来检查每个字段的数据类型是否正确,如果不正确则将其丢弃。 例如:要检查该`my-zipcode`字段,我们可能会这样做: ```php /** * Validate a US zip code. * * @param string $zip_code RAW zip code to check. * * @return bool true if valid, false otherwise. */ function wporg_is_valid_us_zip_code( string $zip_code ):bool { // Scenario 1: empty. if ( empty( $zip_code ) ) { return false; } // Scenario 2: more than 10 characters. // The `maxlength` attribute is only enforced by // the browser, so we still need to validate the // length of the input on the server to protect // against a manual submission. if ( 10 < strlen( trim( $zip_code ) ) ) { return false; } // Scenario 3: incorrect format. if ( ! preg_match( '/^d{5}(-?d{4})?$/', $zip_code ) ) { return false; } // Passed successfully. return true; } ``` 然后,在处理表单时,您的代码应该检查该`wporg_zip_code`字段并根据结果执行操作: ```php if ( isset( $_POST['wporg_zip_code'] ) && wporg_is_valid_us_zip_code( $_POST['wporg_zip_code'] ) ) { // $_POST['wporg_zip_code'] is valid; carry on } ``` 请注意,此特定示例正在检查提供的数据的格式是否正确;它不会检查提供的且格式正确的数据是否是有效的邮政编码。为此,您需要第二个函数来与有效邮政编码列表进行比较。 #### 示例二 假设您的代码将查询数据库中的帖子,并且您希望允许用户对查询结果进行排序。 ```php $allowed_keys = array( 'author', 'post_author', 'date', 'post_date' ); $orderby = sanitize_key( $_POST['orderby'] ); if ( in_array( $orderby, $allowed_keys, true ) ) { // $orderby is valid; carry on } ``` 此示例代码通过将传入的排序键(存储在`orderby` 输入参数中)与允许的排序键数组进行比较来检查其有效性。这可以防止用户传递任意和潜在的恶意数据。 在根据数组检查传入的排序键之前,该键会被传递到内置的 WordPress 函数中 `sanitize_key()`。该函数确保(除其他外)密钥为小写,这是我们想要的,因为`in_array()`执行区分大小写的搜索。 传递`true`到第三个参数 `in_array()`可以启用严格的类型检查,这告诉函数不仅比较值,还比较值类型。这允许代码确定传入的排序键是字符串而不是其他数据类型。 #### 验证功能 大多数验证都是作为自定义代码的一部分完成的,但也有一些辅助函数。这些是除“消毒”页面上列出的内容之外的内容。 - `balanceTags( $html )`或者`force_balance_tags( $html )`– 尝试确保 HTML 标记平衡,以便输出有效的 XML。 - `count()`用于检查数组中有多少项 - `in_array()`用于检查数组中是否存在某些内容 - `is_email()`将验证电子邮件地址是否有效。 - `in_array()`用于检查数组中是否存在某些内容 - `mb_strlen()`或者`strlen()`检查字符串是否具有预期的字符数 - `preg_match()``strpos()`用于检查某些字符串在其他字符串中的出现情况 - `sanitize_html_class( $class, $fallback )`– 清理 html 类名以确保它仅包含有效字符。将字符串剥离为 AZ,az,0-9,'-',如果这导致空字符串,则它将返回提供的备用值。 - `tag_escape( $html_tag_name )`– 清理 HTML 标签名称(不转义任何内容,尽管函数名称如此)。 - `term_exists()`检查标签、类别或其他分类术语是否存在。 - `username_exists()`检查用户名是否存在。 - `validate_file()`将验证输入的文件路径是否为真实路径(但不验证文件是否存在)。 检查[WordPress 代码参考](https://developer.wordpress.org/reference/)以获取更多此类功能。搜索具有如下名称的函数:`*_exists()``*_validate()``is_*()`。并非所有这些都是验证函数,但许多都是有帮助的。 # 转义数据 *转义* 输出是通过删除不需要的数据(例如格式错误的 HTML 或脚本标记)来保护输出数据的过程。此过程有助于在向最终用户呈现数据之前保护您的数据。 大多数 WordPress 函数都会正确准备输出数据,并且不需要额外的转义。 #### 转义函数 WordPress 有许多辅助函数,可用于最常见的场景。 请密切注意每个函数的作用,因为有些函数会删除 HTML,而另一些函数则允许它。您必须使用最适合您所呼应的内容和上下文的功能。你总是在回声的时候想逃跑,而不是之前。 - `esc_html()` – 在 HTML 元素包含正在显示的数据部分时使用。这将删除 HTML。 ```php

``` - `esc_js()` – 用于内联 Javascript。 ```php
``` - `esc_url()` – 用于所有 URL,包括 HTML 元素的 src 和 href 属性中的 URL。 ```php ``` - `esc_url_raw()` – 在数据库中存储 URL 或需要非编码 URL 的其他情况下使用。 - `esc_xml()` – 用于转义 XML 块。 - `esc_attr()` – 用于打印到 HTML 元素属性中的所有其他内容。 ```php