使用Mysqli和PDO主要是因为有些数据没有经过严格校验,然后直接拼接SQL查询。导致漏洞,如:$id=$_GET['id'];$sql="SELECTnameFROMusersWHEREid=$id";因为$_GET['id']没有进行数据类型验证,所以注入器可以提交任何类型的数据,比如"and1=1or"等不安全数据。如果按照下面的方式写,会比较安全。$id=intval($_GET['id']);$sql="SELECTnameFROMusersWHEREid=$id";将id转换为int类型以去除不安全的东西。验证数据防止注入的第一步是验证数据,可以严格按照对应的类型进行验证。比如int类型可以直接用intval进行转换:$id=intval($_GET['id']);字符处理比较复杂,先用sprintf函数格式化单词输出,保证是字符串。然后使用一些安全函数去除一些非法字符,如:$str=addslashes(sprintf("%s",$str));//也可以使用mysqli_real_escape_string 函数代替addslashes,这样会加工后更安全。当然还可以进一步判断字符串的长度,防止“缓冲区溢出攻击”如:$str=addslashes(sprintf("%s",$str)); $str=substr($str,0,40);//最大长度为40Parameterizedbinding参数化绑定是防止SQL注入的另一道屏障。phpMySQLi和PDO都提供了这样的功能。例如,MySQLi可以这样查询:$mysqli=newmysqli('localhost','my_user','my_password','world');$stmt=$mysqli->prepare("INSERTINTOCountryLanguageVALUES(?,?,?,?)");$code='DEU';$language='Bavarian';$official="F";$percent=11.2;$stmt->bind_param('sssd',$code,$language,$official,$percent);PDO更方便,例如:/*通过传递值数组执行准备语句*/$sql='SELECTname,colour,caloriesFROMfruitWHEREcalories<:caloriesANDcolor=:colour';$sth=$dbh->prepare($sql,array(PDO::ATTR_CURSOR=>PDO::CURSOR_FWDONLY));$sth->execute(array(':calories'=>150,':colour'=>'红色'));$red=$sth->fetchAll();$sth->execute(array(':calories'=>175,':colour'=>'yellow'));$yellow=$sth->获取全部();我们编程大多使用php框架,所以最好不要自己拼sql,根据框架给定的参数绑定查询。遇到比较复杂的SQL语句,一定要自己拼写,一定要注意严格判断。也可以不使用PDO或者MySQLi写一个准备好的,比如wordprssdb查询语句。可见其也经过了严格的类型验证。functionprepare($query,$args){if(is_null($query))return;//这并不意味着万无一失——但它会发现明显不正确的用法。if(strpos($query,'%')===false){_doing_it_wrong('wpdb::prepare',sprintf(__('%s的查询参数必须有一个占位符。'),'wpdb::prepare()'),'3.9');}$args=func_get_args();array_shift($args);//如果args作为数组传递(如在vsprintf中),将它们向上移动if(isset($args[0])&&is_array($args[0]))$args=$args[0];$query=str_replace("'%s'",'%s',$query);//以防有人错误地将它单引号$query=str_replace('"%s"','%s',$query);//双引号取消引号$query=preg_replace('|(?=4.3.0,PHP5)的情况下使用。否则,只能使用mysql_escape_string。两者的区别在于:mysql_real_escape_string考虑了连接的当前字符集,而mysql_escape_string则没有。总结一下:addslashes()是强加的;mysql_real_escape_string()会判断字符集,但对PHP版本有要求;mysql_escape_string不考虑连接的当前字符集。服务器配置方面(1)开启php的安全模式。php的安全模式是一个非常重要的内置安全机制,它可以控制php中的一些函数,比如system()。同时控制着很多文件操作函数的权限。一些关键文件也是不允许的,比如/etc/passwd,但是默认的php.ini是没有开启安全模式的,我们开启:safe_mode=on(2)用户组安全当safe_mode开启时,safe_mode_gid关闭,那么php脚本就可以访问该文件,同组的用户也可以访问该文件。推荐设置为:safe_mode_gid=off如果不设置,我们可能无法操作我们服务器网站目录下的文件,比如我们需要操作文件的时候。(3)在安全模式下执行程序的主目录如果安全模式打开了,但是想执行一些程序,可以指定要执行的程序的主目录:safe_mode_exec_dir=D:/usr/bin一般不需要执行任何程序,所以建议不要执行系统程序目录,可以指向一个目录,然后复制要执行的程序,例如:safe_mode_exec_dir=D:/tmp/cmd但是,我建议不要执行任何程序,那么可以指向我们的web目录:safe_mode_exec_dir=D:/usr/www(4)在安全模式下包含文件如果你想在安全模式下包含一些公共文件,那么修改选项:safe_mode_include_dir=D:/usr/www/include/其实一般的php脚本包含的文件都是程序自己写的,这个可以根据具体需要设置。(5)控制php脚本可以访问的目录使用open_basedir选项控制PHP脚本只能访问指定的目录,可以防止PHP脚本访问不该访问的文件,一定程度上限制phpshell的危害。我们一般可以设置为只能访问网站目录:open_basedir=D:/usr/www(6)关闭危险功能如果开启了安全模式,那么功能ban就不用了,不过我们还是考虑安全。比如我们觉得不想执行包括system()在内的可以执行命令的php函数,或者phpinfo()等可以查看php信息的函数,那么我们就可以禁止它们:disable_functions=system,passthru,exec,shell_exec,popen,phpinfo如果要禁止任何文件和目录操作,可以关闭很多文件操作,rmdir,rename,file,file_get_contents,fputs,fwrite,chgrp,chmod,chown只是上面列出的一些常用的文件处理函数。也可以把上面的执行命令函数和这个函数结合起来,抵御大部分的phpshell。(7)关闭http头中PHP版本信息的泄露为了防止黑客获取服务器中的php版本信息,可以关闭http头中的信息斜坡:expose_php=Off例如,当黑客telnetwww.12345.com80,那么就看不到PHP信息了。(8)关闭全局变量的注册在PHP中提交的变量,包括使用POST或GET提交的变量,都会自动注册为全局变量,可以直接访问。这对服务器来说是很不安全的,所以我们不能让它注册如果是全局变量,关闭注册全局变量选项:register_globals=Off当然,如果设置了这个,那么在获取的时候一定要用合理的方法对应的变量,比如获取GET提交的变量var,则必须使用$_GET['var']获取,这点PHP程序员要注意。(9)开启magic_quotes_gpc防止SQL注入。SQL注入是一个非常危险的问题。轻则网站后台被入侵,重则整个服务器宕机,需谨慎。php.ini中有个设置:magic_quotes_gpc=Off,默认是关闭的。如果开启,它会自动将用户提交的查询转换为sql,如将'转换为'等,有助于防止sql注入的主要作用。所以我们推荐设置为:magic_quotes_gpc=On(10)错误信息控制一般php在没有连接数据库或者其他情况下都会提示错误。一般报错信息会包含php脚本当前路径信息或查询SQL语句等信息,此类信息提供给黑客后是不安全的,所以一般服务器建议关闭报错提示:display_errors=Off如果要显示错误信息,必须设置错误显示的级别,比如只显示警告上面的信息:error_reporting=E_WARNING&E_ERROR当然,我还是建议关闭错误提示。(11)错误日志,建议关闭display_errors后记录错误信息,以便查找服务器运行的原因:log_errors=On同时设置错误日志存放目录。建议根apache日志一起存在:error_log=D:/usr/local/apache2/logs/php_error.log注意:该文件必须允许apache用户和组有写权限。新建用户如mysqlstartnetusermysqlstartfuckmicrosoft/addnetlocalgroupusersmysqlstart/del不属于任何组。如果MYSQL安装在d:mysql,那么给mysqlstart完全控制权限,在系统服务中设置。MYSQL服务属性,在登录属性中,选择用户mysqlstart并输入密码,确认。重启MYSQL服务,MYSQL就会以低权限运行。如果Apache是??在Windows平台下构建的,我们就需要注意了。Apache默认以系统权限运行,这很可怕,让人感觉很不舒服。然后让我们降低Apache的权限。netuserapachefuckmicrosoft/addnetlocalgroupusersapache/delok.我们创建了一个不属于任何组的用户apche。我们打开计算机管理器,选择服务,点击apache服务的属性,我们选择登录,选择这个账户,我们填写上面创建的账户和密码,重启apache服务,ok,apache在低权限下运行.其实我们还可以设置各个文件夹的权限,让apache用户只能执行我们希望它能够执行的操作,为每个目录单独创建一个可以读写的用户。这也是很多虚拟主机提供商流行的配置方式,不过为了防止有点矫枉过正,就用这种方式。
