• 技术文章 >后端开发 >php教程

    php安全问题思考

    藏色散人藏色散人2019-09-12 17:56:14转载1686
    用户提交过来的数据都是不可信的,所以,在查库或入库前需要对提交过来的数据进行过滤或字符的转换处理,以防止SQL注入或xss攻击等问题。

    推荐:【PHP教程

    一、防止SQL注入

    什么是SQL注入攻击?

    所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

    寻找SQL注入的方法:

    1.通过get请求

    2.通过post请求

    3.其他http请求,如cookie

    常见的SQL注入问题:

    数据库查询参数的类型转换处理

    1. 转义字符处理不当

    Talk is cheap,Show me the code.

    多说无益,代码亮出来吧!

    // 构造动态SQL语句
      $sql = "select * from tbl where field = '$_GET['input']'";
      // 执行SQL语句
      $res = mysql_query($sql);

    测试:

    在下边的网址后边加一个单引号,就会报数据库错误

    http://testphp.vulnweb.com/ar...

    f8306d0f95b46d4dbd10f1cdb1adb27.png

    php入门到就业线上直播课:进入学习

    2. 类型处理不当

    // 构造动态SQL语句
    $sql = "select * from tbl where field = $_GET['user_id']";
    // 执行SQL语句
     $res = mysql_query($sql);

    Mysql内置了一个命令,可以读取文件

    Union all select load_file('/etc/passwd')--
    select * from tbl where userid = 1 union all select load_file('etc/passwd')--

    该命令会获取数据库管理员的密码。

    处理方法:

    需要将客户端传过来的数据进行类型强制转换,而后再查询

    $user_id = (int)$_GET['user_id'];
    "select * from tbl where userid = {$user_id}";

    3. 查询语句组织不当

    user.php?table=user&

    4. 错误处理不当

    即将站点的错误信息暴漏给用户,这样非常危险。

    // 构造动态查询语句
    $getid = "select * from tbl where userid > 1";
    // 执行SQL语句
    $res = mysql_query($getid) or die('<pre>'.mysql_error().'</pre>');
    5. 多个提交处理不当
    // 参数是否是一个字符串
    if(is_string($_GET["param"])){}

    数据入库时将转换单引号、双引号、反斜杠为实体

    在入库的时候如果不过滤 ' ""这样的东西,这样会使数据库报错,或者注入等问题。

    先将字符串用htmlspecialchars()转换为实体后存储到数据库,然后从数据库读出来时htmlspecialchars_decode()转为HTML标签。

    htmlspecialchars() 函数把一些预定义的字符转换为 HTML 实体。

    函数原型:

    htmlspecialchars(string,quotestyle,character-set)

    预定义的字符是:

    & (和号)    成为 &amp;
    ” (双引号)  成为 &quot;
    ‘ (单引号)  成为 &#039;
    < (小于)    成为 &lt;
    > (大于)    成为 &gt;

    htmlspecialchars_decode() 函数把一些预定义的 HTML 实体转换为字符(和htmlspecialchars相反)。

    函数原型:

    htmlspecialchars_decode(string,quotestyle)

    二、防止xss攻击

    什么是xss攻击?

    和上边的sql注入不同的是,xss攻击是合法的字符串,如经过htmlspecialchars()方法实体化后,可以保存在数据库中,但是,当访问含有该字符串的内容页面时,就会出现问题,如字符串里边还有JavaScript,frame代码,原来的页面就会被篡改。

    比如你写个留言本,有人去留言写<script src="xx"></script><iframe>,这个被显出来容易挂病毒都很容易,和数据库无关。

    XSS概念

    XSS又称CSS,全称Cross SiteScript(跨站脚本攻击), XSS攻击类似于SQL注入攻击,是Web程序中常见的漏洞,XSS属于被动式且用于客户端的攻击方式,所以容易被忽略其危害性。其原理是攻击者向有XSS漏洞的网站中输入(传入)恶意的HTML代码,当用户浏览该网站时,这段HTML代码会自动执行,从而达到攻击的目的。如,盗取用户Cookie信息、破坏页面结构、重定向到其它网站等。

    理论上,只要存在能提供输入的表单并且没做安全过滤或过滤不彻底,都有可能存在XSS漏洞。

    下面是一些最简单并且比较常见的恶意字符XSS输入:

    1.XSS 输入通常包含 JavaScript 脚本,如弹出恶意警告框:<script>alert("XSS");</script>

    2.XSS 输入也可能是 HTML 代码段,譬如:

    (1).网页不停地刷新 <meta http-equiv="refresh" content="0;">

    (2).嵌入其它网站的链接 <iframe src=http://xxxx width=250 height=250></iframe>

    除了通过正常途径输入XSS攻击字符外,还可以绕过JavaScript校验,通过修改请求达到XSS攻击的目的,如下图:

    了解到XSS攻击的原理和危害后,其实要预防也不难,下面提供一个简单的PHP防止XSS攻击的函数:

    除了通过正常途径输入XSS攻击字符外,还可以绕过JavaScript校验,通过修改请求达到XSS攻击的目的。

    了解到XSS攻击的原理和危害后,其实要预防也不难,下面提供一个简单的PHP防止XSS攻击的函数:

    <?PHP
    /**
     * @blog http://www.digtime.cn
     * @param $string
     * @param $low 安全别级低
     */
    function clean_xss(&$string, $low = False)
    {
        if (! is_array ( $string ))
        {
            $string = trim ( $string );
            $string = strip_tags ( $string );
            $string = htmlspecialchars ( $string );
            if ($low)
            {
                return True;
            }
            $string = str_replace ( array ('"', "\\", "'", "/", "..", "../", "./", "//" ), '', $string );
            $no = '/%0[0-8bcef]/';
            $string = preg_replace ( $no, '', $string );
            $no = '/%1[0-9a-f]/';
            $string = preg_replace ( $no, '', $string );
            $no = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';
            $string = preg_replace ( $no, '', $string );
            return True;
        }
        $keys = array_keys ( $string );
        foreach ( $keys as $key )
        {
            clean_xss ( $string [$key] );
        }
    }
    //just a test
    $str = 'codetc.com<meta http-equiv="refresh" content="0;">';
    clean_xss($str); //如果你把这个注释掉,你就知道xss攻击的厉害了
    echo $str;
    ?>

    避免被XSS:

    1.给用户开放的编辑器尽量过滤掉危险的代码

    如果是html编辑器,一般的做法是保留大部分代码,过滤部分可能存在危险的代码,如script, iframe等等

    三、PHP MySQL 预处理语句

    预处理语句对于防止 MySQL 注入是非常有用的。

    预处理语句及绑定参数

    预处理语句用于执行多个相同的 SQL 语句,并且执行效率更高。

    预处理语句的工作原理如下:

    预处理:创建 SQL 语句模板并发送到数据库。预留的值使用参数 "?" 标记 。例如:

    INSERT 
        INTO MyGuests (firstname, lastname, email) VALUES(?, ?, ?)

    数据库解析,编译,对SQL语句模板执行查询优化,并存储结果不输出。

    执行:最后,将应用绑定的值传递给参数("?" 标记),数据库执行语句。应用可以多次执行语句,如果参数的值不一样。

    相比于直接执行SQL语句,预处理语句有两个主要优点:

    预处理语句大大减少了分析时间,只做了一次查询(虽然语句多次执行)。

    绑定参数减少了服务器带宽,你只需要发送查询的参数,而不是整个语句。

    预处理语句针对SQL注入是非常有用的,因为参数值发送后使用不同的协议,保证了数据的合法性。

    PDO预处理机制

    可以使用多种方式实现预处理:指的是在绑定数据进行执行的时候,可以有多种方式。

    预处理语句中为变量

    使用数组指定预处理变量

      1、准备预处理语句(发送给服务器,让服务器准备预处理语句)

    PDOStatement PDO::prepare:类似exec将一条SQL语句发送给Mysql服务器  
        //PDO::prepare 能够自动的准备一个预处理语句,用户需要准备的只是预处理所要执行的语句
        //需求:往学生表里循环插入10条记录
        //PDO的预处理能够自动的将对应的以:开始的变量给记录下来,实际发送给服务器的是“?”
        $sql1 = "insert into pro_student values(null,:s_name,:s_num,:s_gender,:s_age,:c_id)";

      2、发送预处理语句

    $stmt = $pdo->prepare($sql1);

      3、给预处理绑定数据

    $arr = array(
          ':s_name' => '房祖名',
          ':s_num' => 'itcast0013',
          ':s_gender' => 0,
          ':s_age' => 28,
          ':c_id' => 2
        );

      4、执行预处理:将要操作的数据发送给预处理语句,再执行预处理语句

    PDOStatement::execute([$array]):数组用来传递对应的参数
        $stmt->execute($arr); //执行预处理

    PDO预处理原理

    PDO的预防sql注入的机制也是类似于使用mysql_real_escape_string 进行转义,PDO 有两种转义的机制,第一种是本地转义,这种转义的方式是使用单字节字符集(PHP < 5.3.6)来转义的(单字节与多字节),来对输入进行转义,但是这种转义方式有一些隐患。隐患主要是:在PHP版本小于5.3.6的时候,本地转义只能转换单字节的字符集,大于 5.3.6 的版本会根据 PDO 连接中指定的 charset 来转义。

    第二种方式是PDO,首先将 sql 语句模板发送给Mysql Server,随后将绑定的字符变量再发送给Mysql server,这里的转义是在Mysql Server做的,它是根据你在连接PDO的时候,在charset里指定的编码格式来转换的。这样的转义方式更健全,同时还可以在又多次重复查询的业务场景下,通过复用模板,来提高程序的性能。如果要设置Mysql Server 来转义的话,就要首先执行:

    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

    原始链接方法:

    <?php  
    // PDO的使用
    // http://blog.csdn.net/qq635785620/article/details/11284591
    $dbh = new PDO('mysql:host=127.0.0.1:3306;dbname=mysql_safe', 'root', '518666'); 
     
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);    
    $dbh->exec('set names utf8');   
    $title    = "我们的爱情";
    $content  = '你是/谁啊,大几\都"老梁"做做&>women<a>没' . " 测试打印号'我是单引号'哈哈";
    $user_id  = 174742;
    $add_time = date("Y-m-d H:i:s");
    $insert_sql = "insert into post_tbl (title, content, user_id, add_time) values (:x_title, :x_content, :x_user_id, :x_add_time)";
    $stmt = $dbh->prepare($insert_sql); 
    $stmt->execute(array('x_title'=>$title,':x_content'=> $content, ':x_user_id' => $user_id, ':x_add_time' => $add_time));    
    echo $dbh->lastinsertid();


    f3f6719c36c752ee64951e0bf6ef1fe.png

    可见这次PHP是将SQL模板和变量是分两次发送给MySQL的,由MySQL完成变量的转义处理,既然变量和SQL模板是分两次发送的,那么就不存在SQL注入的问题了,但需要在DSN中指定charset属性,如:

    $pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root');

    示例:

    /**
    *  插入用户的Token
    */
    function photo_save_token($user_id, $token)
    {
        // 参数判断
        $user_id = (int)$user_id;
        $token = trim($token);
      
        if(empty($token) || empty($user_id)) return false;
        $sql = "replace into token_db.app_token_tbl
    ( user_id, token, t_date, last_open_time)
    values
    ( :x_user_id, :x_token, :x_t_date, :x_last_open_time)";
        sqlSetParam($sql,"x_user_id",$user_id);
        sqlSetParam($sql,"x_token",$token);
        sqlSetParam($sql,"x_t_date",date('Y-m-d H:i:s'));
        sqlSetParam($sql,"x_last_open_time", time());
        return mysql_query($sql);
    }

    总结: 当调用 prepare() 时,查询语句已经发送给了数据库服务器,此时只有占位符 ? 发送过去,没有用户提交的数据;当调用到 execute()时,用户提交过来的值才会传送给数据库,他们是分开传送的,两者独立的,SQL攻击者没有一点机会。

    小结

    ①、关于sql注入可以使用htmlspecialchars()或addslashes()方法,如果连接mysql,可以用mysql_real_escape_string(),还有在php.ini中配置magic_quotes_gpc开启自动转义的扩展。

    PHP环境打开自动转义,PHP.INI中查看

    当magic_quotes_gpc=on 时将自动进行转义(默认是on),可在程序中用get_magic_quotes_gpc()检查他的状态

    程序为:

    if (get_magic_quotes_gpc()==1){
         $name=stripcslashes($_POST["name"]);       
    }else{
         $name=$_POST["name"];
    }

    ②、关于xss攻击可以写一个去处script,frame等代码的方法:

    直接用这个函数editor_safe_replace代替htmlspecialchars,既保证安全又能用大部分html代码

    function editor_safe_replace($content)
    {
       $content = trim($content);
        $tags = array(
            "'<iframe[^>]*?>.*?</iframe>'is",
            "'<frame[^>]*?>.*?</frame>'is",
            "'<script[^>]*?>.*?</script>'is",
            "'<head[^>]*?>.*?</head>'is",
            "'<title[^>]*?>.*?</title>'is",
            "'<meta[^>]*?>'is",
            "'<link[^>]*?>'is",
        );
       
        
        // 1.先过滤掉含有xss攻击的代码
        $content = preg_replace($tags, "", $content);
        
         // strip_tags过滤掉全部HTML标签(script,iframe,head,a 等标签)和上边的正则方法类似
         // $content = strip_tags($content);
        
        // 2.入库时,防止sql注入,转为HTML实体保存在数据库,单双引号都转
         $content = htmlspecialchars($content, ENT_QUOTES);
         
        // 3.替换反斜杠
        $content = preg_replace("/\\\/", "&#092;", $content);
        // 4.替换斜杠
        $content = preg_replace("/\//", "&#47;", $content);
         
         return $content;
    }

    所以,对于PHP的安全而言,一定要对用户提交的数据进行过滤校验处理,即先防止SQL注入,后再进行XSS过滤,这两个都需要两手一起抓,且两手都要硬,否则,你的网站将会存在很大的安全风险。

    以上就是php安全问题思考的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:segmentfault,如有侵犯,请联系admin@php.cn删除

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    自己动手写 PHP MVC 框架:点击学习

    快速了解MVC架构、了解框架底层运行原理

    专题推荐:php
    上一篇:mac下多个php版本切换(可操作版) 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• php写网站步骤流程详细介绍• java和php的安全性对比• php类和方法的定义详细介绍
    1/1

    PHP中文网