Home > Backend Development > PHP Tutorial > Thoughts on PHP security issues

Thoughts on PHP security issues

藏色散人
Release: 2023-04-07 11:10:02
forward
3290 people have browsed it

用户提交过来的数据都是不可信的,所以,在查库或入库前需要对提交过来的数据进行过滤或字符的转换处理,以防止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);
Copy after login

测试:

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

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

Thoughts on PHP security issues

2. 类型处理不当

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

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

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

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

处理方法:

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

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

3. 查询语句组织不当

user.php?table=user&
Copy after login

4. 错误处理不当

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

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

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

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

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

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

函数原型:

htmlspecialchars(string,quotestyle,character-set)
Copy after login

预定义的字符是:

& (和号)    成为 &
” (双引号)  成为 "
‘ (单引号)  成为 &#039;
< (小于)    成为 <
> (大于)    成为 >
Copy after login

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

函数原型:

htmlspecialchars_decode(string,quotestyle)
Copy after login

二、防止xss攻击

什么是xss攻击?

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

比如你写个留言本,有人去留言写

除了通过正常途径输入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 (&#39;"&#39;, "\\", "&#39;", "/", "..", "../", "./", "//" ), &#39;&#39;, $string );
        $no = &#39;/%0[0-8bcef]/&#39;;
        $string = preg_replace ( $no, &#39;&#39;, $string );
        $no = &#39;/%1[0-9a-f]/&#39;;
        $string = preg_replace ( $no, &#39;&#39;, $string );
        $no = &#39;/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S&#39;;
        $string = preg_replace ( $no, &#39;&#39;, $string );
        return True;
    }
    $keys = array_keys ( $string );
    foreach ( $keys as $key )
    {
        clean_xss ( $string [$key] );
    }
}
//just a test
$str = &#39;codetc.com<meta http-equiv="refresh" content="0;">&#39;;
clean_xss($str); //如果你把这个注释掉,你就知道xss攻击的厉害了
echo $str;
?>
Copy after login

避免被XSS:

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

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

三、PHP MySQL 预处理语句

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

预处理语句及绑定参数

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

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

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

INSERT 
    INTO MyGuests (firstname, lastname, email) VALUES(?, ?, ?)
Copy after login

数据库解析,编译,对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)";
Copy after login

  2、发送预处理语句

$stmt = $pdo->prepare($sql1);
Copy after login

  3、给预处理绑定数据

$arr = array(
      &#39;:s_name&#39; => &#39;房祖名&#39;,
      &#39;:s_num&#39; => &#39;itcast0013&#39;,
      &#39;:s_gender&#39; => 0,
      &#39;:s_age&#39; => 28,
      &#39;:c_id&#39; => 2
    );
Copy after login

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

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

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);
Copy after login

原始链接方法:

<?php  
// PDO的使用
// http://blog.csdn.net/qq635785620/article/details/11284591
$dbh = new PDO(&#39;mysql:host=127.0.0.1:3306;dbname=mysql_safe&#39;, &#39;root&#39;, &#39;518666&#39;); 
 
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);    
$dbh->exec(&#39;set names utf8&#39;);   
$title    = "我们的爱情";
$content  = &#39;你是/谁啊,大几\都"老梁"做做&>women<a>没&#39; . " 测试打印号&#39;我是单引号&#39;哈哈";
$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(&#39;x_title&#39;=>$title,&#39;:x_content&#39;=> $content, &#39;:x_user_id&#39; => $user_id, &#39;:x_add_time&#39; => $add_time));    
echo $dbh->lastinsertid();
Copy after login


Thoughts on PHP security issues

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

$pdo = new PDO(&#39;mysql:host=localhost;dbname=test;charset=utf8&#39;, &#39;root&#39;);
Copy after login

示例:

/**
*  插入用户的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(&#39;Y-m-d H:i:s&#39;));
    sqlSetParam($sql,"x_last_open_time", time());
    return mysql_query($sql);
}
Copy after login

总结: 当调用 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"];
}
Copy after login

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

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

function editor_safe_replace($content)
{
   $content = trim($content);
    $tags = array(
        "&#39;<iframe[^>]*?>.*?</iframe>&#39;is",
        "&#39;<frame[^>]*?>.*?</frame>&#39;is",
        "&#39;<script[^>]*?>.*?</script>&#39;is",
        "&#39;<head[^>]*?>.*?</head>&#39;is",
        "&#39;<title[^>]*?>.*?</title>&#39;is",
        "&#39;<meta[^>]*?>&#39;is",
        "&#39;<link[^>]*?>&#39;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;
}
Copy after login

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

The above is the detailed content of Thoughts on PHP security issues. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
php
source:segmentfault.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template