php防止sql注入的方法

article/2025/9/23 13:09:11

一.什么是SQL注入式攻击?

        所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数,这类表单特别容易受到SQL注入式攻击。

例如一个简单的登录表单(这里把密码写成明文方便说明): 
这里写图片描述 
当在表单中填写这样的语句进行提交登录时会出现这样的SQL语句

select * from t_admin where admin_name='xxx' and admin_pwd='xxx'' or '1'

这样会查询出所有的用户信息,所有存在不安全隐患

二.常见的SQL注入式攻击过程类如:

⑴ 某个php Web应用有一个登录页面,这个登录页面控制着用户是否有权访问应用,它要求用户输入一个名称和密码

⑵ 登录页面中输入的内容将直接用来构造动态的SQL命令,或者直接用作存储过程的参数

⑶ 攻击者在用户名字和密码输入框中输入"'或'1'='1"之类的内容

⑷ 用户输入的内容提交给服务器之后,服务器运行上面的php代码构造出查询用户的SQL命令,但由于攻击者输入的内容非常特殊,所以最后得到的SQL命令变成:SELECT * from Users WHERE login = '' or '1'='1' AND password = '' or '1'='1'.

⑸ 服务器执行查询或存储过程,将用户输入的身份信息和服务器中保存的身份信息进行对比

⑹ 由于SQL命令实际上已被注入式攻击修改,已经不能真正验证用户身份,所以系统会错误地授权给攻击者

如果攻击者知道应用会将表单中输入的内容直接用于验证身份的查询,他就会尝试输入某些特殊的SQL字符串篡改查询改变其原来的功能,欺骗系统授予访问权限。

系统环境不同,攻击者可能造成的损害也不同,这主要由应用访问数据库的安全权限决定。如果用户的帐户具有管理员或其他比较高级的权限,攻击者就可能对数据库的表执行各种他想要做的操作,包括添加、删除或更新数据,甚至可能直接删除表。

三.常用的SQL注入方式

1. 使用单引号及or关键字

SELECT * FROM Users WHERE Username='$username' AND Password='$password' 


我们针对上面的SQL语句分析,发现如果用下面的测试数据就能够进行SQL注入了 

username=1′or′1′=′1password=1’or’1’=’1 

看看整个SQL查询语句变成: 


SELECT * FROM Users WHERE Username='1' OR '1'='1' AND Password='1'OR '1'='1' 


假设参数值是通过GET方法传递到服务器的,且域名为www.example.com 那么我们的访问请求就是:
http://www.example.com/index.php?username=1‘%20or%20’1’%20=%20’1password=1’%20or%20’1’%20=%20’1 

 对上面的SQL语句作简单分析后我们就知道由于该语句永远为真,所以肯定会返回一些数据,在这种情况下实际上并未验证用户名和密码,并且在某些系统中,用户表的第一行记录是管理员,那这样造成的后果则更为严重。

2. 使用括号

 SELECT * FROM Users WHERE((Username='$username')AND(Password=MD5('$password'))) 


