PHP开发中常见的安全问题详解和解决方法(如Sql注入、CSRF、Xss、CC等)

作者:依旧十八岁的夕阳小子(博客园 @chengzhi59)
发布时间:2017-07-29 14:44
原文链接:<https://www.cnblogs.com/chengzhi59/p/7255891.html>
转载出处:<http://www.jb51.net/article/36044.htm>

前言

首先,笔者不是web安全的专家,所以这不是web安全方面专家级文章,而是学习笔记、细心总结文章,里面有些是我们phper不易发现或者说不重视的东西。所以笔者写下来方便以后查阅。在大公司肯定有专门的web安全测试员,安全方面不是phper考虑的范围。但是作为一个phper对于安全知识是:"知道有这么一回事,编程时自然有所注意"。

目录

  1. php一些安全配置

- 关闭php提示错误功能

- 关闭一些"坏功能"

- 严格配置文件权限

  1. 严格的数据验证,你的用户不全是"好"人

- 2.1 数据验证应该包括的内容

- 2.2 程序员容易漏掉 point 或者说需要注意的事项

  1. 防注入

- 3.1 简单判断是否有注入漏洞以及原理

- 3.2 常见的 mysql 注入语句

- 3.3 防注入的一些方法

  1. 防止 xss 攻击

- 4.1 Xss 攻击过程

- 4.2 常见 xss 攻击地方

- 4.3 防 XSS 方法

  1. CSRF

- 5.1 简单说明 CSRF 原理

- 5.2 防范方法

  1. 防盗链
  2. 防 CC 攻击

一、php一些安全配置

(1)关闭php提示错误功能

在 php.ini 中把 display_errors 改成:

display_errors = OFF

或在 php 文件前加入:

error_reporting(0)

1)使用 error_reporting(0); 失败的例子

A 文件代码:

<?
error_reporting(0);
echo 555
echo 444;
?>

错误:

Parse error: parse error, expecting `','' or `';'' in E:\webphp\2.php on line 4

2)使用 error_reporting(0); 成功的例子

a 文件代码:

<?php
error_reporting(0);
include("b.php");
?>

b 文件代码:

<?php
echo 555
echo 444;
?>

这是很多phper说用error_reporting(0)不起作用。第一个例子A.php里面有致命错误,导致不能执行,不能执行服务器则不知有这个功能,所以一样报错。

第二个例子中a.php成功执行,那么服务器知道有抑制错误功能,所以就算b.php有错误也抑制了。

ps:抑制不了 mysql 错误。

(2)关闭一些"坏功能"

1)关闭 magic quotes 功能

在 php.ini 把 magic_quotes_gpc = OFF,避免和 addslashes 等重复转义。

2)关闭 register_globals = Off

在 php.ini 把 register_globals = OFF。

在 register_globals = ON 的情况下:

地址栏目:http://www.jb51.net?bloger=benwin

<?php
//$bloger = $_GET['bloger']   //因为register_globals = ON 所以这步不用了直接可以用$bloger
  echo $bloger;
?>

这种情况下会导致一些未初始化的变量很容易被修改,这也许是致命的。所以把 register_globals = OFF 关掉。

(3)严格配置文件权限。

为相应文件夹分配权限,比如包含上传图片的文件不能有执行权限,只能读取。

二、严格的数据验证,你的用户不全是"好"人。

记得笔者和一个朋友在讨论数据验证的时候,他说了一句话:你不要把你用户个个都想得那么坏!但笔者想说的这个问题不该出现在我们开发情景中,我们要做的是严格验证控制数据流,哪怕10000万用户中有一个是坏用户也足以致命,再说好的用户也有时在数据input框无意输入中文的时,他已经不经意变"坏"了。

2.1 为了确保程序的安全性、健壮性,数据验证应该包括

  1. 关键数据是否存在。如删除数据 id 是否存在。
  2. 数据类型是否正确。如删除数据 id 是否是整数。
  3. 数据长度。如字段是 char(10) 类型则要 strlen 判断数据长度。
  4. 数据是否有危险字符。

