OLEDB 参数化查询

一般情况下,SQL查询是相对固定的,一条语句变化的可能只是条件值,比如之前要求查询二年级学生信息,而后面需要查询三年级的信息,这样的查询一般查询的列不变,后面的条件只有值在变化,针对这种查询可以使用参数化查询的方式来提高效率,也可以时SQL操作更加安全,从根本上杜绝SQL注入的问题。

参数化查询的优势:

  1. 提高效率:之前说过,数据库在执行SQL的过程中,每次都会经过SQL的解析,编译,调用对应的数据库组件,这样如果执行多次同样类型的SQL语句,解析,编译的过程明显是在浪费资源,而参数化查询就是使用编译好的过程(也就是提前告诉数据库要调用哪些数据库组件),这样就跳过了对SQL语句的解析,编译过程,提高了效率(这个过程我觉得有点类似于C/C++语言的编译执行与脚本语言的解释执行)。
  2. 更加安全:从安全编程的角度来说,对于防范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中的使用方式。

  1. 使用“?”符将SQL语句中的条件值常量进行替换,组成一个新的SQL语句,比如上面登录的查询语句可以写成
select count(*) from user where username = ? and password = ?
  1. 调用ICommandText的SetCommandText设置sql语句。
  2. 调用ICommandParpare的Prepare方法对含有"?"的语句进行预处理
  3. 调用ICommandWithParameters方法的GetParameterInfo方法获取参数详细信息的DBPARAMINFO结构(类似于DBCOLUMNINFO)
  4. 分配对应大小的DBBINDING缓冲用来保存每个参数的绑定信息
  5. 调用IAccessor的CreateAccessor方法创建对应的访问器
  6. 为参数分配缓冲,设置合适的参数后准备DBPARAMS结构
  7. 调用ICommandText的Execute方法并将DBPARAMS结构的指针作为参数传入。
  8. 操作返回的结果集对象
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;
}

完整代码


本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏NetCore

[实录]解决Migrator.Net 小bug

好久没写了,平时比较忙,只能趁周末的时候,写一点小东西,自己也记录一下。 平时我们做项目的时候,都会有自己的数据访问层,为了能方便以后的升级,我们一般会抽象出数...

2345
来自专栏小尘哥的专栏

springboot之文件上传、图片预览(thymeleaf+layui)

1、上传 ①.使用spring的正常上传,文件存储路径为磁盘任意位置,可配置 ②.业务表中存附件id ③.前端使用Layui 2、预览 ①.使用nginx代理,...

2952
来自专栏MYSQL轻松学

load data部分字段的正确写法

在日常运维中,会经常遇到将文件中数据导入到数据库中,如果是.sql文件,直接采用source方式即可;如果是.csv、.txt文件,可以采用 load data...

1352
来自专栏大学生计算机视觉学习DeepLearning

新手 php连接数据库大概。简单过程浅析以及遇到的问题分析

1636
来自专栏有趣的Python

5- Flask构建弹幕微电影网站-项目分析、搭建目录及模型设计项目优化与模型设计

已上线演示地址: http://movie.mtianyan.cn 项目源码地址:https://github.com/mtianyan/movie_proj...

4905
来自专栏魏艾斯博客www.vpsss.net

MySQL 数据库类型从 InnoDB 转换为 MyISAM

魏艾斯博客有一个 wordpress 站点,有一天无意中发现数据库挺大的,可是这个站也就不到 10 篇文章,没道理这么大的数据库啊。然后开始查找原因,发现在 p...

51722
来自专栏北京马哥教育

alter table锁表,MySQL出现Waiting for table metadata lock的场景浅析及解决方案

在修改/增加表字段的时候,发现很慢, show processlist; 时, Waiting for table metadata lock 能一直锁很久。 ...

5468
来自专栏沃趣科技

其他混杂存储过程 | 全方位认识 sys 系统库

在上一篇《用于查看配置的存储过程 | 全方位认识 sys 系统库》中,我们介绍了sys 系统库中用于查看performance_schema配置信息的快捷存储过...

1393
来自专栏程序你好

MySQL数据库面试题和答案(一)

1853
来自专栏阿杜的世界

Java Web技术经验总结(十四)

这个语句如果只是查询前面几页,或者是表的数据量不大(小于10万),就没有问题,否则就会出现慢查询。参考文章:【MySQL】 性能优化之 延迟关联进行了优化。

681

扫码关注云+社区

领取腾讯云代金券