1. 概述

1.1 XSS

XSS是跨站脚本攻击的缩写。本质上是攻击者注入一段 script脚本 来达到攻击目的。因此防范方法可以对script 脚本的特殊字符做转义处理。

1.2 CSRF

CSRF是跨域请求伪造的缩写。实现过程:

  1. 用户U打开浏览器,访问网站A,并登录;
  2. 网站A产生Cookie信息并返回给浏览器,在浏览器访问A的时候,会自动带上对应的Cookie;
  3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
  4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
  5. 浏览器在接收到这些攻击性代码后,根据网站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

  1. 尽量使用POST,少用GET。
  2. 检查 Referer 是否同个域名发起的。
  3. Anti CSRF Token

例子:

  1. 用户访问某个表单页面。
  2. 服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie中。在页面表单附带上Token参数。
  3. 用户提交请求后, 服务端验证表单中的Token是否与用户Session(或Cookies)中的Token一致,一致为合法请求,不是则非法请求。
  4. 这个Token的值必须是随机的,不可预测的。由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。
    1. 在 HTTP 头中自定义属性并验证

验证HTTP请求头的csrfToken,前后端约定一个token。

2.3 防范SQL注入

防范SQL注入使用的方法本质上是进行转义,庆幸的是,PHP中有对应的自带处理函数处理。当使用PDO或Mysqli类通过对执行的语句进行预处理来防范:

2.3.1 转义

  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() 可以解转义)。

  1. mysql_escape_string()

mysql_escape_string() 对字符串进行转义时,不转义 %_ ,与 mysql_real_escape_string() 完全一样,除了mysql_real_escape_string()接受的是一个连接句柄并根据当前字符集转义字符串之外。、

  1. 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预处理方法

  1. 使用PDO ```php <? $stmt = $pdo->prepare(‘SELECT * FROM employees WHERE name = :name’);

$stmt->execute(array(‘:name’ => $name));

  1. 需要注意的是使用PDO去访问MySQL数据库时,真正的prepared statements默认情况下是不使用的。为了解决这个问题,需要禁用模拟的prepared statements
  2. ```php
  3. <?
  4. $dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');
  5. // 禁用模拟的prepared statements,使用真正的prepared statements
  6. $dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
  7. // 设置错误报告模式,不会抛出一个致命错误终止,而是抛出PDO Exceptions
  8. $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

预处理在使用上的感受是,通过占位符来传递参数。例:

  1. <?
  2. $pre = $pdo->prepare('INSERT INTO table (column) VALUES (:column)');
  3. $pre->execute(array(':column' => $unsafeValue));
  1. 使用Mysqli ```php <? $stmt = $dbConnection->prepare(‘SELECT * FROM employees WHERE name = ?’); $stmt->bind_param(‘s’, $name);

$stmt->execute(); ```