1. 概述
1.1 XSS
XSS是跨站脚本攻击的缩写。本质上是攻击者注入一段 script脚本 来达到攻击目的。因此防范方法可以对script 脚本的特殊字符做转义处理。
1.2 CSRF
CSRF是跨域请求伪造的缩写。实现过程:
- 用户U打开浏览器,访问网站A,并登录;
- 网站A产生Cookie信息并返回给浏览器,在浏览器访问A的时候,会自动带上对应的Cookie;
- 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
- 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
- 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户U的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
1.3 SQL注入
2. 防范
2.1 防范XSS
防范可以在后端接口等能进入到内部的入口处,对用户输入的数据进行过滤。htmlentities()各方面都和htmlspecialchars()一样,除了 htmlentities() 会转换所有具有 HTML 实体的字符。如果要解码(反向操作),可以使用html_entity_decode()。2.2 防范CSRF
- 尽量使用POST,少用GET。
- 检查 Referer 是否同个域名发起的。
- Anti CSRF Token
例子:
- 用户访问某个表单页面。
- 服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie中。在页面表单附带上Token参数。
- 用户提交请求后, 服务端验证表单中的Token是否与用户Session(或Cookies)中的Token一致,一致为合法请求,不是则非法请求。
- 这个Token的值必须是随机的,不可预测的。由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。
- 在 HTTP 头中自定义属性并验证
验证HTTP请求头的csrfToken,前后端约定一个token。
2.3 防范SQL注入
防范SQL注入使用的方法本质上是进行转义,庆幸的是,PHP中有对应的自带处理函数处理。当使用PDO或Mysqli类通过对执行的语句进行预处理来防范:
2.3.1 转义
- addslashes()
返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线。这些字符是:
- 单引号(’)
- 双引号(”)
- 反斜线(\)
- NULL(NULL 字符)。
一个使用 addslashes() 的例子是当你要往数据库中输入数据时。( 例如:字符串 O’reilly 插入到数据库,这就需要进行转义)。 强烈建议使用 DBMS 指定的转义函数 ( MySQL: mysqli_real_escape_string() )。 当 PHP 指令 magic_quotes_sybase 被设置成 on 时,意味着插入 ‘ 时将使用 ‘ 进行转义。PHP 5.4 之前 PHP 指令 magic_quotes_gpc 默认是 on, 实际上 GET、POST 和 COOKIE 数据都用被 addslashes() 了。 不要对已经被 magic_quotes_gpc 转义过的字符串使用 addslashes(),因为这样会导致双层转义。 遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测(使用 stripslashes() 可以解转义)。
- mysql_escape_string()
mysql_escape_string() 对字符串进行转义时,不转义 % 和 _ ,与 mysql_real_escape_string() 完全一样,除了mysql_real_escape_string()接受的是一个连接句柄并根据当前字符集转义字符串之外。、
- mysql_real_escape_string()
本扩展自 PHP 5.5.0 起已废弃,并在自 PHP 7.0.0 开始被移除。应使用 MySQLi 或 PDO_MySQL 扩展来替换之。
- mysqli_real_escape_string()
- PDO::quote()
2.3.2 DBMS预处理方法
- 使用PDO ```php <? $stmt = $pdo->prepare(‘SELECT * FROM employees WHERE name = :name’);
$stmt->execute(array(‘:name’ => $name));
需要注意的是使用PDO去访问MySQL数据库时,真正的prepared statements默认情况下是不使用的。为了解决这个问题,需要禁用模拟的prepared statements。```php<?$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');// 禁用模拟的prepared statements,使用真正的prepared statements$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);// 设置错误报告模式,不会抛出一个致命错误终止,而是抛出PDO Exceptions$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
预处理在使用上的感受是,通过占位符来传递参数。例:
<?$pre = $pdo->prepare('INSERT INTO table (column) VALUES (:column)');$pre->execute(array(':column' => $unsafeValue));
- 使用Mysqli ```php <? $stmt = $dbConnection->prepare(‘SELECT * FROM employees WHERE name = ?’); $stmt->bind_param(‘s’, $name);
$stmt->execute(); ```
