前言

PHP对字符串处理方面支持度很高,有很多字符串处理函数。面对复杂的WEB环境和各种网络攻击,需要保证我们在处理字符串时要考虑到安全性的方面。

正文

作为一种Web脚本语言,有两种很常见的需求:

  1. 处理用户输入;
  2. 将结果输出到WEB页面上进行显示;

对(1)处理不当,可能会产生SQL注入、XSS攻击等,对(2)处理不当,也会造成站点被XSS攻击,格式错乱等错误。

从安全角度出发,对于上述二者的处理,要遵循以下原则:

永远不要信任用户输入,在输出到页面前一定要进行转义。

处理用户输入

防止SQL注入

当用户的输入需要存入数据库时,我们要注意防止SQL注入。在输入时,可以通过mysql_real_escape_string函数来进行对输入字符串进行转义。

不过这个函数在PHP5.5的时候已经弃用了。

使用高于5.5的PHP进行数据库操作时,强烈推荐使用PDO方式连接数据库。

验证用户输入合法性

当存在用户输入时,我们需要判断用户输入的合法性,如是否是合法邮箱、URL、IP地址等。这个功能通常是通过正则匹配来完成的,但是正则匹配比较难写,并且容易出错,所以推荐使用PHP的Filter扩展。

这个扩展定义了常见输入格式的过滤器,通过调用filter_var函数可以轻松地实现对于用户输入的过滤。

示例代码:

var_dump(filter_var('bob@example.com', FILTER_VALIDATE_EMAIL));
var_dump(filter_var('http://example.com', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED));

执行结果:

string(15) "bob@example.com" 
bool(false)

说明 bob@example.com 是一个合法的Email,而 http://example.com 不是一个带文件路径的URL。

剔除HTML标记

在一些允许用户会更新的站点,我们需要剔除用户输入的HTML标签来避免一些恶意用户进行的非法操作。

这时可以采用strip_tags函数来实现这个功能,它会移除以<开头和以>结尾的内容,其他内容保持不变。

示例代码:

$string = 'This is <b>bold</b> and this is <i>italic</i>. This is a <a href="xxxx">link</a>';
echo $string;
echo '<br>';
echo strip_tags($string);

页面展示如下:

可见,直接输出是带有HTML标签的,而通过strip_tags处理之后,没有了HTML标签,直接输出了一个字符串。

另外,strip_tags的第二个参数可以设置忽略的标签,如不想剔除<b>,可以将它作为第二个参数。

$string = 'This is <b>bold</b> and this is <i>italic</i>. This is a <a href="xxxx">link</a>';
echo strip_tags($string, '<b>');

页面展示如下:

可见,除了函数忽略了<b>标签。

注:如果<没有闭合,那么strip_tags函数会删除从当前位置到字符串尾部的所有文本。

输出内容到页面

在输出内容到页面时,我们要考虑到某些字符串可能会有特殊的含义,如果处理不当,最好的情况是输出的字符串难以理解;最差的情况,输出的字符串造成了某些安全问题。

所以,在输出时,要注意以下这几个问题:

  • 输出是否是URL(<a>标签内的href值)?
  • 输出是否是HTML元素?
  • 是否要保留格式?

构造URL

在构造URL时,如果这个URL中包含着汉字、空格、&、<、>等特殊符号时,可能的输出和我们想到不太一样,这时需要进行URL转义。

PHP中有两个函数,可以用于URL转义,分别是rawurlencodeurlencode,它们的区别只有一点:

对于空格,rawurlencode会转义为%20urlencode会转义为+

在输出URL时,推荐选用rawurlencode

输出为HTML元素

如果要将输出直接显示在HTML页面上,在输出前最好用htmlentities函数将内容进行转换,转成HTML实体,这样可以在一定程度上防范XSS攻击。

例如下面的代码:

$output = '<script>alert("hello")</script>';
echo $output;

当打开这个页面时,就会执行这个JS语句,弹出一个提示框,进行了XSS攻击,效果如下:

但是如果我们将它通过htmlentities进行转义,就会直接输出这句话。

$output = '<script>alert("hello")</script>';
echo htmlentities($output);

不会产生XSS攻击,执行结果如下:

保留换行格式

有时候,我们希望页面上的显示内容能和输出字符串一样,也就是能保留换行,这时我们可以用nl2br函数。

示例代码:

$output = '1st line
2nd line
3rd line';
echo $output;

输出如下:

1st line 2nd line 3rd line

实例代码:

$output = '1st line
2nd line
3rd line';
echo nl2br($output);

输出如下:

1st line
2nd line
3rd line

指定字符数后换行输出

这个需求在我们需要输出到一个指定宽度的区域内很常见,例如div的宽度只有10字符,我们希望输出的字符串也能每10个字符进行一次换行。

这时可以使用wordwrap来实现,它的作用是每隔n个字符插入指定字符,当指定字符为<br>时,就实现了每隔n个字符进行换行。

示例代码:

$output = 'this is a very-long-long-long word.';
echo wordwrap($output, 10, '<br>');

此时输出为:

this is a
very-long-long-long
word.

我们发现其实并没有严格按照每10个字符进行分割,而是保留了单词的完整性。如果想严格按照10个字符进行切割,将第4个参数设置为true。

$output = 'this is a very-long-long-long word';
echo wordwrap($output, 10, '<br>', true);

此时输出为:

this is a
very-long-
long-long
word

总结

其实这篇博客就是介绍了几个函数的使用:

  1. URL转义:rawurlencode/urlencode;
  2. HTML转义:htmlentities
  3. 保留换行:nl2br
  4. 指定字符数换行:wordwrap
  5. 剔除HTML标签:strip_tags
  6. 验证用户输入合法性:Filter扩展