PHP开发中常见的安全问题详解和解决方法(如Sql注入、CSRF、Xss、CC等)
浅谈Php安全和防Sql注入,防止Xss攻击,防盗链,防CSRF 前言:首先,笔者不是web安全的专家,所以这不是web安全方面专家级文章,而是学习笔记、细心总结文章,里面有些是我们phper不易发现或者说不重视的东西。所以笔者写下来方便以后查阅。在大公司肯定有专门的web安全测试员,安全方面不是phper考虑的范围。但是作为一个phper对于安全知识是:“知道有这么一回事,编程时自然有所注意”。 目录:1、php一些安全配置(1)关闭php提示错误功能 (2)关闭一些“坏功能” (3)严格配置文件权限。 2、严格的数据验证,你的用户不全是“好”人 2.1为了确保程序的安全性,健壮性,数据验证应该包括内容。 2.2程序员容易漏掉point或者说需要注意的事项 3、防注入 3.1简单判断是否有注入漏洞以及原理 3.2常见的mysql注入语句 (1)不用用户名和密码 (2)在不输入密码的情况下,利用某用户 (3)猜解某用户密码 (4)插入数据时提权 (5)更新提权和插入提权同理 (6)恶意更新和删除 (7)union、join等 (8)通配符号%、_ (9)还有很多猜测表信息的注入sql 33防注入的一些方法 2.3.1 php可用于防注入的一些函数和注意事项。 2.3.2防注入字符优先级。 2.3.3防注入代码 (1)参数是数字直接用intval()函数 (2)对于非文本参数的过滤 (3)文本数据防注入代码。 (4)当然还有其他与addslashes、mysql_escape_string结合的代码。 4、防止xss攻击 4.1Xss攻击过程 4.2常见xss攻击地方 4.3防XSS方法 5、CSRF 5.1简单说明CSRF原理 5.2防范方法 6、防盗链 7、防拒CC攻击 1、php一些安全配置(1)关闭php提示错误功能在php.ini 中把display_errors改成 1)使用error_reporting(0);失败的例子: A文件代码: 2)使用error_reporting(0);成功的例子: a文件代码: 代码如下:error_reporting(0); include("b.php"); ?> b文件代码: 代码如下: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 2)关闭register_globals = Off 在php.ini 把register_globals = OFF 在register_globals = ON的情况下 地址栏目://www.jb51.cc?bloger=benwin 这种情况下会导致一些未初始化的变量很容易被修改,这也许是致命的。所以把register_globals = OFF关掉 (3)严格配置文件权限。 为相应文件夹分配权限,比如包含上传图片的文件不能有执行权限,只能读取 2、严格的数据验证,你的用户不全是“好”人。记得笔者和一个朋友在讨论数据验证的时候,他说了一句话:你不要把你用户个个都想得那么坏!但笔者想说的这个问题不该出现在我们开发情景中,我们要做的是严格验证控制数据流,哪怕10000万用户中有一个是坏用户也足以致命,再说好的用户也有时在数据input框无意输入中文的时,他已经不经意变“坏”了。 2.1为了确保程序的安全性,健壮性,数据验证应该包括(1) 关键数据是否存在。如删除数据id是否存在 数据验证有些人主张是把功能完成后再慢慢去写安全验证,也有些是边开发边写验证。笔者偏向后者,这两种笔者都试过,然后发现后者写的验证相对健壮些,主要原因是刚开发时想到的安全问题比较齐全,等开发完功能再写时有两个问题,一个phper急于完成指标草草完事,二是确实漏掉某些point。 (1) 进库数据一定要安全验证,笔者在广州某家公司参与一个公司内部系统开发的时候,见过直接把$_POST数据传给类函数classFunctionName($_POST),理由竟然是公司内部使用的,不用那么严格。暂且不说逻辑操作与数据操控耦合高低问题,连判断都没判断的操作是致命的。安全验证必须,没任何理由推脱。 3、防注入3.1简单判断是否有注入漏洞以及原理。 网址://www.jb51.cc/benwin.php?id=1 运行正常,sql语句如:select * from phpben where id = 1 (1) 网址://www.jb51.cc/benwin.php?id=1' sql语句如:select * from phpben where id = 1' 然后运行异常 这能说明benwin.php文件没有对id的值进行“'” 过滤和intval()整形转换,当然想知道有没有对其他字符如“%”,“/*”等都可以用类似的方法穷举测试(很多测试软件使用) 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`,5)#',1) "; $sql = "insert into phpben.com (`user_name`,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是否开启了magic_quotes_gpc=ON,开启若使用addslashes会出现重复。所以使用的时候要先get_magic_quotes_gpc()检查 一般代码类似: (2)mysql_escape_string()和mysql_ real _escape_string()mysql_real_escape_string 必须在(PHP 4 >= 4.3.0,PHP 5)的情况下才能使用。否则只能用 mysql_escape_string (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”,“rn”,“nr”,“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以为过滤了,一不小心一样有漏洞,那就是在参数为数字的时候: (2)对于非文本参数的过滤文本参数是指标题、留言、内容等可能有“'”,“'”等内容,过滤时不可能全部转义或代替。 但非文本数据可以。 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()过滤掉,这样就导致数据的完整性,这是很不可取的。 代码: (4)当然还有其他与addslashes、mysql_escape_string结合的代码。防注入的代码其实来来去去都是那些组合,然后根据自己程序代码变通,笔者这些代码也是没考虑全的,不如cookes、session、request都没全过滤。重要是知道其中原理,为什么过滤这些字符,字符有什么危害。 4、防止xss攻击XSS:cross site script 跨站脚本,为什么不叫css,为了不和div+css混淆。 4.1Xss攻击过程:(1)发现A站有xss漏洞。 (2)注入xss漏洞代码。可以js代码,木马,脚本文件等等,这里假如A站的benwin.php这个文件有漏洞。 (3)通过一些方法欺骗A站相关人员运行benwin.php,其中利用相关人员一些会员信息如cookies,权限等。 相关人员: 管理员(如贴吧版主),管理员一般有一定权限。目的是借用管理员的权限或进行提权,添或加管理员,或添加后门,或上传木马,或进一步渗透等相关操作。 A站会员:会员运行A站的benwin.php。目的一般是偷取会员在A站的信息资料。 方法: 1) 在A站发诱骗相关人到benwin.php的信息,比如网址,这种是本地诱骗 2) 在其他网站发诱骗信息或者发邮件等等信息。 一般通过伪装网址骗取A站相关人员点击进benwin.php (4)第三步一般已经是一次xss攻击,如果要更进一步攻击,那不断重复执行(2)、(3)步以达到目的。 简单例说xss攻击 代码:benwin.php文件 |