SQLite 防注入及单引号/双引号处理(C++)

8 篇文章 0 订阅
2 篇文章 0 订阅

1. CppSQLite 库

SQLite 是一个用c写的超轻量级的开源数据库;
CppSQLite3 是对SQLite进行二次封装后的C++类, 使用时需要依赖 sqlite3 库及 sqlite3.h;

在 windows 环境下使用这个类的时候,你需要确保几件事情:
1. 你要下载下面 5 个文件。 https://github.com/neosmart/CppSQLite
       CppSQLite3.h 和CppSQLite3.cpp
       sqlite3.h、sqlite3.lib 和 sqlite3.dll (windows环境)
2. 工程中引入 sqlite3.lib,最后要将CppSQLite3.h 和CppSQLite3.cpp添加到工程中 

2. 制作 CppSQLite3 动态库

// 制作 CppSQLite3 动态库
#ifdef XXXLIB_EXPORTS
#    define XXX_API  __declspec(dllexport)
#else
#    define XXX_API  __declspec(dllimport)
#endif

添加导出宏 XXX_API 及 [改进版]新增的 5 个函数

// CppSQLite3.h   
   //1. 类名前面增加导出符号 XXX_API
class XXX_API CppSQLite3Exception
class XXX_API CppSQLite3Buffer
class XXX_API CppSQLite3Binary
class XXX_API CppSQLite3Query
class XXX_API CppSQLite3Table
class XXX_API CppSQLite3Statement   // 这个是预编译,防注入用的
class XXX_API CppSQLite3DB

  // 2. CppSQLite3DB 类中增加 5 个函数
const char*   get_errormsg();
int           get_errorcode();
unsigned int  op_begin(const char *src_dbfile,const char *asdb);
unsigned int  op_append(const char *src_table,const char *dst_table,const char *key,const char *asdb);
unsigned int  op_end(const char *asdb);

 // 下面是对应的函数实现, 在 CppSQLite3.cpp 中增加
const char*  CppSQLite3DB::get_errormsg() {
    return sqlite3_errmsg(mpDB);
}

int  CppSQLite3DB::get_errorcode() {
    return  sqlite3_errcode(mpDB);
}

//
unsigned int CppSQLite3DB::op_begin(const char *src_dbfile,const char *asdb)
{
    // TODO: 判断 srcdbfile 是否文件存在    
    char sql[512];
    sprintf_s(sql,sizeof(sql),"detach database %s;",asdb);
    execDML(sql); // 分离数据库

    sprintf_s(sql, sizeof(sql),"attach DataBase '%s' as %s;",src_dbfile, asdb);
    int iError = execDML(sql);  // 附加数据库
    if ((iError) && (iError == get_errorcode()) ) {
        return -1;
    }

    iError = execDML("begin transaction;");
    return 0;
}

unsigned int CppSQLite3DB::op_append(const char *src_table, const char *dst_table, const char *key, const char *asdb)
{
    if (!tableExists(dst_table)) {
        return 0;
    }

    int iError = 0;
    char sql[512];
    if (key)
    {
        sprintf_s(sql,sizeof(sql),"delete from %s where %s.%s in (select %s from %s.%s);",dst_table, dst_table,key, key, asdb, src_table);
        iError = execDML(sql);
    }

    sprintf_s(sql,sizeof(sql),"insert into %s select A.* from %s.%s as A ;", dst_table, asdb, src_table);
    iError = execDML(sql);

    if (iError < 0) {
        return 0;
    }
    
    return iError;
}

unsigned int CppSQLite3DB::op_end(const char *asdb)
{
    char sql[64];
    sprintf_s(sql,sizeof(sql),"detach database %s;",asdb);  // 分离数据库
    int iError = execDML(sql);
    
    iError = execDML("commit transaction;");
    
    if (iError < 0) {
        return 0;
    }
    
    return iError;
}

3. sqlite 处理数据很慢的原因

sqlite在没有显式使用事务的时候会为每条 insert 都使用事务操作,而
sqlite数据库是以文件的形式存在磁盘中,就相当于每次访问时都要打开一次文件,
如果对数据进行大量的操作,时间都耗费在I/O操作上,所以很慢。

解决方法: 
显式使用事务的形式提交, 因为我们开始事务后,进行的大量操作的语句都保存在内存中,
当提交时才全部写入数据库,此时,数据库文件也就只用打开一次。

4. sqlite 处理单引号/双引号

错误处理方式:

//错误处理方式: 字符串拼接, 当 %s 本身包含 ' 或是 " 或是 一些注入攻击字符串时, 会出错.
CppSQLite3DB bsdb;
bsdb.open(file);
sprintf_s(dstsql,sizeof(dstsql),"insert into task_total (taskId,gdsId,taskName,taskBrief,taskDetail,beginTime,endTime) values (%llu,%u,'%s','%s','%s','%s','%s');",
(uint64)row[0], (DWORD)row[1], (const char*)row[2], (const char*)row[3], (const char*)row[4], (const char*)row[5],(const char*)row[6];
bsdb.execDML(dstsql);

正确处理方式:

//正确处理方式:借助 SQLite 的预编译 compileStatement 方法
char dstsql[1024]={0};
sprintf_s(dstsql, sizeof(dstsql), "INSERT INTO xxx (col1,col2,col3....) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);");
CppSQLite3Statement stmt = bsdb.compileStatement(dstsql);

char srcsql[1024]={0};
sprintf_s(srcsql,sizeof(srcsql),"SELECT **** FROM **** WHERE ****;");
mysqlpp::Query query = sqlconn->m_conn->query(srcsql);
mysqlpp::UseQueryResult res = query.use();
for (;res;)
{
    mysqlpp::Row row = res.fetch_row();
    if (!row) break;
	//进行绑定
	int i = 0;
	for (;i<6; ++i)
	stmt.bind(i+1, (const int)row[i]);// int	
	
	for (;i<8; ++i)
	stmt.bind(i+1, (const double)row[i]);// float/double
	
	for (;i<16; ++i)
	stmt.bind(i+1, (const char*)row[i]);// char*		
	
	stmt.execDML();
}

参考
mysqlpp 封装了C++对MySQL操作
CppSQLite3 封装了C++对SQLite操作
CppSQLite3Statement参数绑定
SQLite 基础教程 [事务]
SQLite 基础教程 [附加]
SQLite 基础教程 [分离]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值