OLEDB 参数化查询
一般情况下,SQL查询是相对固定的,一条语句变化的可能只是条件值,比如之前要求查询二年级学生信息,而后面需要查询三年级的信息,这样的查询一般查询的列不变,后面的条件只有值在变化,针对这种查询可以使用参数化查询的方式来提高效率,也可以时SQL操作更加安全,从根本上杜绝SQL注入的问题。
参数化查询的优势:
- 提高效率:之前说过,数据库在执行SQL的过程中,每次都会经过SQL的解析,编译,调用对应的数据库组件,这样如果执行多次同样类型的SQL语句,解析,编译的过程明显是在浪费资源,而参数化查询就是使用编译好的过程(也就是提前告诉数据库要调用哪些数据库组件),这样就跳过了对SQL语句的解析,编译过程,提高了效率(这个过程我觉得有点类似于C/C++语言的编译执行与脚本语言的解释执行)。
- 更加安全:从安全编程的角度来说,对于防范SQL注入方面,它比关键字过滤更有效,实现起来也更加方便。
科普SQL注入和安全编程
什么是SQL注入:
所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。举个例子来说在用户登录时会输入用户名密码,这个时候在后台就可以执行这样的SQL语句
select count(*) from user where username = 'haha' and password = '123456'
只有输入对了用户名和密码才能登录,但是如果没有对用户输入进行校验,当用户输入一些SQL中的语句,而后台直接将用户输入进行拼接并执行,就会发生注入,比如此时用户输入 *** 'haha' or 1 = 1 -- *** ,此时再后台执行的sql语句就变成了这样:
select count(*) from user where username = 'haha' or 1 = 1 -- and password = ''
这样用户就可以不用密码,直接使用用户名就登录了。而防范这类攻击,一般采用的是关键字过滤的方式,但是关键字过滤并不能杜绝这类工具,当一时疏忽忘记了过滤某个关键字仍然会产生这类问题。而且关键字过滤一般采用正则表达式,而正则表达式并不是一般人可以驾驭的。而防范SQL注入最简单也是最一劳永逸的方式就是参数化查询。
为什么参数化查询能够从根本上解决SQL注入
发生SQL注入一般的原因是程序将用户输入当做SQL语句的一部分进行执行,但是参数化查询它只是将用户输入当做参数,当做查询的条件,从数据库的层面上来说,它不对应于具体的数据库组件,它只是一组数据,而不会执行。这里可以简单的将传统的SQL拼接方式理解为C语言中的宏,宏也可以有参数,但是它不对参数进行校验,只是简单的进行替换,那么我可以使用一些指令作为参数传入,但是函数就不一样,函数的参数就是具体类型的变量或者常量。所以参数化查询从根本上解决的SQL注入的问题。
参数化查询的使用
前面说了这么多参数化查询的好处,那么到底怎么使用它呢?
在Java等语言中内置了数据库操作,而对于C/C++来说,它并没有提供这方方面的标准。不同的平台有自己独特的一套机制,但是从总体来说,思想是共通的,只是语法上的不同,这里主要是说明OLEDB中的使用方式。
- 使用“?”符将SQL语句中的条件值常量进行替换,组成一个新的SQL语句,比如上面登录的查询语句可以写成
select count(*) from user where username = ? and password = ?
- 调用ICommandText的SetCommandText设置sql语句。
- 调用ICommandParpare的Prepare方法对含有"?"的语句进行预处理
- 调用ICommandWithParameters方法的GetParameterInfo方法获取参数详细信息的DBPARAMINFO结构(类似于DBCOLUMNINFO)
- 分配对应大小的DBBINDING缓冲用来保存每个参数的绑定信息
- 调用IAccessor的CreateAccessor方法创建对应的访问器
- 为参数分配缓冲,设置合适的参数后准备DBPARAMS结构
- 调用ICommandText的Execute方法并将DBPARAMS结构的指针作为参数传入。
- 操作返回的结果集对象
typedef struct tagDBPROPIDSET {
DBPROPID * rgPropertyIDs;
ULONG cPropertyIDs;
GUID guidPropertySet;
} DBPROPIDSET;
DBPARAMS结构的定义如下:
typedef struct tagDBPARAMS
{
void *pData;
DB_UPARAMS cParamSets;
HACCESSOR hAccessor;
} DBPARAMS;
- pData是保存参数信息的缓冲;
- cParamSets: 表示又多少个参数
- hAccessor: 之前获取到的绑定结构的访问器句柄
下面是一个使用的例子:
BOOL QueryData(LPOLESTR pQueryStr, IOpenRowset* pIOpenRowset, IRowset* &pIRowset)
{
IAccessor *pParamAccessor = NULL; //与参数化查询相关的访问器接口
LPOLESTR pSql = _T("Select * From aa26 Where Left(aac031,2) = ?"); //参数化查询语句
BOOL bRet = FALSE;
DB_UPARAMS uParams = 0;
DBPARAMINFO* rgParamInfo = NULL;
LPOLESTR pParamBuffer = NULL;
DWORD dwOffset = 0;
DBBINDING *rgParamBinding = NULL;
HACCESSOR hAccessor = NULL;
DBPARAMS dbParams = {0};
DBBINDSTATUS *pdbBindStatus = NULL;
//设置SQL
hRes = pICommandText->SetCommandText(DBGUID_DEFAULT, pSql);
//预处理SQL命令
pICommandPrepare->Prepare(0);
hRes = pICommandText->QueryInterface(IID_ICommandPrepare, (void**)&pICommandPrepare);
//获取参数信息
hRes = pICommandText->QueryInterface(IID_ICommandWithParameters, (void**)&pICommandWithParameters);
COM_SUCCESS(hRes, _T("查询接口ICommandWithParameters失败,错误码为:%08x\n"), hRes);
hRes = pICommandWithParameters->GetParameterInfo(&uParams, &rgParamInfo, &pParamBuffer);
COM_SUCCESS(hRes, _T("获取参数信息失败,错误码为:%08x\n"), hRes);
rgParamBinding = (DBBINDING*)MALLOC(sizeof(DBBINDING) * uParams);
ZeroMemory(rgParamBinding, sizeof(DBBINDING) * uParams);
//绑定参数信息
for (int i = 0; i < uParams; i++)
{
rgParamBinding[i].bPrecision = rgParamInfo[i].bPrecision;
rgParamBinding[i].bScale = rgParamInfo[i].bScale;
rgParamBinding[i].cbMaxLen = 7 * sizeof(WCHAR); //行政区编号最大长度为6
rgParamBinding[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgParamBinding[i].dwPart = DBPART_LENGTH | DBPART_VALUE;
rgParamBinding[i].eParamIO = DBPARAMIO_INPUT;
rgParamBinding[i].iOrdinal = rgParamInfo[i].iOrdinal;
rgParamBinding[i].obLength = dwOffset;
rgParamBinding[i].obStatus = 0;
rgParamBinding[i].obValue = dwOffset + sizeof(ULONG);
rgParamBinding[i].wType = DBTYPE_WSTR;
dwOffset = dwOffset + sizeof(ULONG) + rgParamBinding[i].cbMaxLen;
dwOffset = UPROUND(dwOffset);
}
//获取访问器
pdbBindStatus = (DBBINDSTATUS*)MALLOC(uParams * sizeof(DBBINDSTATUS));
ZeroMemory(pdbBindStatus, uParams * sizeof(DBBINDSTATUS))
pParamAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, uParams, rgParamBinding, dwOffset, &hAccessor, pdbBindStatus);
COM_SUCCESS(hRes, _T("获取参数访问器失败,错误码为:%08x\n"), hRes);
//准备参数
dbParams.pData = MALLOC(dwOffset);
ZeroMemory(dbParams.pData, dwOffset);
dbParams.cParamSets = uParams;
dbParams.hAccessor = hAccessor;
for (int i = 0; i < uParams; i++)
{
*(ULONG*)((BYTE*)dbParams.pData + rgParamBinding[i].obLength) = _tcslen(pQueryStr) * sizeof(WCHAR);
StringCchCopy((LPTSTR)((BYTE*)dbParams.pData + rgParamBinding[i].obValue), _tcslen(pQueryStr) + 1, pQueryStr);
}
//执行SQL
hRes = pICommandText->Execute(NULL, IID_IRowset, &dbParams, NULL, (IUnknown**)&pIRowset);
return bRet;
}
OLEDB 参数化查询的更多相关文章
- ACCESS的参数化查询
看论坛上还许多人问及ACCESS被注入的安全问题许多人解决的方法仍然是用Replace替换特殊字符,然而这样做也并没有起到太大做用今天我就把我用ACCESS参数化查询的一些方法和经验和大家分享希望对大 ...
- SQL Server 2008 R2——VC++ ADO 操作 参数化查询
==================================声明================================== 本文原创,转载在正文中显要的注明作者和出处,并保证文章的完 ...
- SQL参数化查询--最有效可预防SQL注入攻击的防御方式
参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值. 在使用参 ...
- SQL 语句在查询分析器执行很快,程序 Dapper 参数化查询就很慢(parameter-sniffing)
这个问题困扰我好长时间了,使用SQLSERVER 事务探查器找到执行超时的SQL语句,参数查询都是通过执行exe sp_executesql 的存储过程调用,因为它能够分析并缓存查询计划,从而优化查询 ...
- 使用参数化查询防止SQL注入漏洞(转)
SQL注入的原理 以往在Web应用程序访问数据库时一般是采取拼接字符串的形式,比如登录的时候就是根据用户名和密码去查询: string sql * FROM [User] WHERE UserName ...
- 防止sql注入的参数化查询
参数化查询为什么能够防止SQL注入 http://netsecurity.51cto.com/art/201301/377209.htm OleDbDataAdapter Class http://m ...
- SQL参数化查询自动生成SqlParameter列表
string sql = @"INSERT INTO stu VALUES (@id,@name) "; 参数化查询是经常用到的,它可以有效防止SQL注入.但是需要手动去匹配参数@ ...
- Sql Server参数化查询之where in和like实现详解
where in 的参数化查询实现 首先说一下我们常用的办法,直接拼SQL实现,一般情况下都能满足需要 string userIds = "1,2,3,4"; using (Sql ...
- 【转】浅析Sql Server参数化查询
转载至: http://www.cnblogs.com/lzrabbit/archive/2012/04/21/2460978.html 错误认识1.不需要防止sql注入的地方无需参数化 参数化查询就 ...
随机推荐
- HDU6308-2018ACM暑假多校联合训练1011-Time Zone
题目大意就是给你UTC-8时区的时间 让你求对应时区的时间 哇 这个题 看似简单,但是一开始怎么都过不了啊 同学用自己写的read过了,后来看了一下各位大佬说改成分钟随便过,就随便过了 Problem ...
- 解决 Github用户名 变为 invalid-email-address 问题
解决 Github用户名 变为 invalid-email-address 问题 If the identity used for this commit is wrong, you can fix ...
- SDUT OJ 顺序表应用6:有序顺序表查询
顺序表应用6:有序顺序表查询 Time Limit: 1000 ms Memory Limit: 4096 KiB Submit Statistic Discuss Problem Descripti ...
- JS 函数的基本练习
// - 求3个数中的最大值 function biJiao3(x, y, z) { return x > y ? (x > z ? x : z) : (y > z ? y : z) ...
- libcaffe.so.1.0.0: cannot open shared object file: No such file or directory 运行时报错
caffe安装好后lib没有配置到/usr/lib或/usr/local/lib中,需手动配置: sudo vim ~/.bashrc export LD_LIBRARY_PATH=your_path ...
- python学习之路---day21--模块和栈
模块和栈 一:计数模块collections 基础版本: s="qwewsfdfjiehrfqweqweqwqewq" dic={} for el in s: dic[el]=di ...
- 洛谷 P3616 富金森林公园题解(树状数组)
P3616 富金森林公园 题目描述 博艾的富金森林公园里有一个长长的富金山脉,山脉是由一块块巨石并列构成的,编号从1到N.每一个巨石有一个海拔高度.而这个山脉又在一个盆地中,盆地里可能会积水,积水也有 ...
- POJ_3126 Prime Path 【BFS+素数打表】
一.题目 http://poj.org/problem?id=3126 二.分析 该题主要是要让我们找到一个$4$位素数到另一个$4$位素数的最少的变换次数,且要求保证每一次变换都满足 1.下一个数必 ...
- Oracle之q操作符
Oracle本身默认的是单引号,但是在大家写存储过程或者写SQL语句时,有时候需要拼SQL或者是SQL的值里需要传入含单引号的值,此时就需要使用两个单引号''''来进行转义,其实oracle本身提供了 ...
- 移动距离--dfs-蓝桥杯
题目描述: X星球居民小区的楼房全是一样的,并且按矩阵样式排列.其楼房的编号为1,2,3... 当排满一行时,从下一行相邻的楼往反方向排号. 比如:当小区排号宽度为6时,开始情形如下: 1 2 ...