很多人都知道SQL注入,也知道SQL参数化查询可以防止SQL注入,可为什么能防止注入却并不是很多人都知道的。

本文主要讲述的是这个问题,也许你在部分文章中看到过这块内容,当然了看看也无妨。

首先:我们要了解SQL收到一个指令后所做的事情:

在这里,我简单的表示为: 收到指令 -> 编译SQL生成执行计划 ->选择执行计划 ->执行执行计划。

具体可能有点不一样,但大致的步骤如上所示。

接着我们来分析为什么拼接SQL 字符串会导致SQL注入的风险呢?

首先创建一张表Users: CREATE TABLE [dbo].[Users]( [Id] [uniqueidentifier] NOT NULL, [UserId] [int] NOT NULL, [UserName] [varchar](50) NULL, [Password] [varchar](50) NOT NULL, CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]

插入一些数据: INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),1,'name1','pwd1'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),2,'name2','pwd2'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),3,'name3','pwd3'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),4,'name4','pwd4'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),5,'name5','pwd5');

假设我们有个用户登录的页面,代码如下:

验证用户登录的sql 如下: select COUNT(*) from Users where Password = 'a' and UserName = 'b'

这段代码返回Password 和UserName都匹配的用户数量,如果大于1的话,那么就代表用户存在。

本文不讨论SQL 中的密码策略,也不讨论代码规范,主要是讲为什么能够防止SQL注入,请一些同学不要纠结与某些代码,或者和SQL注入无关的主题。

可以看到执行结果:

这个是SQL profile 跟踪的SQL 语句。

注入的代码如下: select COUNT(*) from Users where Password = 'a' and UserName = 'b' or 1=1—'

这里有人将UserName设置为了 “b' or 1=1 –”.

实际执行的SQL就变成了如下:

可以很明显的看到SQL注入成功了。