在这个例子中,存在两个问题,一个是括号的用法,还有一个是MD5哈希函数的用法。对于第一个问题,我们很容找出缺少的右括号解决,对于第二个问题,我们可以想办法使第二个条件失效。我们在查询语句的最后加上一个注释符以表示后面的都是注释,常见的注释起始符是/*(在Oracle中是–),也就是说,我们用如下的用户名和密码: 

username=1′or′1′=′1′))/∗password = foo 
那么整条SQL语句就变为: 


  SELECT * FROM Users WHERE(( Username='1'or '1'='1'))/*')AND (Password=MD5('$password'))) 



那么看看URL请求就变为: 

http://www.example.com/index.php?username=1‘%20or%20’1’%20=%20’1’))/*&password=foo 

#####

3.Union查询SQL注入测试 
 


Union查询SQL注入测试 
 还有一种测试是利用Union的,利用Union可以连接查询,从而从其他表中得到信息,假设如下查询: 


  SELECT Name, Phone, Address FROM Users WHERE Id=$id 


然后我们设置id的值为: 

$id =1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCarTable ,
那么整体的查询就变为: 


SELECT Name, Phone,Address FROM Users WHERE Id=1 UNION ALL SELECT creaditCardNumber,1,1 FROM CreditCarTable 


显示这就能得到所有信用卡用户的信息。 

盲目SQL注入测试 
 在上面我们提到过盲SQL注入,即bind SQL Injection,它意味着对于某个操作我们得不到任何信息,通常这是由于程序员已经编写了特定的出错返回页面,从而隐藏了数据库结构的信息。 



但利用推理方法,有时候我们能够恢复特定字段的值。这种方法通常采用一组对服务器的布尔查询,依据返回的结果来推断结果的含义。仍然延续上面的www.example.com有一个参数名为id, 那么我们输入以下url请求: 

 http://www.exampe.com/index.php?id=1’ 

显然由于语法错误,我们会得到一个预先定义好的出错页面,假设服务器上的查询语句为 :

SELECT field1,field2,field3 FROM Users WHERE Id=’Id′假设我们想要的用户名字段的值,那么通过一些函数,我们就可以逐字符的读取用户名的值。在这里我们使用以下的函数:SUBSTRING(text,start,length),ASCII(char),LENGTH(text)
我们定义id为:引用Id=1’ AND ASCII(SUBSTRING(username,1,1))=97 AND ‘1’=’1 

那么最终的SQL查询语句为: 

SELECT field1,field2,field3 FROM Users WHERE Id='1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1' 


那么,如果在数据库中有用户名的第一字符的ASCII码为97的话,那么我们就能得到一个真值u,那么就继续寻找该用户名的下一个字符;如果没有的话,那么我们就增猜测第一个字符的ASCII码为98的用户名,这样反复下去就能判断出合法的用户名

四.如何防范?

只要在利用表单输入的内容构造SQL命令之前,把所有输入内容过滤一番就可以了。过滤输入内容可以按多种方式进行:

 (1)对于动态构造SQL查询的场合,可以使用下面的技术:

  第一:替换单引号,即把所有单独出现的单引号改成两个单引号,防止攻击者修改SQL命令的含义。再来看前面的例子,“SELECT * from Users WHERE login = ''' or ''1''=''1' AND password = ''' or ''1''=''1'”显然会得到与“SELECT * from Users WHERE login = '' or '1'='1' AND password = '' or '1'='1'”不同的结果

  第二:删除用户输入内容中的所有连字符,防止攻击者构造出类如“SELECT * from Users WHERE login = 'mas' AND password =''”之类的查询,因为这类查询的后半部分已经被注释掉,不再有效,攻击者只要知道一个合法的用户登录名称,根本不需要知道用户的密码就可以顺利获得访问权限

第三:对于用来执行查询的数据库帐户,限制其权限。用不同的用户帐户执行查询、插入、更新、删除操作。由于隔离了不同帐户可执行的操作,因而也就防止了原本用于执行SELECT命令的地方却被用于执行INSERT、UPDATE或DELETE命令

 (2)用存储过程来执行所有的查询。SQL参数的传递方式将防止攻击者利用单引号和连字符实施攻击。此外,它还使得数据库权限可以限制到只允许特定的存储过程执行,所有的用户输入必须遵从被调用的存储过程的安全上下文,这样就很难再发生注入式攻击了。

 (3)限制表单或查询字符串输入的长度。如果用户的登录名字最多只有10个字符,那么不要认可表单中输入的10个以上的字符,这将大大增加攻击者在SQL命令中插入有害代码的难度

 (4)检查用户输入的合法性,确信输入的内容只包含合法的数据。数据检查应当在客户端和服务器端都执行,之所以要执行服务器端验证,是为了弥补客户端验证机制脆弱的安全性  

 在客户端,攻击者完全有可能获得网页的源代码,修改验证合法性的脚本(或者直接删除脚本),然后将非法内容通过修改后的表单提交给服务器。因此,要保证验证操作确实已经执行,唯一的办法就是在服务器端也执行验证。你可以使用许多内建的验证对象,例如RegularExpressionValidator,它们能够自动生成验证用的客户端脚本,当然你也可以插入服务器端的方法调用。如果找不到现成的验证对象,你可以通过CustomValidator自己创建一个。

 (5) 将用户登录名称、密码等数据加密保存。加密用户输入的数据,然后再将它与数据库中保存的数据比较,这相当于对用户输入的数据进行了“消毒”处理,用户输入的数据不再对数据库有任何特殊的意义,从而也就防止了攻击者注入SQL命令

(6) 检查提取数据的查询所返回的记录数量。如果程序只要求返回一个记录,但实际返回的记录却超过一行,那就当作出错处理。

 (7)使用预处理语句

常用SQL注入的解决方案

1.添加图形码进行验证

添加图形码在一定程序上增加代码的安全性,给机器强制破解有一定的拦截作用,但不能阻止所有的攻击,故还是需要在程序上进行安全性考虑

2.使用预备义语句和参数化查询

使用预处理语句和参数化查询。预处理语句和参数分别发送到数据库服务器进行解析,参数将会被当作普通字符处理。这种方式使得攻击者无法注入恶意的SQL。常用的方式有两种

2.1 使用PDO(PHP Data Objects )

$stmt = $pdo->prepare('SELECT * FROM t_admin WHERE admin_name = :name');
$stmt->execute(array('name' => $name));
foreach ($stmt as $row) {// do something with $row
}

注意,在默认情况使用PDO并没有让MySQL数据库执行真正的预处理语句(原因见下文)。为了解决这个问题,你应该禁止PDO模拟预处理语句。一个正确使用PDO创建数据库连接的例子如下

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

2.2 使用MySQLi

$stmt = $dbConnection->prepare('SELECT * FROM t_admin WHERE admin_name = ?');
$stmt->bind_param('s', $name);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {// do something with $row
}

3.对用户传递的参数进行处理

我们知道Web上提交数据有两种方式,一种是get、一种是post,那么很多常见的sql注射就是从get方式入手的,而且注射的语句里面一定是包含一些sql语句的,因为没有sql语句,那么如何进行,sql语句有四大句:select 、update、delete、insert,那么我们如果在我们提交的数据中进行过滤是不是能够避免这些问题呢? 
于是我们使用正则就构建如下函数:

 <?phpfunction inject_check($sql_str)     {     return eregi('select|insert|update|delete|');}function verify_id($id=null)     {     if (!$id) { exit('没有提交参数!'); } // 是否为空判断     elseif (inject_check($id)) { exit('提交的参数非法!'); } // 注射判断     elseif (!is_numeric($id)) { exit('提交的参数非法!'); } // 数字判断     $id = intval($id); // 整型化         return $id;     }     ?>

那么我们就能够进行校验了,于是我们上面的程序代码就变成了下面的:

 <?php     if (inject_check($_GET['id']))     {     exit('你提交的数据非法,请检查后重新提交!');     }     else     {     $id = verify_id($_GET['id']); // 这里引用了我们的过滤函数,对$id进行过滤     echo '提交的数据合法,请继续!';     }     ?>

好,问题到这里似乎都解决了,但是我们有没有考虑过post提交的数据,大批量的数据呢? 
比如一些字符可能会对数据库造成危害,比如 ’ _ ‘, ’ %’,这些字符都有特殊意义,那么我们如果进行控制呢?还有一点,就是当我们的php.ini里面的magic_quotes_gpc = off的时候,那么提交的不符合数据库规则的数据都是不会自动在前面加’ ‘的,那么我们要控制这些问题,于是构建如下函数:

 <?php        function str_check( $str )     {     if (!get_magic_quotes_gpc()) // 判断magic_quotes_gpc是否打开     {     $str = addslashes($str); // 进行过滤     }     $str = str_replace("_", "\_", $str); // 把 '_'过滤掉     $str = str_replace("%", "\%", $str); // 把' % '过滤掉     return $str;     }     ?>
最后,再考虑提交一些大批量数据的情况,比如发贴,或者写文章、新闻,我们需要一些函数来帮我们过滤和进行转换,再上面函数的基础上,对提交的数据进行引号转义及将HTML标签转换成HTML实体,可以构建如下函数:
 <?php      function post_check($post)     {     if (!get_magic_quotes_gpc()) // 判断magic_quotes_gpc是否为打开     {     $post = addslashes($post); // 进行magic_quotes_gpc没有打开的情况对提交数据的过滤     }     $post = str_replace("_", "\_", $post); // 把 '_'过滤掉     $post = str_replace("%", "\%", $post); // 把' % '过滤掉     $post = nl2br($post); // 回车转换     $post= htmlspecialchars($post); // html标记转换        return $post;     }     ?>或者根据API文档中的:
TP框架防止sql注入:

对于WEB应用来说,SQL注入攻击无疑是首要防范的安全问题,系统底层对于数据安全方面本身进行了很多的处理和相应的防范机制,例如:

  1. $User = M("User"); // 实例化User对象
  2. $User->find($_GET["id"]);

即便用户输入了一些恶意的id参数,系统也会强制转换成整型,避免恶意注入。这是因为,系统会对数据进行强制的数据类型检测,并且对数据来源进行数据格式转换。而且,对于字符串类型的数据,ThinkPHP都会进行escape_string处理(real_escape_string,mysql_escape_string),如果你采用PDO方式的话,还支持参数绑定。

通常的安全隐患在于你的查询条件使用了字符串参数,然后其中一些变量又依赖由客户端的用户输入。

要有效的防止SQL注入问题,我们建议:

  • 查询条件尽量使用数组方式,这是更为安全的方式;
  • 如果不得已必须使用字符串查询条件,使用预处理机制;
  • 使用自动验证和自动完成机制进行针对应用的自定义过滤;
  • 如果环境允许,尽量使用PDO方式,并使用参数绑定。

查询条件预处理

where方法使用字符串条件的时候,支持预处理(安全过滤),并支持两种方式传入预处理参数,例如:

  1. $Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select();
  2. // 或者
  3. $Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();

模型的query和execute方法 同样支持预处理机制,例如:

  1. $model->query('select * from user where id=%d and status=%d',$id,$status);
  2. //或者
  3. $model->query('select * from user where id=%d and status=%d',array($id,$status));

execute方法用法同query方法。


http://chatgpt.dhexx.cn/article/CCSI61Rd.shtml

相关文章

如何防止SQL注入攻击?

SQL注入是一种注入攻击&#xff0c;可以执行恶意SQL语句。下面本篇文章就来带大家了解一下SQL注入&#xff0c;简单介绍一下防止SQL注入攻击的方法&#xff0c;希望对大家有所帮助。 什么是SQL注入&#xff1f; SQL注入&#xff08;SQLi&#xff09;是一种注入攻击&#xff0c;…

SecureCRT 7.3软件下载及破解工具+教程

1、首先下载这两个文件&#xff1b; 2、安装scrt7.3.4&#xff0c;我这里提供的是一个32位的版本&#xff0c;64位的机器安装之后也没问题&#xff0c;在安装过程中&#xff0c;程序会提示你是否将32位的软件安装至64的操作系统&#xff0c;直接点击Continue&#xff1b; 3、是…

【51单片机】单片机仿真软件Proteus 8.7破解和汉化教程(附下载地址)

教程 进入安装包P8.7.SP3.exe&#xff0c;接受协议后next 选择Use a locally installed license key&#xff0c;next 进入激活界面&#xff0c;打开Crack包里的license.lxk并安装 默认选项&#xff0c;next 下一步选择完整安装 安装完成后不要运行&#xff0c;点击Close退出安…

软件破解注册码

写在破解之前&#xff1a;&#xff1a;&#xff1a;       软件破解的目的是&#xff1a;有些需要注册的软件&#xff0c;可是找不到注册码&#xff0c;将其破解之后&#xff0c;输入任何注册码都会提示注册成功。             声明&#xff1a;此贴适合从来没接触…

软件破解工具合集

[原文]调试工具&#xff08;Debuggers&#xff09; OllyDbg调试器 OllyDbg v1.1 一个新的动态追踪工具&#xff0c;将IDA与结合起来的思想&#xff0c;Ring 3级调试器&#xff0c;非常容易上手&#xff0c;己代替SoftICE成为当今最为流行的调试解密工具了。强烈推荐&#xff01…

软件破解基础教程

分步阅读 步骤 1 2 3 4 5 6 7 8 先教大家一些基础知识,学习破解其实是要和程序打交道的,汇编是破解程序的必备知识,但有可能部分朋友都没有学习过汇编语言,所以我就在这里叫大家一些简单实用的破解语句吧! -------------------------------------------------…

破解软件

转载&#xff1a;http://gbuy.gdcvi.com/?ArticleID19712 手把手教你破解软件&#xff0c;每一步都有图示&#xff0c;你若再不会就太XX 软件破解的目的是&#xff1a;&#xff1a;&#xff1a;有些需要注册的软件&#xff0c;可是找不到注册码&#xff0c;将其破解之后&am…

软件逆向工程:破解教程(1/24)

软件逆向工程&#xff1a;破解教程&#xff08;1/24&#xff09; 想学破解&#xff0c;花了很多时间还是没有多少进步&#xff1f;网上很多教程&#xff0c;看来看去&#xff0c;到头来还是一头雾水&#xff1f;其实&#xff0c;很正常&#xff0c;不是你的问题&#xff0c;是没…

软件破解初级实例教程(附工具附图)

最近在群里总是看到很多新朋友在问&#xff1a;1、“新手怎么学破解啊&#xff1f;”&#xff08;这是标准的伸手党&#xff0c;baidu google其实很好用&#xff09;2、“哎呀XX大牛&#xff0c;我什么基础都没有啊我不会汇编&#xff0c;不会C更不会C还不会…………总之高手会…

软件破解实例教程

破解需要的软件&#xff08;点击下载&#xff09;&#xff1a; 侦壳 language.exe 脱壳AspackDie.exe 反编译 W32Dasm黄金中文版 16进制编辑器 UltraEdit.rar 在破解之前先复习一下基础知识&#xff1a; 一.破解的等级 初级,修改程序,用ultraedit修改exe文件,称…

原码 反码 补码的简单计算附例题

原码 反码 补码 对计算机中常见数据简单分类机器数 与 真值原码反码补码扩展 对计算机常见数据的分类 机器数 就是数值在计算机中的二进制表现形式 机器数在计算机中有符号,使用 最高位表示符号 , 使用0 表示正,使用 1 表示负 一个字节8个bit位表示 5 0000 0101 -10 1…

原码反码补码移码

原码&#xff1a; 数值直接转为二进制数&#xff0c;负数的最高位 置1。 以8位为例&#xff1a; 1的原码为0000 0001&#xff0c;-1的原码为1000 0001. 127的原码为0111 1111&#xff0c;-127的原码为1111 1111. 0的原码为0000 0000&#xff0c;-0的原码为1000 0000. 反码…

二进制原码反码补码详解

二进制原码反码补码 首先我们在了解什么是原码&#xff0c;反码&#xff0c;补码之前&#xff0c;我们先来谈谈为什么需要有这些&#xff0c;只要原码不行吗&#xff1f; 答案肯定是不行的&#xff01;&#x1f61c; 因为在计算机中&#xff0c;二进制的运算对于正数之间不会出…

真值 原码 反码 补码

引言 计算机中只能做加法运算&#xff0c;它的减法是通过加法来实现的。原码&#xff0c;反码&#xff0c;补码的产生过程&#xff0c;就是为了解决计算机做减法和引入符号位的问题 真值 正数或负数的真值 → 为其绝对值对应的二进制数前面加上正号或负号 例如&#xff1a; 1 …

原码 反码 补码及应用

原码 反码 补码及应用 原码 什么是原码&#xff1f; 原码&#xff1a;十进制数据的二进制表现形式&#xff0c;最左边是符号位&#xff0c;0为正&#xff0c;1为负。 56 > 0 0111000 ​ 符号位 数据 最大值&#xff1a;01111111 > 127 最小值&#xff1a;11111111…

原码反码补码原理理解

原码反码补码原理理解 基础知识原码定义在这里&#xff0c;我们模仿一下计算机对数据运算的过程&#xff01;那么所有运算都是正确的吗&#xff1f;我们再试一组。 反码定义在这里&#xff0c;我们模仿一下计算机对数据运算的过程&#xff01;正数加负数也没问题了&#xff0c;…

java基础-原码反码补码

本文帮助理解&#xff0c;Java中原码反码补码的原理 1&#xff1a;原码反码补码&#xff0c;基础概念和计算方法 对于一个数&#xff0c;计算机需要使用一定的编码方式进行存储。原码反码补码是计算机存储一个具体数字的编码方式。 原码&#xff1a; 第一位表示符号位&…

原码反码补码习题

1、有以下变量求输出结果 signed char c 178; printf("%din",c); printf("%u\n",c); unsigned char d -9; printf("%din".d); printf("%u\n",d)&#xff1b; 并写出数据存储和取出的过程 2、求-57的原码、反码、补码 原码&am…

原码、反码、补码

一、前置概念 计算机底层存储数据时使用的是二进制数字&#xff0c;但是计算机在存储一个数字时并不是直接存储该数字对应的二进制数字&#xff0c;而是存储该数字对应二进制数字的补码。所以接下来我们需要来了解一下原码、反码和补码。 那么再了解原码、反码、补码之前&…

原码、反码以及补码

目录 一、概念 二、补码的意义 一、概念 计算机底层存储数据时&#xff0c;存储的是数据对应的二进制数字。对于整型数据&#xff0c;其二进制表示形式有三种&#xff0c;分别是&#xff1a;原码、反码、补码&#xff0c;而实际存储的是整型数据的补码。 原码、反码以及补码…