数据验证有些人主张是把功能完成后再慢慢去写安全验证,也有些是边开发边写验证。笔者偏向后者,这两种笔者都试过,然后发现后者写的验证相对健壮些,主要原因是刚开发时想到的安全问题比较齐全,等开发完功能再写时有两个问题,一个phper急于完成指标草草完事,二是确实漏掉某些 point。

2.2 程序员容易漏掉 point 或者说需要注意的事项

  1. 进库数据一定要安全验证。 笔者在广州某家公司参与一个公司内部系统开发的时候,见过直接把 $_POST 数据传给类函数 classFunctionName($_POST),理由竟然是公司内部使用的,不用那么严格。暂且不说逻辑操作与数据操控耦合高低问题,连判断都没判断的操作是致命的。安全验证必须,没任何理由推脱。
  2. 数据长度问题。 如数据库建表字段 char(25),大多phper考虑到是否为空、数据类型是否正确,却忽略字符长度,忽略还好更多是懒于再去判断长度。(这个更多出现在新手当中,笔者曾经也有这样的思想)
  3. 以为前端用 js 判断验证过了,后台不需要判断验证。 这也是致命,要知道伪造一个表单就几分钟的事,js判断只是为了减少用户提交次数从而提高用户体验、减少 http 请求减少服务器压力,在安全情况下不能防"小人",当然如果合法用户在 js 验证控制下是完美的,但作为 phper 我们不能只有 js 验证而抛弃再一次安全验证。
  4. 缺少对表单某些属性(如 select、checkbox、radio、button 等)的验证。 这些属性在 web 页面上开发者已经设定其值和值域(白名单值),这些属性值在 js 验证方面一般不会验证,因为合法用户只有选择权没修改权,然后 phper 就在后端接受数据处理验证数据的时候不会验证这些数据,这是一个惯性思维,安全问题也就有了,小人一个伪表单。
  5. 表单相应元素 name 和数据表的字段名一致。 如用户表用户名的字段是 user_name,然后表单中的用户名输入框也是 user_name,这和暴库没什么区别。
  6. 过滤危险字符方面,如防注入,下面会独立讲解。

三、防注入

3.1 简单判断是否有注入漏洞以及原理

网址:http://www.jb51.net/benwin.php?id=1 运行正常,sql 语句如:

select * from phpben where id = 1
  1. 网址:http://www.jb51.net/benwin.php?id=1',sql 语句如:

```sql

select * from phpben where id = 1'

```

然后运行异常,这能说明 benwin.php 文件没有对 id 的值进行 ' 过滤和 intval() 整形转换。当然想知道有没有对其他字符如 %、/* 等都可以用类似的方法穷举测试(很多测试软件使用)。

  1. 网址:http://www.jb51.net/benwin.php?id=1 and 1=1,则 sql 语句可能是:

```sql

select * from phpben where id = 1 and 1=1

```

运行正常且结果和 http://www.jb51.net/benwin.php?id=1 结果一样,则说明 benwin.php 可能没有对空格 " "、和 and 过滤(这里是可能,所以要看下一点)。

  1. 网址:http://www.jb51.net/benwin.php?id=1 and 1=2,则 sql 语句可能是:

```sql

select * from phpben where id = 1 and 1=2

```

如果运行结果异常说明 sql 语句中 and 1=2 起作用,所以能3个条件都满足,则很确定 benwin.php 存在注入漏洞。

ps:这里用 get 方法验证,post 也可以,只要把值按上面的输入,可以一一验证。

3.2 常见的 mysql 注入语句

(1)不用用户名和密码

//正常语句
$sql ="select * from phpben where user_name='admin' and pwd ='123'";
//在用户名框输入'or'='or'或 'or 1='1 然后sql如下
$sql ="select * from phpben where user_name=' 'or'='or'' and pwd ='' ";
$sql ="select * from phpben where user_name=' 'or 1='1' and pwd ='' ";

这样不用输入密码。话说笔者见到登录框都有尝试的冲动。

(2)在不输入密码的情况下,利用某用户

//正常语句
$sql ="select * from phpben where user_name='$username' and pwd ='$pwd'";
//利用的用户名是benwin 则用户名框输入benwin'#  密码有无都可,则$sql变成
$sql ="select * from phpben where user_name=' benwin'#' and pwd ='$pwd'";