很多人都知道参数化查询可以避免上面出现的注入问题,比如下面的代码: class Program { private static string connectionString = "Data Source=.;Initial Catalog=Test;Integrated Security=True"; static void Main(string[] args) { Login("b", "a"); Login("b' or 1=1--", "a"); } private static void Login(string userName, string password) { using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; //为每一条数据添加一个参数 comm.CommandText = "select COUNT(*) from Users where Password = @Password and UserName = @UserName"; comm.Parameters.AddRange( new SqlParameter[]{ new SqlParameter("@Password", SqlDbType.VarChar) { Value = password}, new SqlParameter("@UserName", SqlDbType.VarChar) { Value = userName}, }); comm.ExecuteNonQuery(); } } }

实际执行的SQL 如下所示: exec sp_executesql N'select COUNT(*) from Users where Password = @Password and UserName = @UserName',N'@Password varchar(1),@UserName varchar(1)',@Password='a',@UserName='b' exec sp_executesql N'select COUNT(*) from Users where Password = @Password and UserName = @UserName',N'@Password varchar(1),@UserName varchar(11)',@Password='a',@UserName='b'' or 1=1—'

可以看到参数化查询主要做了这些事情: 1:参数过滤,可以看到 @UserName='b'' or 1=1—' 2:执行计划重用

因为执行计划被重用,所以可以防止SQL注入。

首先分析SQL注入的本质,

用户写了一段SQL 用来表示查找密码是a的,用户名是b的所有用户的数量。

通过注入SQL,这段SQL现在表示的含义是查找(密码是a的,并且用户名是b的,) 或者1=1 的所有用户的数量。

可以看到SQL的语意发生了改变,为什么发生了改变呢?,因为没有重用以前的执行计划,因为对注入后的SQL语句重新进行了编译,因为重新执行了语法解析。所以要保证SQL语义不变,即我想要表达SQL就是我想表达的意思,不是别的注入后的意思,就应该重用执行计划。

如果不能够重用执行计划,那么就有SQL注入的风险,因为SQL的语意有可能会变化,所表达的查询就可能变化。

在SQL Server 中查询执行计划可以使用下面的脚本: DBCC FreeProccache select total_elapsed_time / execution_count 平均时间,total_logical_reads/execution_count 逻辑读, usecounts 重用次数,SUBSTRING(d.text, (statement_start_offset/2) + 1, ((CASE statement_end_offset WHEN -1 THEN DATALENGTH(text) ELSE statement_end_offset END - statement_start_offset)/2) + 1) 语句执行 from sys.dm_exec_cached_plans a cross apply sys.dm_exec_query_plan(a.plan_handle) c ,sys.dm_exec_query_stats b cross apply sys.dm_exec_sql_text(b.sql_handle) d --where a.plan_handle=b.plan_handle and total_logical_reads/execution_count>4000 ORDER BY total_elapsed_time / execution_count DESC;

在这篇文章中有这么一段:

这里作者有一句话:”不过这种写法和直接拼SQL执行没啥实质性的区别”

任何拼接SQL的方式都有SQL注入的风险,所以如果没有实质性的区别的话,那么使用exec 动态执行SQL是不能防止SQL注入的。

比如下面的代码: private static void TestMethod() { using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; //使用exec动态执行SQL  //实际执行的查询计划为(@UserID varchar(max))select * from Users(nolock) where UserID in (1,2,3,4)   //不是预期的(@UserID varchar(max))exec('select * from Users(nolock) where UserID in ('+@UserID+')') comm.CommandText = "exec('select * from Users(nolock) where UserID in ('+@UserID+')')"; comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" }); //comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4); delete from Users;--" }); comm.ExecuteNonQuery(); } }

执行的SQL 如下: exec sp_executesql N'exec(''select * from Users(nolock) where UserID in (''+@UserID+'')'')',N'@UserID varchar(max) ',@UserID='1,2,3,4' 可以看到SQL语句并没有参数化查询。 如果你将UserID设置为”

1,2,3,4); delete from Users;—- ”,那么执行的SQL就是下面这样: exec sp_executesql N'exec(''select * from Users(nolock) where UserID in (''+@UserID+'')'')',N'@UserID varchar(max) ',@UserID='1,2,3,4); delete from Users;--'

不要以为加了个@UserID 就代表能够防止SQL注入,实际执行的SQL 如下:

任何动态的执行SQL 都有注入的风险,因为动态意味着不重用执行计划,而如果不重用执行计划的话,那么就基本上无法保证你写的SQL所表示的意思就是你要表达的意思。 这就好像小时候的填空题,查找密码是(____) 并且用户名是(____)的用户。 不管你填的是什么值,我所表达的就是这个意思。 最后再总结一句:因为参数化查询可以重用执行计划,并且如果重用执行计划的话,SQL所要表达的语义就不会变化,所以就可以防止SQL注入,如果不能重用执行计划,就有可能出现SQL注入,

存储过程也是一样的道理,因为可以重用执行计划。

java参数化查询_【转】参数化查询为什么能够防止SQL注入相关推荐

  1. 参数化查询为什么能够防止SQL注入

    很多人都知道SQL注入,也知道SQL参数化查询可以防止SQL注入,可为什么能防止注入却并不是很多人都知道的. 本文主要讲述的是这个问题,也许你在部分文章中看到过这块内容,当然了看看也无妨. 首先:我们 ...

  2. oracle 查询天,Oracle查询_ 单表查询

    前面我们详解了关于Oracle的增删改,今天让我们接着来学习Oracle的查询吧, Oracle中查询可是重头戏噢!!!跟着煌sir的步伐,走位,走位~~~ 小知识锦囊 在此前,先讲解一个小知识点 O ...

  3. java数据查询_数据的查询

    -- 创建数据 CREATE DATABASE day2; -- 使用数据库 USE day2; -- 创建一个表格 CREATE TABLE stu( id INT, NAME VARCHAR(20 ...

  4. spring mysql防注入攻击_【spring】(填坑)sql注入攻击 - 持久层参数化

    结果 填坑失败,并没有看懂是如何检测sql攻击的. 只能说的是:建议都使用参数化传递sql语句参数.(所以,用hibernate.mybatis等框架的真不用太担心sql攻击问题.) 前言 在上文中的 ...

  5. sql 联合查询_一张图看懂sql运行顺序

    ​五月天的<干杯>这首歌短短几分钟,将一个人的一整个人生快速地表现出来.从上课爱看漫画的小男孩到精力充沛的高中生,再到走上工作岗位,而后有了下一代,再为下一代操心,最后进入天堂,和亲人们告 ...

  6. 如何根据vin码查询_车架号查询-VIN查询-车辆识别码查询-宜配网

    请输入17位车架号码: 通过车架号查询车辆信息,在销售配件,二手车交易中十分重要,宜配网车架号在线查询系统,提供免费的车架号查询. 宜配网解码了全球大多数主流车型的车架号,宜配网车架号在线查询系统,不 ...

  7. oracle之子查询_,Oracle子查询详解

    Oracle子查询详解,根据查询的结果(内部嵌套查询的结果)把子查询的类型分为单行子查询与多行子查询, 子查询概念 :当一个查询的结果是另一个查询的条件时,,称之为子查询. 使用子查询注意事项: 子查 ...

  8. c mysql数据库函数库查询_Mysql数据库使用concat函数执行SQL注入查询

    SQL注入语句有时候会使用替换查询技术,就是让原有的查询语句查不到结果出错,而让自己构造的查询语句执行,并把执行结果代替原有查询语句查询结果显示出来. 例如:原本查询语句是 select userna ...

  9. wap建站程序_因建站系统存在SQL注入漏洞,国内两家网络公司被CNVD公开点名

    2月27日,国家信息安全漏洞共享平台(CNVD)的漏洞列表收录了两个新的SQL注入漏洞--CNVD-2019-04308和CNVD-2019-05341.根据CNVD公布的信息来看,这两个漏洞的危害级 ...

  10. java通用分页条件查询_通用分页查询

    packagecom.dao;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.util.ArrayList;import ...

最新文章

  1. 通知:Spring Cloud Alibaba 仓库迁移
  2. 烧写文件系统——韦东山嵌入式Linux学习笔记11
  3. JavaScript内存泄漏知多少?
  4. Javascript中call函数和apply函数的使用
  5. 多线程join,强制执行完
  6. linux还原windows,双系统如何删除Linux,恢复Windows从  MBR引导启动?
  7. Spark中foreachRDD、foreachPartition和foreach解读
  8. rsync和inotify实时同步配置 exclude排除多个文件夹
  9. 企业办公3D指纹考勤系统解决方案(一)
  10. SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解——跟着开涛学SpringMVC...
  11. python超市管理系统流程图_python实现超市管理系统(后台管理)
  12. 解决拯救者Y7000打游戏,看视频没有声音的问题
  13. C4D教程大纲(R23)+笔记
  14. android系统电源管理驱动分析
  15. java微信授权登录回调地址,微信开发者工具,注册微信公共平台
  16. Heterogeneous Treatment Effect
  17. 计算机无法读取配置文件,由于权限不足,无法读取配置文件
  18. 数据结构栈的操作,初始化栈,进栈,出栈,读取栈顶元素;
  19. Vue2 轮播图组件 原生slide组件
  20. DPABI下载安装、测试及部分问题汇总

热门文章

  1. js或css文件后面跟参数的原因说明
  2. VC2005用不惯呀....
  3. Flex 与外部的数据通信(HTTPService,URLLoader和URLRequest)
  4. A bit of fun: fun with bits[翻译]
  5. 概率论综述(题型篇)
  6. springboot logback 调整 mybatis 日志级别无效
  7. logback.xml日志配置文件,springboot
  8. 转自weiphone]在美做开发多年,写给国内apple开发新手的一些心得,无教程
  9. Preference Learning——Object Ranking
  10. 利用CSS3实现鼠标悬停在图片上图片缓慢缩放的两种方法