数据验证

10条评论

危险数据的来源多种多样(用户、第三方站点、自己的数据库!,等等),因此无论是在输入或是输入数据时,都需要经过验证。

过滤输出信息

数据过滤的方式取决于数据类型以及数据所在背景。下面介绍一些常见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文档的输出结果。将&符号和单引号(')编码成数值实体引用(&#038, &#039)。

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())