这是因为 mysql 中其中的一个注悉是 #,上面语句中 # 已经把后面的内容给注悉掉,所以密码可以不输入或任意输入。网上有些人介绍说用 /* 来注悉,笔者想提的是只有开始注悉没结束注悉 */ 时,mysql 会报错,也不是说 /**/ 不能注悉,而是这里很难添加上 */ 来结束注悉。还有 – 也是可以注悉 mysql,但要注意 – 后至少有一个空格也就是 – ,当然防注入代码要把三种都考虑进来,值得一提的是很多防注入代码中没把 – 考虑进防注入范围。

(3)猜解某用户密码

//正常语句
$sql ="select * from phpben.com where user_name='$username' and pwd ='$pwd'";
//在密码输入框中输入“benwin' and left(pwd,1)='p'#”,则$sql是
$sql ="select * from phpben.com where user_name=' benwin' and left(pwd,1)='p'#' and pwd ='$pwd'";

如果运行正常则密码的密码第一个字符是 p,同理猜解剩下字符。

(4)插入数据时提权

//正常语句,等级为1
$sql = "insert into phpben.com (`user_name`,`pwd`,`level`) values(‘benwin','iampwd',1) ";
//通过修改密码字符串把语句变成
$sql = "insert into phpben.com (`user_name`,`pwd`,`level`) values(‘benwin','iampwd',5)#',1) ";
$sql = "insert into phpben.com (`user_name`,`pwd`,`level`) values(‘benwin','iampwd',5)--  ',1) ";

这样就把一个权限为 1 的用户提权到等级 5。

(5)更新提权和插入提权同理

//正常语句
$sql = "update phpben set  `user_name` ='benwin', level=1";
//通过输入用户名值最终得到的$sql
$sql = "update phpben set  `user_name` ='benwin',level=5#', level=1";
$sql = "update phpben set  `user_name` ='benwin',level=5--  ', level=1";

(6)恶意更新和删除

//正常语句
$sql = "update phpben set `user_name` = ‘benwin' where id =1";
//注入后,恶意代码是“1 or id>0”
$sql = "update phpben set `user_name` = ‘benwin' where id =1 or id>0";
//正常语句
$sql = "update phpben set  `user_name` ='benwin' where id=1";
//注入后
$sql = "update phpben set  `user_name` ='benwin' where id>0#' where id=1";
$sql = "update phpben set  `user_name` ='benwin' where id>0-- ' where id=1";

(7)union、join 等

//正常语句
$sql ="select * from phpben1 where `user_name`='benwin' ";
//注入后
$sql ="select * from phpben1 where`user_name`='benwin' uninon select * from phpben2#' ";
$sql ="select * from phpben1 where`user_name`='benwin' left join……#' ";

(8)通配符号 %、_

//正常语句
$sql ="select * from phpben where `user_name`='benwin' ";
//注入通配符号%匹配多个字符,而一个_匹配一个字符,如__则匹配两个字符
$sql ="select * from phpben where `user_name` like '%b' ";
$sql ="select * from phpben where `user_name` like '_b_' ";

这样只要有一个用户名字是 b 开头的都能正常运行," b" 是匹配三个字符,且这三个字符中间一个字符是 b。这也是为什么有关 addslashes() 函数介绍时提示注意没有转义 % 和 _(其实这个是很多 phper 不知为什么要过滤 % 和 _ 下划线,只是一味的跟着网上代码走)。

(9)还有很多猜测表信息的注入 sql

//正常语句
$sql ="select * from phpben1 where`user_name`='benwin'";
//猜表名,运行正常则说明存在phpben2表
$sql ="select * from phpben1 where`user_name`='benwin' and (select count(*) from phpben2 )>0#' ";
//猜表字段,运行正常则说明phpben2表中有字段colum1
$sql ="select * from phpben1 where`user_name`='benwin' and (select count(colum1) from phpben2 )>0#'";
//猜字段值
$sql ="select * from phpben1 where`user_name`='benwin' and left(pwd,1)='p'#''";

当然还有很多,笔者也没研究到专业人士那种水平,这里提出这些都是比较常见的,也是 phper 应该知道并掌握的,而不是一味的在网上复制粘贴一些防注入代码,知然而不解其然。

