prepare函数源代码如下:
function prepare( $query = null ) { // ( $query, *$args )
if ( is_null( $query ) )
return;
$args = func_get_args();
array_shift( $args );
// If args were passed as an array (as in vsprintf), move them up
if ( isset( $args[0] ) && is_array($args[0]) )
$args = $args[0];
$query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it
$query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting
$query = preg_replace( '|(?<!%)%s|', "'%s'", $query ); // quote the strings, avoiding escaped strings like %%s
array_walk( $args, array( &$this, 'escape_by_ref' ) );
return @vsprintf( $query, $args );
}
为什么先说prepare函数呢,主要是因为在.net中可以直接通过Parameter来实现避免sql注入(当然如果在sql语句中进行拼接,还是需要进行二次的过滤,否则还是会出现sql注入的风险)而php中基本都是通过sql语句的拼接来执行sql操作的,通过提供的prepare函数可以减少拼接,这个函数也就是基本实现了.net中的Parameter。
官方函数说明的例子wpdb::prepare( "SELECT * FROM `table` WHERE `column` = %s AND `field` = %d", 'foo', 1337 ),如果要.net来实现的话应该是这样“SELECT * FROM `table` WHERE `column` = @c AND `field` = @f”。
Command对象会自动进行字符的转义,而php还是需要进行手动字符转换,并且拼接出转移后的sql语句执行,这就是prepare韩函数的真正作用。
但是wp中是怎么进行转移的呢?继续看代码
function escape_by_ref( &$string ) {
$string = $this->_real_escape( $string );
}
function _real_escape( $string ) {
if ( $this->dbh && $this->real_escape )
return mysql_real_escape_string( $string, $this->dbh );
else
return addslashes( $string );
}
使用的是mysql_real_escape_string和addslashes,具体的介绍直接看函数说明吧,这里要说的是
$this->real_escape是什么东西?继续找代码,只有这个函数出现了
function set_charset($dbh, $charset = null, $collate = null) {
if ( !isset($charset) )
$charset = $this->charset;
if ( !isset($collate) )
$collate = $this->collate;
if ( $this->has_cap( 'collation', $dbh ) && !empty( $charset ) ) {
if ( function_exists( 'mysql_set_charset' ) && $this->has_cap( 'set_charset', $dbh ) ) {
mysql_set_charset( $charset, $dbh );
$this->real_escape = true;
} else {
$query = $this->prepare( 'SET NAMES %s', $charset );
if ( ! empty( $collate ) )
$query .= $this->prepare( ' COLLATE %s', $collate );
mysql_query( $query, $dbh );
}
}
}
addslashes的问题在于黑客可以用0xbf27来代替单引号,而addslashes只是将0xbf27修改为0xbf5c27,成为一个有效的多字节字符,其中的0xbf5c仍会被看作是单引号,所以addslashes无法成功拦截。 当然addslashes用于单字节字符串是没问题的, mysql_real_escape_string 会判断字符集,所以会有个判断,在非单字节字符串的时候直接用addslashes,否则用mysql_real_escape_string,这样就防止了sql注入的危险。
prepare函数虽然对于.net没什么帮助,但是还是通过查看代码加深了我对于php防止mysql注入的理解。