危险数据的来源多种多样(用户、第三方站点、自己的数据库!,等等),因此无论是在输入或是输入数据时,都需要经过验证。
过滤输出信息
数据过滤的方式取决于数据类型以及数据所在背景。下面介绍一些常见WordPress数据及其过滤方法。
整数
intval ($int)或(int) $int
若应为整数,就将其作为整数
absint ($int)
保证结果为非负数
HTML/XML
很多类型的XML文档(与HTML文档相反)都只能识别少数几个具名的字符引用:apos, amp, gt, lt, quot。将数据输出到这类XML文档中时,一定要用WordPress的ent2ncr( $text )函数过滤掉含有非法具名实体的信息。
HTML/XML片段
wp_kses( (string) $fragment, (array) $allowed_html, (array) $protocols = null )
KSES 除去非法脚本。所有不可信的HTML元素(如日志内容、评论内容等)都需要经过wp_kses()的检查。wp-includes/kses.php中有对wp_kses的用法、默认值等方面的介绍。
wp_rel_nofollow((string) $html)
在所有<a>链接后添加"rel=nofollow"属性。
文字节点
esc_html( $text ) (自WP 2.8起开始使用)
将 <(小于)、 >(大于)、 &(and符号)、 "(双引号)、 '(单引号)译成密码。类似于esc_attr。
esc_html_ (自WP 2.8起开始使用)
翻译并编码
esc_html_ e(自WP 2.8起开始使用)
翻译、编码并响应
wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) (WP 2.8起停用)
将 <(小于)、 >(大于)、 &(and符号)译成密码。不会重复将实体进行编码。考虑到对旧版插件的附加保护,从WP 2.8起,如果只调用一个参数,也会对引号字符进行编码(通过esc_html)。
htmlspecialchars( $text, ENT_NOQUOTES )
将 <(小于)、 >(大于)、 &(and符号)译成密码。如果二次运行该函数,也会进行两次HTML实体编码。
属性节点
esc_attr (自WP 2.8起开始使用)
attribute_escape( $text )(WP 2.8起停用)
将 <(小于)、 >(大于)、 &(and符号)译成密码。不会重复将实体进行编码。参见下文中的clean_url()。
esc_attr_
翻译并编码
esc_attr_e
翻译,编码并响应
htmlspecialchars( $text, ENT_QUOTES )
将 <(小于)、 >(大于)、 &(and符号)译成密码。如果二次运行该函数,也会进行两次HTML实体编码。参见下文中的clean_url()。
JavaScript
- esc_js(自WP 2.8起开始使用)
- js_escape( $text ) (WP 2.8起停用)
- 除去'符号,编译''符号,修正行结尾。
URLs
- esc_url (自WP 2.8起开始使用)
- clean_url( $url, (array) $protocols = null, $context = 'display' )
过滤URL(在文字节点、属性节点等位置)时总是使用clean_url。驳回没有任何一个已知白名单协议(默认为http, https, ftp, ftps, mailto, news, irc, gopher, nntp, feed以及telnet)的URL,消除无效字符,删除危险字符。变量$context的有效值包括:
display
用于(X)HTML或XML文档的输出结果。将&符号和单引号(')编码成数值实体引用(&, ')。
url
仅除去无效的URL字符
db
在数据库中插入字符时使用该变量值
esc_url_raw (自WP 2.8起开始使用)
用于在数据库中插入URL(类似于上文中的$context="db")
urlencode($scalar)
为用在URL中的字符进行编码(例如某个查询参数)
urlencode_deep($array)
为所有数组元素进行URL编码
数据库
$wpdb->insert( $table, (array) $data )
不应除去$data参数(函数会自动出去这些参数)。关键字是表中的列,值则是相应值。
$wpdb->update( $table, (array) $data, (array) $where )
不应除去$data参数。关键字是表中的列,值则是相应值。$where也不应除去。多个WHERE条件由AND组合在一起。
$wpdb->update( 'my_table', array( 'status' => $untrusted_status, 'title' => $untrusted_title ), array( 'id' => 123 ) );
$wpdb->prepare( $format, (scalar) $value1, (scalar) $value2, … )
$format是一个类似于格式字符串的sprintf()函数。$format只能够识别 %s 与 %d,在引号中这两者都无需编码。
$wpdb->get_var( $wpdb->prepare( "SELECT something FROM table WHERE foo = %s and status = %d", $name, // an unescaped string (function will do the sanitation for you) $status // an untrusted integer (function will do the sanitation for you) ) );
esc_sql( $text ) (自WP 2.8起开始使用)
$wpdb->escape( $text )
除去一个字符串以供在SQL查询中使用。美其名曰addslashes()。
$wpdb->escape_by_ref( &$text )
不返回值
like_escape( $string )
过滤$string以供在SQL语句的LIKE表达式中使用。仍然需要(利用以上某个函数)来除去SQL。
文件系统
validate_file( (string) $filename, (array) $allowed_files = "" )
该函数用来阻止目录遍历攻击,或测试文件名是否在白名单中。若$filename表示一个有效的相对路径,返回0。文件名经过验证后,必须将$filename视为相对路径(如,必须在绝对路径前显示相对路径),该函数会验证类似于/etc/hosts 的信息。若给出的的路径中包含..、 ./或:, ,或路径不在$allowed_files白名单中,返回一个大于零的正式。对返回结果进行布尔值解释时要注意:false (0)表示文件名通过验证,而true (>0)则表示未通过验证。
HTTP信息头
信息头分裂攻击很令人困扰,这是因为它们都依赖于HTTP客户端。WordPress几乎没有必要在HTTP信息头中加入用户生成的信息,但一旦加入,WordPress会用白名单来过滤HTTP信息头。
WordPress会在HTTP定位信息头中使用用户生成的信息,并且会过滤这些信息。
wp_redirect($location, $status = 302)
重定向到其它URL的安全方式。保证结果HTTP定位信息头的合法性。
wp_safe_redirect($location, $status = 302)
比上述方法更安全的方法。只允许重定向到白名单中列出的域名。
验证输入信息
上文中“过滤输出信息”中大多数函数都可用来验证输入信息。此外,WordPress还利用以下函数来验证输入信息。
别名
sanitize_title( $title )
可用在日志别名等地
sanitize_user( $username, $strict = false )
生成新用户时使用$strict(尽管原来应该使用API)
HTML
- balanceTags( $html ) or force_balance_tags( $html )
- 试图确保HTML标签相对应,以便输出有效的XML。
tag_escape( $html_tag_name )
过滤HTML标签名(虽然函数名称中有“escape”,但实际上该函数不除去任何字符)
电子邮件
is_mail ($email_address)
返回布尔值
数组
array_map ('absint', $array)
保证所有元素都是非负整数。用任何适合数据的信息代替回调。
验证原理
下面是几种不同的验证原理,分别适用于不同场合。
白名单
只接受来自某个具有已知受信任值的有限列表的数据。
$possible_values = array( 'a', 1, 'good' ); if ( !in_array( $untrusted, $possible_values ) ) die( "Don't do that!" );
// Be careful here with fancy breaks and default actions. switch ( $untrusted ) { case 'a' : ... break; ... default : die( "You hoser!" ); }
黑名单
拒绝一切来自已知不受信任值的有限列表的数据。不推荐使用这个方法。
格式检测
检查数据格式是否正确。只接受格式正确的数据。
if ( !ctype_alnum( $data ) ) die( "Your data is teh suX0R" ); if ( preg_match( "/[^0-9.-]/", $data ) ) die( "Float on somewhere else, jerky" );
格式更正
几乎接受所有数据,但会删除或修改危险数据。
$trusted_integer = (int) $untrusted_integer; $trusted_alpha = preg_replace( '/[^a-z]/i', "", $untrusted_alpha ); $trusted_slug = sanitize_title( $untrusted_slug );
修改记录
-
WordPress 2.8停止使用以下函数:(摘自WordPress开发更新)
- clean_url() -> esc_url()
- sanitize_url() -> esc_url_raw()
- wp_specialchars() -> esc_html() (即: esc_html__() 与 esc_html_e())
- attribute_escape() -> esc_attr() (即: esc_attr__() 与 esc_attr_e())
分类:中文手册