下面一些防注入方法回看可能更容易理解。

3.3 防注入的一些方法

3.3.1 php 可用于防注入的一些函数和注意事项

(1)addslashes 和 stripslashes

Addslashes 给这些 '、"、`、NULL 添加斜杆 \'、\"、\、\NULL,stripslashes 则相反。这里要注意的是 php.ini 是否开启了 magicquotesgpc=ON,开启若使用 addslashes 会出现重复。所以使用的时候要先 getmagicquotes_gpc()` 检查。

一般代码类似:

if(!get_magic_quotes_gpc())
{
$abc = addslashes($abc);
}

其实这个稍微学习 php 一下的人都知道了,只不过笔者想系统点介绍(前面都说不是专家级文章),所以也顺便写上了。addslashes

(2)mysqlescapestring() 和 mysqlrealescape_string()

mysql_real_escape_string 必须在(PHP 4 >= 4.3.0, PHP 5)的情况下才能使用。否则只能用 mysql_escape_string。

if (PHP_VERSION >= '4.3')
{
$string  =  mysql_real_escape_string($string);
}else
{
$string  =  mysql_escape_string($string );
}

mysql_escape_string() 和 mysql_real_escape_string() 区别在于后者会判断当前数据库连接字符集,换句话说在没有连接数据库的前提下会出现类似错误:

Warning: mysql_real_escape_string() [function.mysql-real-escape-string]: Access denied for user 'ODBC'@'localhost' (using password: NO) in E:\webphp\test.php on line 11
(3)字符代替函数和匹配函数

str_replace()、perg_replace() 这些函数之所以也在这里提,是因为这些函数可以用于过滤或替代一些敏感、致命的字符。

3.3.2 防注入字符优先级

防注入则要先知道有哪些注入字符或关键字,常见的 mysql 注入字符有:字符界定符号如 '、";逻辑关键字如 and、or;mysql 注悉字符如 #、– 、/**/;mysql 通配符 %、_;mysql 关键字 select|insert|update|delete|*|union|join|into|load_file|outfile。

(1)对于一些有规定格式的参数来说,防注入优先级最高的是空格 " "

如一些银行卡号、身份证号、邮箱、电话号码、生日、邮政编码等,这些有自己规定的格式且格式规定不能有空格符号的参数,在过滤的时候一般最先过滤掉空格(包括一些空格"变种"),因为其他字符界定符号、逻辑关键字、mysql 注悉,注意下图可以看出重要的是 '、" 。

