[转]Php 5.2.0 中的输入过滤函数 Filter
时间:2006-11-17 来源:一地风飞
转自 Exceed php club(
http://club.phpe.net/index.php?act=ST&f=16&t=14570&s=7b22ffc0bed79b9ce72dcb6795458d26
)
如有问题,请到原帖发表!
#######################################################
第一次翻译,请多谅解,如果看不懂的地方请参照原文:
http://devzone.zend.com/node/view/id/1113
程序的安全性已经成为php开发人员首要(或者经常要)考虑的问题。不论是在单独的,开源的或者商业项目中,它的重要性都越来越高。
安全性对于php是一个很重要的部分。但是所有的讨论重点都是用户的输入数据。我们在这里并不讨论诸如XSS,SQL注入这样的话题,如果你对这些感兴趣可以看本文结尾出的相关文章。
不要相信外部数据
几乎所有的应用程序(web,桌面,命令行)都需要接收相应的数据或者动作来完成一定的操作然后再输出结果。这些输入数据可能是用户或者其他的程序(网络客户端,病毒,扫描器等)。基本原则是:过滤所有的外来数据。对于输入的过滤是保证整个程序安全的基础,和采用什么语言或者什么系统毫无关系。Php提供了很多相应的函数来对数据进行转换或者验证,但是和其他语言比起来明显的缺陷是没有一个标准的函数来过滤所有数据。新的函数Filter实现了这个功能。
什么是外部输入数据?
所有的form提交的数据
所有的$_GET, $_POST, $_REQUEST
Cookie($_COOKIES)
网络服务器数据
文件
服务器信息(比如 $_SERVER[‘SERVER_NAME’])
环境变量
数据库的查询
Filter很好的支持了上述这些输入数据。
为什么用Filter?
Filter扩展库的目的就是要达到使数据过滤尽量的简单,下面是两个简单例子:
检查2个通过_GET方式输入的整数:
1
2
3
if (isset($_GET['mode'])) {
4
if (!is_numeric($_GET['mode'])) {
5
echo "The 'mode' argument must be a valid integer.
";
6
exit();
7
}
8
$mode = (int)$_GET['mode'];
9
} else {
10
echo "The 'mode' argument is missing.
";
11
exit();
12
}
13
14
if (isset($_GET['type'])) {
15
$type = (int)$_GET['type'];
16
if (!is_numeric($_GET['type']) ||
17
(is_numeric($_GET['type']) && $type >= 3 && $type
18
echo "The 'type' argument must be an integer between 3 and 10.
";
19
exit();
20
} else {
21
}
22
$mode = $_GET['type'];
23
} else {
24
echo "The 'type' argument is missing.
";
25
exit();
26
}
27
echo "Ok.
";
28
?>
= 3 && $type " originalCode="= 3 && $type ">
view plain
|
print
|
copy to clipboard
|
?
= 3 && $type
使用Filter扩展库的filter_input函数来过滤:
1
2
3
$mode = filter_input(INPUT_GET, 'mode', FILTER_VALIDATE_INT);
4
$type = filter_input(INPUT_GET, 'type', FILTER_VALIDATE_INT, array('options'=>array('min_range'=>3, 'max_range'=>10)));
5
6
if (is_null($mode)) {
7
echo "The 'mode' argument is missing.
";
8
exit();
9
} elseif ($mode === false) {
10
echo "The 'mode' argument must be a valid integer.
";
11
exit();
12
} else {
13
echo "mode is: $mode.
";
14
}
15
16
if (is_null($type)) {
17
echo "The 'type' argument is missing.
";
18
exit();
19
} elseif ($type === false) {
20
echo "The 'type' argument must be an integer between 3 and 10.
";
21
exit();
22
} else {
23
echo "type is: $type.
";
24
}
25
?>
array('min_range'=>3, 'max_range'=>10))); if (is_null($mode)) { echo "The 'mode' argument is missing.
"; exit(); } elseif ($mode === false) { echo "The 'mode' argument must be a valid integer.
"; exit(); } else { echo "mode is: $mode.
"; } if (is_null($type)) { echo "The 'type' argument is missing.
"; exit(); } elseif ($type === false) { echo "The 'type' argument must be an integer between 3 and 10.
"; exit(); } else { echo "type is: $type.
"; } ?> " originalCode="array('min_range'=>3, 'max_range'=>10))); if (is_null($mode)) { echo "The 'mode' argument is missing.
"; exit(); } elseif ($mode === false) { echo "The 'mode' argument must be a valid integer.
"; exit(); } else { echo "mode is: $mode.
"; } if (is_null($type)) { echo "The 'type' argument is missing.
"; exit(); } elseif ($type === false) { echo "The 'type' argument must be an integer between 3 and 10.
"; exit(); } else { echo "type is: $type.
"; } ?>">
view plain
|
print
|
copy to clipboard
|
?
array('min_range'=>3, 'max_range'=>10)));
if (is_null($mode)) {
echo "The 'mode' argument is missing.
";
exit();
} elseif ($mode === false) {
echo "The 'mode' argument must be a valid integer.
";
exit();
} else {
echo "mode is: $mode.
";
}
if (is_null($type)) {
echo "The 'type' argument is missing.
";
exit();
} elseif ($type === false) {
echo "The 'type' argument must be an integer between 3 and 10.
";
exit();
} else {
echo "type is: $type.
";
}
?>
它是如何工作的?
把一个请求处理成用户可以用到的变量是由SAPI层来完成的。这里不去研究SAPI层的具体细节,就把SAPI层看做是连接PHP引擎和web层之间的东西。PHP负责处理来自SAPI的数据(ENV,SERVER,COOKIE,GET,POST)并转换成能用的全局变量或者像getenv这样的函数能调用的数据。
Filter能处理SAPI层的数据和PHP引擎数据。SAPI支持自定义的过滤选项,当每一个外部数据进行处理的时候PHP引擎都会调用Filter函数。通过配置php.ini文件可以使得SAPI默认支持filter。
假设有一个这样的简单的POST请求:
POST /myform.php?myfield=hola
下图说明了正常情况下(php5.1或之前没有Filter扩展的时候)和安装了Filter扩展后在处理上的区别:
运行条件
Filter在PHP 5.1和之前的版本可以使用扩展方式。从PHP 5.2.0开始变成了PHP的标准库。
安装
PHP 5.2.0 中已经默认支持,所以不需要再额外安装。
Unix/Linux:
$ pecl install filter
或者
$ wget
http://pecl.php.net/get/filter
$ tar xzf filter
$ cd filter-0.11.0
$ phpize
$ ./configure
$ make
$ make install
Windows:
在这里
http://pecl4win.php.net
可以下载与PHP版本相对应的filter.dll 文件。
在任何系统中都需要修改php.ini文件加入 extension=filter.so 然后重起web服务。
原则
Filter 可以有两种形式:
1. 过滤判断
* 允许或者不允许字符串中有指定的字符
* 和数据的格式无关
* 返回一个字符串
2. 检测判断
* 强大的数据分析
* 已知的数据格式
* 成功返回指定的格式
可用到的函数:
* filter_input, 得到一个输入值
* filter_input_array, 一次得到多个输入值
* filter_var, 过滤一个值
* filter_var_array, 一次过滤多个值
如果成功的话将返回转换过之后的格式,如果失败(格式不符,值超出范围等等。)将返回FALSE,如果变量没有设置将返回NULL。使用FILTER_NULL_ON_FAILURE标记后将会在失败的时候返回NULL,变量没有设置的时候返回FALSE。
请看下面的例子,可以避免使用isset或者is_numeric这样的混乱局面。
检测判断的例子
1
2
3
4
"example04.php" method="post" >
5
Enter your age: "age" size="2">
6
"submit" name="submit" value="Go">
7
8
9
Enter your age: ' originalCode=' Enter your age: '>
view plain
|
print
|
copy to clipboard
|
?
Enter your age:
处理脚本:
1
2
if (!filter_has_var(INPUT_POST, 'submit')) {
3
echo "form";
4
// include the form.
5
}
6
7
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT);
8
if (is_null($age)) {
9
echo "The 'age' field is required.
";
10
} elseif ($age === FALSE) {
11
echo "Please enter a valid age.
";
12
} else {
13
echo "Welcome.";
14
}
15
?>
"; } ?> " originalCode=""; } ?>">
view plain
|
print
|
copy to clipboard
|
?
";
}
?>
filter_has_var检测给定的变量是否存在。它不会做任何处理只会告诉程序变量是否已经设置。相当于isset($_POST[‘submit’])。filter_input会取得一个变量返回处理好的数据。在上面的例子中会返回一个整数。
如果是要返回一个处于一定范围之内的值,假设是7-77岁之间的人。可以指定一个最大值和一个最小值。
1
2
if (!filter_has_var(INPUT_POST, 'submit')) {
3
echo "form";
4
// include the form.
5
}
6
$options = array('options'=> array('min_range'=>7, 'min_range'=>77));
7
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, $options);
8
9
if (is_null($age)) {
10
echo "The 'age' field is required.
";
11
} elseif ($age === FALSE) {
12
echo "You must be enter a valid age and be between 7 and 77 years old.
";
13
} else {
14
echo "Welcome.";
15
}
16
?>
array('min_range'=>7, 'min_range'=>77)); $age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, $options); if (is_null($age)) { echo "The 'age' field is required.
"; } elseif ($age === FALSE) { echo "You must be enter a valid age and be between 7 and 77 years old.
"; } else { echo "Welcome."; } ?> " originalCode=" array('min_range'=>7, 'min_range'=>77)); $age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, $options); if (is_null($age)) { echo "The 'age' field is required.
"; } elseif ($age === FALSE) { echo "You must be enter a valid age and be between 7 and 77 years old.
"; } else { echo "Welcome."; } ?>">
view plain
|
print
|
copy to clipboard
|
?
array('min_range'=>7, 'min_range'=>77));
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, $options);
if (is_null($age)) {
echo "The 'age' field is required.
";
} elseif ($age === FALSE) {
echo "You must be enter a valid age and be between 7 and 77 years old.
";
} else {
echo "Welcome.";
}
?>
如果是要判断一个有效的邮件地址的话,可以这样:
1
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
view plain
|
print
|
copy to clipboard
|
?
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
如果邮件地址不正确或者是空的话$email的值将为FALSE。
过滤判断的例子
1
2
3
4
"example05.php" method="post" >
5
Enter your name: "name" size="50">
6
"submit" name="submit" value="Go">
7
8
9
Enter your name: ' originalCode=' Enter your name: '>
view plain
|
print
|
copy to clipboard
|
?
Enter your name:
下面的filter_input函数将自动过滤并返回适当的值:
1
2
if (!filter_has_var(INPUT_POST, 'submit')) {
3
echo "form";
4
// include the form.
5
}
6
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_SPECIAL_CHARS);
7
if (is_null($name)) {
8
echo "The 'name' field is required.
";
9
} else {
10
echo "Hello $name.";
11
}
12
?>
"; } ?> " originalCode=""; } ?>">
view plain
|
print
|
copy to clipboard
|
?
";
}
?>
如果接收到的name值是:
Johnny Weißmüller Jr
FILTER_SANITIZE_SPECIAL_CHARS 将会返回:
Hello Johnny Wei&#223;müller <b>Jr</b>.
一个更好的过滤写法:
1
$name = filter_input(INPUT_POST,
2
'name',
3
FILTER_SANITIZE_STRING,
4
FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW);
view plain
|
print
|
copy to clipboard
|
?
$name = filter_input(INPUT_POST,
'name',
FILTER_SANITIZE_STRING,
FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW);
输出:
Hello Johnny Weißmüller Jr.
这里的函数还有很多选项,如果你想了解更多的细节可以查看Filter的文档。
如何一次处理所有输入?
1
2
3
4
"example07.php" method="post" >
5
Name: "name" size="50">
6
Email: "email" size="50">
7
Homepage: "homepage" size="50">
8
Age: "age" size="4">
9
Income: "income" size="50">
10
Your two favourites languages:
11
"favourites[]" size="6" multiple>
12
"haskell">haskell
13
"r">R
14
"lua">Lua
15
"pike">Pike
16
"rebol">Rebol
17
"php">PHP
18
19
"submit" name="submit" value="Go">
20
21
22
Name:
Email:
Homepage:
Age:
Income:
Your two favourites languages: haskell R Lua Pike Rebol PHP
' originalCode=' Name:
Email:
Homepage:
Age:
Income:
Your two favourites languages: haskell R Lua Pike Rebol PHP
'>
view plain
|
print
|
copy to clipboard
|
?
Name:
Email:
Homepage:
Age:
Income:
Your two favourites languages:
haskell
R
Lua
Pike
Rebol
PHP
处理程序:
1
2
if (!filter_has_var(INPUT_POST, 'submit')) {
3
echo "form";
4
// include the form.
5
}
6
7
$defs = array(
8
'name' => array('filter'=>FILTER_SANITIZE_STRING,
9
'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW),
10
'email' => FILTER_VALIDATE_EMAIL,
11
'homepage' => FILTER_VALIDATE_URL,
12
'age' => array( 'filter' => FILTER_VALIDATE_INT,
13
'options'=> array('min_range'=>7, 'min_range'=>77)),
14
'income' => FILTER_VALIDATE_FLOAT,
15
'favourites' => array(
16
'filter' => FILTER_SANITIZE_STRING,
17
'flags' => FILTER_REQUIRE_ARRAY
18
),
19
);
20
21
$input = filter_input_array(INPUT_POST, $defs);
22
23
if ($input['age'] === FALSE) {
24
exit("You must be between 7 and 77 years old.");
25
}
26
27
if (is_null($input['favourites'])) {
28
exit("You have to choose two or more languages.");
29
}
30
31
if (!in_array('PHP', $inputs['favourites'])) {
32
exit("You don't like PHP!");
33
}
34
35
/*Other checks for required values */
36
?>
array('filter'=>FILTER_SANITIZE_STRING, 'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW), 'email' => FILTER_VALIDATE_EMAIL, 'homepage' => FILTER_VALIDATE_URL, 'age' => array( 'filter' => FILTER_VALIDATE_INT, 'options'=> array('min_range'=>7, 'min_range'=>77)), 'income' => FILTER_VALIDATE_FLOAT, 'favourites' => array( 'filter' => FILTER_SANITIZE_STRING, 'flags' => FILTER_REQUIRE_ARRAY ), ); $input = filter_input_array(INPUT_POST, $defs); if ($input['age'] === FALSE) { exit("You must be between 7 and 77 years old."); } if (is_null($input['favourites'])) { exit("You have to choose two or more languages."); } if (!in_array('PHP', $inputs['favourites'])) { exit("You don't like PHP!"); } /*Other checks for required values */ ?> " originalCode=" array('filter'=>FILTER_SANITIZE_STRING, 'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW), 'email' => FILTER_VALIDATE_EMAIL, 'homepage' => FILTER_VALIDATE_URL, 'age' => array( 'filter' => FILTER_VALIDATE_INT, 'options'=> array('min_range'=>7, 'min_range'=>77)), 'income' => FILTER_VALIDATE_FLOAT, 'favourites' => array( 'filter' => FILTER_SANITIZE_STRING, 'flags' => FILTER_REQUIRE_ARRAY ), ); $input = filter_input_array(INPUT_POST, $defs); if ($input['age'] === FALSE) { exit("You must be between 7 and 77 years old."); } if (is_null($input['favourites'])) { exit("You have to choose two or more languages."); } if (!in_array('PHP', $inputs['favourites'])) { exit("You don't like PHP!"); } /*Other checks for required values */ ?>">
view plain
|
print
|
copy to clipboard
|
?
array('filter'=>FILTER_SANITIZE_STRING,
'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW),
'email' => FILTER_VALIDATE_EMAIL,
'homepage' => FILTER_VALIDATE_URL,
'age' => array( 'filter' => FILTER_VALIDATE_INT,
'options'=> array('min_range'=>7, 'min_range'=>77)),
'income' => FILTER_VALIDATE_FLOAT,
'favourites' => array(
'filter' => FILTER_SANITIZE_STRING,
'flags' => FILTER_REQUIRE_ARRAY
),
);
$input = filter_input_array(INPUT_POST, $defs);
if ($input['age'] === FALSE) {
exit("You must be between 7 and 77 years old.");
}
if (is_null($input['favourites'])) {
exit("You have to choose two or more languages.");
}
if (!in_array('PHP', $inputs['favourites'])) {
exit("You don't like PHP!");
}
/*Other checks for required values */
?>
正如上面例子中的,通过一个函数就能处理所有的变量。唯一不同的就是事先定义一个对应的数组。需要注意的是数组中选项的正确性。
这样的做法不但增加了程序的易读性,并且如果要添加移除或者修改处理规则也会非常方便。
更复杂的处理
在下面的处理“favourites”变量时用到了用户自定义函数。"options"指定一个用户自定义函数通过定义callback来实现,语法和PHP的call_user_func一样。
1
2
class language {
3
function __construct($name) {
4
$this->name = $name;
5
}
6
}
7
8
function check_languages($var) {
9
static $called = 0;
10
$called++;
11
echo "called: $called: $var
";
12
$var = filter_var($var, FILTER_SANITIZE_STRIPPED);
13
$l = new language($var);
14
return $l;
15
}
16
17
if (!filter_has_var(INPUT_POST, 'submit')) {
18
echo "form";
19
// include the form.
20
}
21
22
$defs = array(
23
'name' => array('filter'=>FILTER_SANITIZE_STRING,
24
'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW),
25
'email' => FILTER_VALIDATE_EMAIL,
26
'homepage' => FILTER_VALIDATE_URL,
27
'age' => FILTER_VALIDATE_INT,
28
'income' => FILTER_VALIDATE_FLOAT,
29
'favourites' => array(
30
'filter' => FILTER_CALLBACK,
31
'options' => 'check_languages'
32
),
33
);
34
35
$input = filter_input_array(INPUT_POST, $defs);
36
37
if ($input['age'] === FALSE) {
38
exit("You must be between 7 and 77 years old.");
39
}
40
41
if (is_null($input['favourites'])) {
42
exit("You have to choose two or more languages.");
43
}
44
45
echo "Your favourite languages:
";
46
foreach ($input['favourites'] as $l) echo '' . $l->name . "";
47
echo '';
48
?>
name = $name; } } function check_languages($var) { static $called = 0; $called++; echo "called: $called: $var
"; $var = filter_var($var, FILTER_SANITIZE_STRIPPED); $l = new language($var); return $l; } if (!filter_has_var(INPUT_POST, 'submit')) { echo "form"; // include the form. } $defs = array( 'name' => array('filter'=>FILTER_SANITIZE_STRING, 'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW), 'email' => FILTER_VALIDATE_EMAIL, 'homepage' => FILTER_VALIDATE_URL, 'age' => FILTER_VALIDATE_INT, 'income' => FILTER_VALIDATE_FLOAT, 'favourites' => array( 'filter' => FILTER_CALLBACK, 'options' => 'check_languages' ), ); $input = filter_input_array(INPUT_POST, $defs); if ($input['age'] === FALSE) { exit("You must be between 7 and 77 years old."); } if (is_null($input['favourites'])) { exit("You have to choose two or more languages."); } echo "Your favourite languages:
"; $var = filter_var($var, FILTER_SANITIZE_STRIPPED); $l = new language($var); return $l; } if (!filter_has_var(INPUT_POST, 'submit')) { echo "form"; // include the form. } $defs = array( 'name' => array('filter'=>FILTER_SANITIZE_STRING, 'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW), 'email' => FILTER_VALIDATE_EMAIL, 'homepage' => FILTER_VALIDATE_URL, 'age' => FILTER_VALIDATE_INT, 'income' => FILTER_VALIDATE_FLOAT, 'favourites' => array( 'filter' => FILTER_CALLBACK, 'options' => 'check_languages' ), ); $input = filter_input_array(INPUT_POST, $defs); if ($input['age'] === FALSE) { exit("You must be between 7 and 77 years old."); } if (is_null($input['favourites'])) { exit("You have to choose two or more languages."); } echo "Your favourite languages:
view plain
|
print
|
copy to clipboard
|
?
name = $name;
}
}
function check_languages($var) {
static $called = 0;
$called++;
echo "called: $called: $var
";
$var = filter_var($var, FILTER_SANITIZE_STRIPPED);
$l = new language($var);
return $l;
}
if (!filter_has_var(INPUT_POST, 'submit')) {
echo "form";
// include the form.
}
$defs = array(
'name' => array('filter'=>FILTER_SANITIZE_STRING,
'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW),
'email' => FILTER_VALIDATE_EMAIL,
'homepage' => FILTER_VALIDATE_URL,
'age' => FILTER_VALIDATE_INT,
'income' => FILTER_VALIDATE_FLOAT,
'favourites' => array(
'filter' => FILTER_CALLBACK,
'options' => 'check_languages'
),
);
$input = filter_input_array(INPUT_POST, $defs);
if ($input['age'] === FALSE) {
exit("You must be between 7 and 77 years old.");
}
if (is_null($input['favourites'])) {
exit("You have to choose two or more languages.");
}
echo "Your favourite languages:
";
foreach ($input['favourites'] as $l) echo '' . $l->name . "";
echo '';
?>
如果参数是标准参数,函数将只会别调用一次,如果变量是一个数组,将会自动被数组的每个成员调用一次。
当使用callback的时候Filter不会转换或者验证输入数据。但是上面例子中的filter_var可以在callback函数或者方法中使用。
为什么不用面向对象
Filter不支持任何的对象。当前API已经足够灵活,能方便的添加各种函数,支持unicode,可以方便的整合到你自己的结构中去。
如果你感兴趣的话,很高兴你可以自己去研究。但我们在短期内不会提供对OO的支持。
Default filter, PHP 5.1.x and shared host
在PHP 5.2.0之后的版本中filter默认已经支持。如果你打算使用,事先要修改好你的程序或者提醒用户。
相信各种程序很快会依赖它。它虽然不能完全解决安全问题,但是为很多相同的问题和隐患提供了一个解决方案。
一些链接关于filter的链接
文档
http://www.php.net/filter
下载
http://pecl.php.net/get/filter
官方网站
http://pecl.php.net/filter
其他关于php安全方面的链接
http://www.php.net/security
http://talks.php.net/index.php/Security
http://phpsec.org
http://www.hardened-php.net/
欢迎和我交流讨论php的话题:
mailto:[email protected]
http://club.phpe.net/index.php?act=ST&f=16&t=14570&s=7b22ffc0bed79b9ce72dcb6795458d26
)
如有问题,请到原帖发表!
#######################################################
第一次翻译,请多谅解,如果看不懂的地方请参照原文:
http://devzone.zend.com/node/view/id/1113
程序的安全性已经成为php开发人员首要(或者经常要)考虑的问题。不论是在单独的,开源的或者商业项目中,它的重要性都越来越高。
安全性对于php是一个很重要的部分。但是所有的讨论重点都是用户的输入数据。我们在这里并不讨论诸如XSS,SQL注入这样的话题,如果你对这些感兴趣可以看本文结尾出的相关文章。
不要相信外部数据
几乎所有的应用程序(web,桌面,命令行)都需要接收相应的数据或者动作来完成一定的操作然后再输出结果。这些输入数据可能是用户或者其他的程序(网络客户端,病毒,扫描器等)。基本原则是:过滤所有的外来数据。对于输入的过滤是保证整个程序安全的基础,和采用什么语言或者什么系统毫无关系。Php提供了很多相应的函数来对数据进行转换或者验证,但是和其他语言比起来明显的缺陷是没有一个标准的函数来过滤所有数据。新的函数Filter实现了这个功能。
什么是外部输入数据?
所有的form提交的数据
所有的$_GET, $_POST, $_REQUEST
Cookie($_COOKIES)
网络服务器数据
文件
服务器信息(比如 $_SERVER[‘SERVER_NAME’])
环境变量
数据库的查询
Filter很好的支持了上述这些输入数据。
为什么用Filter?
Filter扩展库的目的就是要达到使数据过滤尽量的简单,下面是两个简单例子:
检查2个通过_GET方式输入的整数:
1
2
3
if (isset($_GET['mode'])) {
4
if (!is_numeric($_GET['mode'])) {
5
echo "The 'mode' argument must be a valid integer.
";
6
exit();
7
}
8
$mode = (int)$_GET['mode'];
9
} else {
10
echo "The 'mode' argument is missing.
";
11
exit();
12
}
13
14
if (isset($_GET['type'])) {
15
$type = (int)$_GET['type'];
16
if (!is_numeric($_GET['type']) ||
17
(is_numeric($_GET['type']) && $type >= 3 && $type
18
echo "The 'type' argument must be an integer between 3 and 10.
";
19
exit();
20
} else {
21
}
22
$mode = $_GET['type'];
23
} else {
24
echo "The 'type' argument is missing.
";
25
exit();
26
}
27
echo "Ok.
";
28
?>
= 3 && $type " originalCode="= 3 && $type ">
view plain
|
|
copy to clipboard
|
?
= 3 && $type
使用Filter扩展库的filter_input函数来过滤:
1
2
3
$mode = filter_input(INPUT_GET, 'mode', FILTER_VALIDATE_INT);
4
$type = filter_input(INPUT_GET, 'type', FILTER_VALIDATE_INT, array('options'=>array('min_range'=>3, 'max_range'=>10)));
5
6
if (is_null($mode)) {
7
echo "The 'mode' argument is missing.
";
8
exit();
9
} elseif ($mode === false) {
10
echo "The 'mode' argument must be a valid integer.
";
11
exit();
12
} else {
13
echo "mode is: $mode.
";
14
}
15
16
if (is_null($type)) {
17
echo "The 'type' argument is missing.
";
18
exit();
19
} elseif ($type === false) {
20
echo "The 'type' argument must be an integer between 3 and 10.
";
21
exit();
22
} else {
23
echo "type is: $type.
";
24
}
25
?>
array('min_range'=>3, 'max_range'=>10))); if (is_null($mode)) { echo "The 'mode' argument is missing.
"; exit(); } elseif ($mode === false) { echo "The 'mode' argument must be a valid integer.
"; exit(); } else { echo "mode is: $mode.
"; } if (is_null($type)) { echo "The 'type' argument is missing.
"; exit(); } elseif ($type === false) { echo "The 'type' argument must be an integer between 3 and 10.
"; exit(); } else { echo "type is: $type.
"; } ?> " originalCode="array('min_range'=>3, 'max_range'=>10))); if (is_null($mode)) { echo "The 'mode' argument is missing.
"; exit(); } elseif ($mode === false) { echo "The 'mode' argument must be a valid integer.
"; exit(); } else { echo "mode is: $mode.
"; } if (is_null($type)) { echo "The 'type' argument is missing.
"; exit(); } elseif ($type === false) { echo "The 'type' argument must be an integer between 3 and 10.
"; exit(); } else { echo "type is: $type.
"; } ?>">
view plain
|
|
copy to clipboard
|
?
array('min_range'=>3, 'max_range'=>10)));
if (is_null($mode)) {
echo "The 'mode' argument is missing.
";
exit();
} elseif ($mode === false) {
echo "The 'mode' argument must be a valid integer.
";
exit();
} else {
echo "mode is: $mode.
";
}
if (is_null($type)) {
echo "The 'type' argument is missing.
";
exit();
} elseif ($type === false) {
echo "The 'type' argument must be an integer between 3 and 10.
";
exit();
} else {
echo "type is: $type.
";
}
?>
它是如何工作的?
把一个请求处理成用户可以用到的变量是由SAPI层来完成的。这里不去研究SAPI层的具体细节,就把SAPI层看做是连接PHP引擎和web层之间的东西。PHP负责处理来自SAPI的数据(ENV,SERVER,COOKIE,GET,POST)并转换成能用的全局变量或者像getenv这样的函数能调用的数据。
Filter能处理SAPI层的数据和PHP引擎数据。SAPI支持自定义的过滤选项,当每一个外部数据进行处理的时候PHP引擎都会调用Filter函数。通过配置php.ini文件可以使得SAPI默认支持filter。
假设有一个这样的简单的POST请求:
POST /myform.php?myfield=hola
下图说明了正常情况下(php5.1或之前没有Filter扩展的时候)和安装了Filter扩展后在处理上的区别:
运行条件
Filter在PHP 5.1和之前的版本可以使用扩展方式。从PHP 5.2.0开始变成了PHP的标准库。
安装
PHP 5.2.0 中已经默认支持,所以不需要再额外安装。
Unix/Linux:
$ pecl install filter
或者
$ wget
http://pecl.php.net/get/filter
$ tar xzf filter
$ cd filter-0.11.0
$ phpize
$ ./configure
$ make
$ make install
Windows:
在这里
http://pecl4win.php.net
可以下载与PHP版本相对应的filter.dll 文件。
在任何系统中都需要修改php.ini文件加入 extension=filter.so 然后重起web服务。
原则
Filter 可以有两种形式:
1. 过滤判断
* 允许或者不允许字符串中有指定的字符
* 和数据的格式无关
* 返回一个字符串
2. 检测判断
* 强大的数据分析
* 已知的数据格式
* 成功返回指定的格式
可用到的函数:
* filter_input, 得到一个输入值
* filter_input_array, 一次得到多个输入值
* filter_var, 过滤一个值
* filter_var_array, 一次过滤多个值
如果成功的话将返回转换过之后的格式,如果失败(格式不符,值超出范围等等。)将返回FALSE,如果变量没有设置将返回NULL。使用FILTER_NULL_ON_FAILURE标记后将会在失败的时候返回NULL,变量没有设置的时候返回FALSE。
请看下面的例子,可以避免使用isset或者is_numeric这样的混乱局面。
检测判断的例子
1
2
3
4
"example04.php" method="post" >
5
Enter your age: "age" size="2">
6
"submit" name="submit" value="Go">
7
8
9
Enter your age: ' originalCode=' Enter your age: '>
view plain
|
|
copy to clipboard
|
?
Enter your age:
处理脚本:
1
2
if (!filter_has_var(INPUT_POST, 'submit')) {
3
echo "form";
4
// include the form.
5
}
6
7
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT);
8
if (is_null($age)) {
9
echo "The 'age' field is required.
";
10
} elseif ($age === FALSE) {
11
echo "Please enter a valid age.
";
12
} else {
13
echo "Welcome.";
14
}
15
?>
"; } ?> " originalCode=""; } ?>">
view plain
|
|
copy to clipboard
|
?
";
}
?>
filter_has_var检测给定的变量是否存在。它不会做任何处理只会告诉程序变量是否已经设置。相当于isset($_POST[‘submit’])。filter_input会取得一个变量返回处理好的数据。在上面的例子中会返回一个整数。
如果是要返回一个处于一定范围之内的值,假设是7-77岁之间的人。可以指定一个最大值和一个最小值。
1
2
if (!filter_has_var(INPUT_POST, 'submit')) {
3
echo "form";
4
// include the form.
5
}
6
$options = array('options'=> array('min_range'=>7, 'min_range'=>77));
7
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, $options);
8
9
if (is_null($age)) {
10
echo "The 'age' field is required.
";
11
} elseif ($age === FALSE) {
12
echo "You must be enter a valid age and be between 7 and 77 years old.
";
13
} else {
14
echo "Welcome.";
15
}
16
?>
array('min_range'=>7, 'min_range'=>77)); $age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, $options); if (is_null($age)) { echo "The 'age' field is required.
"; } elseif ($age === FALSE) { echo "You must be enter a valid age and be between 7 and 77 years old.
"; } else { echo "Welcome."; } ?> " originalCode=" array('min_range'=>7, 'min_range'=>77)); $age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, $options); if (is_null($age)) { echo "The 'age' field is required.
"; } elseif ($age === FALSE) { echo "You must be enter a valid age and be between 7 and 77 years old.
"; } else { echo "Welcome."; } ?>">
view plain
|
|
copy to clipboard
|
?
array('min_range'=>7, 'min_range'=>77));
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, $options);
if (is_null($age)) {
echo "The 'age' field is required.
";
} elseif ($age === FALSE) {
echo "You must be enter a valid age and be between 7 and 77 years old.
";
} else {
echo "Welcome.";
}
?>
如果是要判断一个有效的邮件地址的话,可以这样:
1
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
view plain
|
|
copy to clipboard
|
?
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
如果邮件地址不正确或者是空的话$email的值将为FALSE。
过滤判断的例子
1
2
3
4
"example05.php" method="post" >
5
Enter your name: "name" size="50">
6
"submit" name="submit" value="Go">
7
8
9
Enter your name: ' originalCode=' Enter your name: '>
view plain
|
|
copy to clipboard
|
?
Enter your name:
下面的filter_input函数将自动过滤并返回适当的值:
1
2
if (!filter_has_var(INPUT_POST, 'submit')) {
3
echo "form";
4
// include the form.
5
}
6
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_SPECIAL_CHARS);
7
if (is_null($name)) {
8
echo "The 'name' field is required.
";
9
} else {
10
echo "Hello $name.";
11
}
12
?>
"; } ?> " originalCode=""; } ?>">
view plain
|
|
copy to clipboard
|
?
";
}
?>
如果接收到的name值是:
Johnny Weißmüller Jr
FILTER_SANITIZE_SPECIAL_CHARS 将会返回:
Hello Johnny Wei&#223;müller <b>Jr</b>.
一个更好的过滤写法:
1
$name = filter_input(INPUT_POST,
2
'name',
3
FILTER_SANITIZE_STRING,
4
FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW);
view plain
|
|
copy to clipboard
|
?
$name = filter_input(INPUT_POST,
'name',
FILTER_SANITIZE_STRING,
FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW);
输出:
Hello Johnny Weißmüller Jr.
这里的函数还有很多选项,如果你想了解更多的细节可以查看Filter的文档。
如何一次处理所有输入?
1
2
3
4
"example07.php" method="post" >
5
Name: "name" size="50">
6
Email: "email" size="50">
7
Homepage: "homepage" size="50">
8
Age: "age" size="4">
9
Income: "income" size="50">
10
Your two favourites languages:
11
"favourites[]" size="6" multiple>
12
"haskell">haskell
13
"r">R
14
"lua">Lua
15
"pike">Pike
16
"rebol">Rebol
17
"php">PHP
18
19
"submit" name="submit" value="Go">
20
21
22
Name:
Email:
Homepage:
Age:
Income:
Your two favourites languages: haskell R Lua Pike Rebol PHP
' originalCode=' Name:
Email:
Homepage:
Age:
Income:
Your two favourites languages: haskell R Lua Pike Rebol PHP
'>
view plain
|
|
copy to clipboard
|
?
Name:
Email:
Homepage:
Age:
Income:
Your two favourites languages:
haskell
R
Lua
Pike
Rebol
PHP
处理程序:
1
2
if (!filter_has_var(INPUT_POST, 'submit')) {
3
echo "form";
4
// include the form.
5
}
6
7
$defs = array(
8
'name' => array('filter'=>FILTER_SANITIZE_STRING,
9
'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW),
10
'email' => FILTER_VALIDATE_EMAIL,
11
'homepage' => FILTER_VALIDATE_URL,
12
'age' => array( 'filter' => FILTER_VALIDATE_INT,
13
'options'=> array('min_range'=>7, 'min_range'=>77)),
14
'income' => FILTER_VALIDATE_FLOAT,
15
'favourites' => array(
16
'filter' => FILTER_SANITIZE_STRING,
17
'flags' => FILTER_REQUIRE_ARRAY
18
),
19
);
20
21
$input = filter_input_array(INPUT_POST, $defs);
22
23
if ($input['age'] === FALSE) {
24
exit("You must be between 7 and 77 years old.");
25
}
26
27
if (is_null($input['favourites'])) {
28
exit("You have to choose two or more languages.");
29
}
30
31
if (!in_array('PHP', $inputs['favourites'])) {
32
exit("You don't like PHP!");
33
}
34
35
/*Other checks for required values */
36
?>
array('filter'=>FILTER_SANITIZE_STRING, 'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW), 'email' => FILTER_VALIDATE_EMAIL, 'homepage' => FILTER_VALIDATE_URL, 'age' => array( 'filter' => FILTER_VALIDATE_INT, 'options'=> array('min_range'=>7, 'min_range'=>77)), 'income' => FILTER_VALIDATE_FLOAT, 'favourites' => array( 'filter' => FILTER_SANITIZE_STRING, 'flags' => FILTER_REQUIRE_ARRAY ), ); $input = filter_input_array(INPUT_POST, $defs); if ($input['age'] === FALSE) { exit("You must be between 7 and 77 years old."); } if (is_null($input['favourites'])) { exit("You have to choose two or more languages."); } if (!in_array('PHP', $inputs['favourites'])) { exit("You don't like PHP!"); } /*Other checks for required values */ ?> " originalCode=" array('filter'=>FILTER_SANITIZE_STRING, 'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW), 'email' => FILTER_VALIDATE_EMAIL, 'homepage' => FILTER_VALIDATE_URL, 'age' => array( 'filter' => FILTER_VALIDATE_INT, 'options'=> array('min_range'=>7, 'min_range'=>77)), 'income' => FILTER_VALIDATE_FLOAT, 'favourites' => array( 'filter' => FILTER_SANITIZE_STRING, 'flags' => FILTER_REQUIRE_ARRAY ), ); $input = filter_input_array(INPUT_POST, $defs); if ($input['age'] === FALSE) { exit("You must be between 7 and 77 years old."); } if (is_null($input['favourites'])) { exit("You have to choose two or more languages."); } if (!in_array('PHP', $inputs['favourites'])) { exit("You don't like PHP!"); } /*Other checks for required values */ ?>">
view plain
|
|
copy to clipboard
|
?
array('filter'=>FILTER_SANITIZE_STRING,
'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW),
'email' => FILTER_VALIDATE_EMAIL,
'homepage' => FILTER_VALIDATE_URL,
'age' => array( 'filter' => FILTER_VALIDATE_INT,
'options'=> array('min_range'=>7, 'min_range'=>77)),
'income' => FILTER_VALIDATE_FLOAT,
'favourites' => array(
'filter' => FILTER_SANITIZE_STRING,
'flags' => FILTER_REQUIRE_ARRAY
),
);
$input = filter_input_array(INPUT_POST, $defs);
if ($input['age'] === FALSE) {
exit("You must be between 7 and 77 years old.");
}
if (is_null($input['favourites'])) {
exit("You have to choose two or more languages.");
}
if (!in_array('PHP', $inputs['favourites'])) {
exit("You don't like PHP!");
}
/*Other checks for required values */
?>
正如上面例子中的,通过一个函数就能处理所有的变量。唯一不同的就是事先定义一个对应的数组。需要注意的是数组中选项的正确性。
这样的做法不但增加了程序的易读性,并且如果要添加移除或者修改处理规则也会非常方便。
更复杂的处理
在下面的处理“favourites”变量时用到了用户自定义函数。"options"指定一个用户自定义函数通过定义callback来实现,语法和PHP的call_user_func一样。
1
2
class language {
3
function __construct($name) {
4
$this->name = $name;
5
}
6
}
7
8
function check_languages($var) {
9
static $called = 0;
10
$called++;
11
echo "called: $called: $var
";
12
$var = filter_var($var, FILTER_SANITIZE_STRIPPED);
13
$l = new language($var);
14
return $l;
15
}
16
17
if (!filter_has_var(INPUT_POST, 'submit')) {
18
echo "form";
19
// include the form.
20
}
21
22
$defs = array(
23
'name' => array('filter'=>FILTER_SANITIZE_STRING,
24
'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW),
25
'email' => FILTER_VALIDATE_EMAIL,
26
'homepage' => FILTER_VALIDATE_URL,
27
'age' => FILTER_VALIDATE_INT,
28
'income' => FILTER_VALIDATE_FLOAT,
29
'favourites' => array(
30
'filter' => FILTER_CALLBACK,
31
'options' => 'check_languages'
32
),
33
);
34
35
$input = filter_input_array(INPUT_POST, $defs);
36
37
if ($input['age'] === FALSE) {
38
exit("You must be between 7 and 77 years old.");
39
}
40
41
if (is_null($input['favourites'])) {
42
exit("You have to choose two or more languages.");
43
}
44
45
echo "Your favourite languages:
";
46
foreach ($input['favourites'] as $l) echo '' . $l->name . "";
47
echo '';
48
?>
name = $name; } } function check_languages($var) { static $called = 0; $called++; echo "called: $called: $var
"; $var = filter_var($var, FILTER_SANITIZE_STRIPPED); $l = new language($var); return $l; } if (!filter_has_var(INPUT_POST, 'submit')) { echo "form"; // include the form. } $defs = array( 'name' => array('filter'=>FILTER_SANITIZE_STRING, 'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW), 'email' => FILTER_VALIDATE_EMAIL, 'homepage' => FILTER_VALIDATE_URL, 'age' => FILTER_VALIDATE_INT, 'income' => FILTER_VALIDATE_FLOAT, 'favourites' => array( 'filter' => FILTER_CALLBACK, 'options' => 'check_languages' ), ); $input = filter_input_array(INPUT_POST, $defs); if ($input['age'] === FALSE) { exit("You must be between 7 and 77 years old."); } if (is_null($input['favourites'])) { exit("You have to choose two or more languages."); } echo "Your favourite languages:
-
"; foreach ($input['favourites'] as $l) echo '
- ' . $l->name . ""; echo '
"; $var = filter_var($var, FILTER_SANITIZE_STRIPPED); $l = new language($var); return $l; } if (!filter_has_var(INPUT_POST, 'submit')) { echo "form"; // include the form. } $defs = array( 'name' => array('filter'=>FILTER_SANITIZE_STRING, 'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW), 'email' => FILTER_VALIDATE_EMAIL, 'homepage' => FILTER_VALIDATE_URL, 'age' => FILTER_VALIDATE_INT, 'income' => FILTER_VALIDATE_FLOAT, 'favourites' => array( 'filter' => FILTER_CALLBACK, 'options' => 'check_languages' ), ); $input = filter_input_array(INPUT_POST, $defs); if ($input['age'] === FALSE) { exit("You must be between 7 and 77 years old."); } if (is_null($input['favourites'])) { exit("You have to choose two or more languages."); } echo "Your favourite languages:
-
"; foreach ($input['favourites'] as $l) echo '
- ' . $l->name . ""; echo '
view plain
|
|
copy to clipboard
|
?
name = $name;
}
}
function check_languages($var) {
static $called = 0;
$called++;
echo "called: $called: $var
";
$var = filter_var($var, FILTER_SANITIZE_STRIPPED);
$l = new language($var);
return $l;
}
if (!filter_has_var(INPUT_POST, 'submit')) {
echo "form";
// include the form.
}
$defs = array(
'name' => array('filter'=>FILTER_SANITIZE_STRING,
'flags' => FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW),
'email' => FILTER_VALIDATE_EMAIL,
'homepage' => FILTER_VALIDATE_URL,
'age' => FILTER_VALIDATE_INT,
'income' => FILTER_VALIDATE_FLOAT,
'favourites' => array(
'filter' => FILTER_CALLBACK,
'options' => 'check_languages'
),
);
$input = filter_input_array(INPUT_POST, $defs);
if ($input['age'] === FALSE) {
exit("You must be between 7 and 77 years old.");
}
if (is_null($input['favourites'])) {
exit("You have to choose two or more languages.");
}
echo "Your favourite languages:
";
foreach ($input['favourites'] as $l) echo '' . $l->name . "";
echo '';
?>
如果参数是标准参数,函数将只会别调用一次,如果变量是一个数组,将会自动被数组的每个成员调用一次。
当使用callback的时候Filter不会转换或者验证输入数据。但是上面例子中的filter_var可以在callback函数或者方法中使用。
为什么不用面向对象
Filter不支持任何的对象。当前API已经足够灵活,能方便的添加各种函数,支持unicode,可以方便的整合到你自己的结构中去。
如果你感兴趣的话,很高兴你可以自己去研究。但我们在短期内不会提供对OO的支持。
Default filter, PHP 5.1.x and shared host
在PHP 5.2.0之后的版本中filter默认已经支持。如果你打算使用,事先要修改好你的程序或者提醒用户。
相信各种程序很快会依赖它。它虽然不能完全解决安全问题,但是为很多相同的问题和隐患提供了一个解决方案。
一些链接关于filter的链接
文档
http://www.php.net/filter
下载
http://pecl.php.net/get/filter
官方网站
http://pecl.php.net/filter
其他关于php安全方面的链接
http://www.php.net/security
http://talks.php.net/index.php/Security
http://phpsec.org
http://www.hardened-php.net/
欢迎和我交流讨论php的话题:
mailto:[email protected]
相关阅读 更多 +
排行榜 更多 +