ps:空格字符的变种有:%20、\n、\r、\r\n、\n\r、chr("32″)。这也是为什么 mysql_escape_string() 和 mysql_real_escape_string() 两个函数转义 \n、\r。其实很多 phper 只知道转义 \n,\r 而不知原因,在 mysql 解析 \n,\r 时把它们当成空格处理,笔者测试验证过,这里就不贴代码了。
(2)and、or、`、#、– `

逻辑关键可以组合很多注入代码;mysql 注悉则把固有 sql 代码后面的字符全部给注悉掉,从而让注入后的 sql 语句能正常运行;` 也是能组合很多注入字符 \x00、\x1a`。

ps:sql 解析 #、– 是大多数 mysql 防注入代码没有考虑到的,也是很多 phper 忽略。还有因为一些 phper 给参数赋值的时候会有用 - 来隔开,所以笔者建议不要这样写参数。当然也可以在过滤参数的时候把 – (注意有空格的,没空格不解析为注悉)当一个整体过滤而不是过滤 -,这样就避免过多过滤参数。
(3)null、%、_

这几个不能独立,都要在特定情况下,比如通配字符 %、_ 都要在 mysql like 子句的前提下。所以 %、_ 的过滤一般在搜索相关才过滤,不能把它们纳入通常过滤队列,因为有些如邮箱就可以有 _ 字符。

(4)关键字 select|insert|update|delete|*|union|join|into|load_file|outfile

也许你会问怎么这些重要关键字却优先级这么低。笔者想说的是因为这些关键字在没有 '、"、 、and、or 等情况下构不成伤害。换句话说这些关键字不够"独立","依赖性"特别大。当然优先级低,不代表不要过滤。

3.3.3 防注入代码

(1)参数是数字直接用 intval() 函数

注意:现在很多网上流行的防注入代码都只是用 addslashes()、mysql_escape_string()、mysql_real_escape_string() 或三者任意组合过滤,但 phper 以为过滤了,一不小心一样有漏洞,那就是在参数为数字的时候:

$id = addslashes($_POST['id']); //正确是$id = intval($_POST['id']);
$sql =" select * from phpben.com where id =$id";
$sql =" select * from phpben.com where id =1 or 1=1";

对比容易发现,post 过来的数据通过 addslashes 过滤后的确很多注入已经不起作用,但是 $id 并没有 intval,导致漏洞的存在,这是个小细节,不小心则导致漏洞。

(2)对于非文本参数的过滤

文本参数是指标题、留言、内容等可能有 '、" 等内容,过滤时不可能全部转义或代替。但非文本数据可以。

function _str_replace($str )
{
$str = str_replace(" ","",$str);
$str = str_replace("\n","",$str);
$str = str_replace("\r","",$str);
$str = str_replace("'","",$str);
$str = str_replace('"',"",$str);
$str = str_replace("or","",$str);
$str = str_replace("and","",$str);
$str = str_replace("#","",$str);
$str = str_replace("\","",$str);
$str = str_replace("-- ","",$str);
$str = str_replace("null","",$str);
$str = str_replace("%","",$str);
//$str = str_replace("_","",$str);
$str = str_replace(">","",$str);
$str = str_replace("<","",$str);
$str = str_replace("=","",$str);
$str = str_replace("char","",$str);
$str = str_replace("declare","",$str);
$str = str_replace("select","",$str);
$str = str_replace("create","",$str);
$str = str_replace("delete","",$str);
$str = str_replace("insert","",$str);
$str = str_replace("execute","",$str);
$str = str_replace("update","",$str);
$str = str_replace("count","",$str);
return $str;
}
ps:还有一些从列表页操作过来的一般 href 是 phpben.php?action=delete&id=1,这时候就注意啦,_str_replace($_GET['action']) 会把参数过滤掉,笔者一般不用敏感关键作为参数,比如 delete 会写成 del,update 写成 edite,只要不影响可读性即可;
还有上面代码过滤下划线的笔者注悉掉了,因为有些参数可以使用下划线,自己权衡怎么过滤;
有些代码把关键字当重点过滤对象,其实关键字的 str_replace 很容易"蒙过关",str_replace("ininsertsert") 过滤后的字符还是 insert,所以关键的是其他字符而不是 mysql 关键字。
(3)文本数据防注入代码

文本参数是指标题、留言、内容等,这些数据不可能也用 str_replace() 过滤掉,这样就导致数据的完整性,这是很不可取的。

代码:

function no_inject($str)
{
if(is_array($str))
{
foreach($str as $key =>$val)
{
$str[$key]=no_inject($val);
}
}else
{
$str = str_replace(" "," ",$str);
$str = str_replace("\","\",$str);
$str = str_replace("'","'",$str);
$str = str_replace('"',"",$str);
$str = str_replace("or","or",$str);
$str = str_replace("and","and",$str);
$str = str_replace("#","#",$str);
$str = str_replace("-- ","-- ",$str);
$str = str_replace("null","null",$str);
$str = str_replace("%","%",$str);
//$str = str_replace("_","",$str);
$str = str_replace(">",">",$str);
$str = str_replace("<","<",$str);
$str = str_replace("=","=",$str);
$str = str_replace("char","char",$str);
$str = str_replace("declare","declare",$str);
$str = str_replace("select","select",$str);
$str = str_replace("create","create",$str);
$str = str_replace("delete","delete",$str);
$str = str_replace("insert","insert",$str);
$str = str_replace("execute","execute",$str);
$str = str_replace("update","update",$str);
$str = str_replace("count","count",$str);
}
return $str;
}
(4)当然还有其他与 addslashes、mysqlescapestring 结合的代码

防注入的代码其实来来去去都是那些组合,然后根据自己程序代码变通。笔者这些代码也是没考虑全的,不如 cookies、session、request 都没全过滤。重要是知道其中原理,为什么过滤这些字符,字符有什么危害。

四、防止 xss 攻击

XSS:cross site script 跨站脚本,为什么不叫 css,为了不和 div+css 混淆。

4.1 Xss 攻击过程

  1. 发现 A 站有 xss 漏洞。
  2. 注入 xss 漏洞代码。可以 js 代码、木马、脚本文件等等,这里假如 A 站的 benwin.php 这个文件有漏洞。
  3. 通过一些方法欺骗 A 站相关人员运行 benwin.php,其中利用相关人员一些会员信息如 cookies、权限等。

相关人员:

方法:

  1. 在 A 站发诱骗相关人到 benwin.php 的信息,比如网址,这种是本地诱骗。
  2. 在其他网站发诱骗信息或者发邮件等等信息。

一般通过伪装网址骗取 A 站相关人员点击进 benwin.php。

  1. 第三步一般已经是一次 xss 攻击,如果要更进一步攻击,那不断重复执行(2)、(3)步以达到目的。

简单例说 xss 攻击

代码:benwin.php 文件

<html>
<head>
<title>简单xss攻击例子</title></head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<dody>
<form action="phpben.com?user_name=<?php echo $user_name; ?>">
<input type="submit" value="提交" >
</form>
</body>
</html>

当用户名 $user_name 的值是 benwin" onSubmit="alert('这是xss攻击的例子');" class= ""(这里)

<form action="phpben.com?user_name=benwin" onSubmit="alert('这是xss攻击的例子');" class= "" >
<input type="submit" value="提交" >
</form>

当提交表单的时候就会弹出提示框。

  1. 很明显 $user_name 在保存进数据库的时候没有过滤 xss 字符(和防注入很像,这里举例说明)==> 发现漏洞
  2. 构造 xss 代码:benwin" onSubmit="alert('这是xss攻击的例子');" class= "" 传入数据库
  3. 骗相关人员进来点击"提交"按钮

4.2 常见 xss 攻击地方

(1)Js 地方

<script language="javascript">
var testname =" <?php echo $testname;?>";
</script>

$testname 的值只要符合 js 闭合关系:";alert("test xss ");"(以下同理)

(2)form 表单里面

<input type="text" name="##" value="<?php echo $val; ?>" />

(3)a 标签

<a href="benwin.php?id= <?php echo $id; ?>">a标签可以隐藏xss攻击</a>

(4)用得很多的 img 标签

<img src="<?php echo $picPath; ?>" />

甚至一些文本中插入整个 img 标签并且用 width、height、css 等隐藏的很隐蔽。

(5)地址栏

总之,有输出数据的地方,更准确的说是有输出用户提交的数据的地方,都有可能是 XSS 攻击的地方。

4.3 防 XSS 方法

防 xss 方法其实和防注入很相似,都是一些过滤、代替、实体化等方法。

  1. 过滤或移除特殊的 Html 标签。 例如:<、>、'、"、<script>、<iframe> 等。
  2. 过滤触发 JavaScript 事件的标签。 例如 onload、onclick、onfocus、onblur、onmouseover 等等。
  3. php 一些相关函数,strip_tags()、htmlspecialchars()、htmlentities() 等函数可以起作用。

五、CSRF

CSRF 跨站请求伪造:cross site request forgery。

5.1 简单说明 CSRF 原理

  1. A 登录 Site1(如现在网民常上的淘宝、微博、QQ 等),产生一些信息,session、cookies 等等,且一直保持没退出。
  2. A 再登录 Site2(如一些成人网等,至于怎么跑到 Site2,多数是 Site 通过些手段,邮件欺骗等),打开 site2 的浏览器和打开 site1 的一样,否则无效。
  3. Site2 站中伪造了 Site1 的 http 请求(如修改密码、买东西、转账等),Site1 的服务器误以为 A 在 site1 的正常操作(因为同浏览器且 A 还没登出),然后就运行了请求,那么 csrf 已成功操作。

csrf 和 xss 很相似。xss 也能伪造请求,csrf 也能制造脚本。

伪造的请求可以很多方面,发邮件、改密码、返回用户信息、交易等等,所以相对于 xss 攻击来说 csrf 危害更严重。

5.2 防范方法

对于 phper:

(1)严密操控执行入口

执行一些敏感操作比如改密码这些操作前判断请求来源,只有本站服务器发的请求才可以执行。判断方法可以判断 ip 来源。非本站服务器 ip 不会执行。

(2)本站有外链的话做些必要操作

一般 site2 的 hacker 会在 site1(比如论坛里)里发欺骗连接,因为在 site1 诱骗的相关人员一般都登录 site1 了,满足 csrf 条件之一。

如当你点击 QQ 邮件里面的长外链时候,回跳转到一个页面提示"有风险"之类,这样不仅可以减低跳出率,一些不懂的人看到这样的提示,若不是非必要而是处于好奇点击的连接一般不会继续点击访问;还有是 QQ 邮件正文里的图片在加载内容时是不加载图片的,要点击"显示图片"按钮才显示图片,这里一个原因之一就是避免攻击。

当然对于用户体验来说这是不可取的,可以优化的是判断到一些网址(如 QQ 本身网址)是安全直接可以显示(不用提示),而可疑的才提示或禁止。

(3)防止 csrf 也可以用防 xss 的方法

六、防盗链

盗链问题增加服务器的负担。盗链就是盗链网站盗取被盗链网站资源来实现一些功能。盗链方面主要是图片、视频、以及其他资源下载文件。

方法:判断 ip,只有本站服务器才能使用站点资源,否则不能使用。

代码:

(1)在 Apache htaccess 添加

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$ [NC]
RewriteCond %{HTTP_REFERER} !phpben.com [NC]
RewriteCond %{HTTP_REFERER} !google.com [NC]
RewriteCond %{HTTP_REFERER} !baidu.com [NC]
RewriteCond %{HTTP_REFERER} !zhuaxia.com [NC]
RewriteRule .(jpg|gif|png|bmp|swf|jpeg) /image/replace.gif [R,NC,L]
RewriteRule ^(.*)$ http:\/\/phpben.com\/image\/$1 [L]

这样,凡是不是 phpben.com、google.com、baidu.com、zhuaxia.com 域名请求的都返回 replace.gif 代替返回。

七、防 CC 攻击

CC 攻击:是利用不断对网站发送连接请求致使形成拒绝服务的目的。

详细百度百科:<http://baike.baidu.com/view/662394.htm>

代码:

session_start();
$ll_nowtime = $timestamp ;
if (session_is_registered('ll_lasttime')){
$ll_lasttime = $_SESSION['ll_lasttime'];
$ll_times = $_SESSION['ll_times'] + 1;
$_SESSION['ll_times'] = $ll_times;
}else{
$ll_lasttime = $ll_nowtime;
$ll_times = 1;
$_SESSION['ll_times'] = $ll_times;
$_SESSION['ll_lasttime'] = $ll_lasttime;
}
if (($ll_nowtime - $ll_lasttime)<3){
if ($ll_times>=5){
header(sprintf("Location: %s",'http://127.0.0.1'));
exit;
}
}else{
$ll_times = 0;
$_SESSION['ll_lasttime'] = $ll_nowtime;
$_SESSION['ll_times'] = $ll_times;
}

附:整理说明

  1. 来源:本文整理自博客园文章(2017-07-29),原文正文整体包裹在一个带行号的 <pre> 代码块中,本身无 HTML 标题层级。此处按内容语义重建了标题树(# 篇名 → ## 一级节点 → ### 子节点 → #### 条目),并将原文"复制代码 代码如下:"的引导语统一还原为代码围栏。
  2. 代码语言标注:按上下文推断标注(ini / php / sql / html / apache / text),代码内容未做改动。
  3. 原文笔误保留:为确保内容可核对,保留了原文用词,如"注悉"(应为"注释")、"uninon"、"dody"、"perg_replace"、"却别"、"购不成伤害"、"ccokies/cookes"等。
  4. 目录编号:原文目录中"33防注入的一些方法"与正文"3.3"不一致,已统一为 3.3;目录层级中的缩进错位也已修正。
  5. 未尽事项:原文正文示例中的引号嵌套(如 str_replace("\","\",$str))在源站渲染时即已变形,此处按源站显示内容原样保留。