qt SQL注入攻击与事务

一、QSqlQuery

        在QT中,QSqlQuery用来执行sql语句操作,通常使用一下两个函数来进行相关的操作:

1.使用给定的数据库驱动执行对应的sql语句

explicit QSqlQuery(const QString& query=QString(), QSqlDatabase db=QSqlDatabase());

    

2.使用给定的数据库创建QSqlQuery对象,但不执行任何操作

explicit QSqlQuery(QSqlDatabase db);

         下面给出一段代码更详细,直观的介绍上面两个函数:

//1. 使用第一种 假定数据库已经打开 对象为db
QSqlQuery query("select * from user",db);
//query会直接执行查询操作
while(query.next())
{
    ...
}


//2. 使用第二种 
QSqlQuery query(db);
QString sql = "select * from user";
//query不会执行查询操作 需要query.exec(sql)才会执行查询操作
query.exec(sql);
while(query.next())
{
    ...
}

二、SQL注入攻击

        什么时SQL注入攻击?即攻击者在进行一些sql语句操作时,构建特殊的输入作为参数传入,(SQL语法里的一些组合),从而欺骗数据库执行操作得到非法授权的数据。其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。

        常见的SQL注入攻击构建的一条语句为: 'OR '1=1。如:

//假如传入的username = bob 'OR'1=1

QString sql = "select * from user where username =' " + username + "'";

//拼接后的语句

sql = "select *from user where username = ' bob ' OR ' 1=1' ";

//这样 1=1这个条件一直为真,sql语句一执行,就会把user表中所有数据打印出来

 那怎么避免SQL注入攻击了?

        使用prepared Query。主要步骤如下:

// username = bob' or '1=1
QString sql = "select * from user where usename = :username";
// 1.创建QSqlQuery对象
QSqlQuery query(db);
// 2.使用prepared语句 自动解析sql语句
query.prepared(sql);
// 3.使用bindValue替换占位符
query.bindValue(":username",username);
// 4.执行query操作
query.exec();
while(query.next())
{
    ...
}

注意:1.占位符,在QT中,可以使用 :+ 任意字符串作为占位符的形式

           2.bindValue()执行后,默认给字符串加上了引号''

三、使用占位符遇到的一个问题 

        在使用占位符的操作时,遇到一个问题,虽然这个问题有点搞笑,但还是分享一下,希望各位在用到时,可以引以为鉴。

代码如下:

 
//首先创建数据驱动
    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    //基本的设置
    db.setHostName("127.0.0.1");
    db.setPort(3306);
    db.setUserName("root");
    db.setPassword("1234");
    db.setDatabaseName("qt_db");
 
    //打开数据库 建立连接
    bool isok = db.open();
 
    if(isok)
    {
        qDebug() << "success" ;
        QString aa = "%o%";
        QString sql = "SELECT * FROM user WHERE username LIKE:match";
        QSqlQuery query;
        query.prepare(sql);
        query.bindValue(":match", aa);
        if(!query.exec(sql))
        {
            qDebug() << query.lastError().text();
        }
        while(query.next())
        {
            qDebug() << QString("Id: %1, Username: %2, Password: %3")
                                .arg(query.value("id").toInt())
                                .arg(query.value("username").toString())
                                .arg(query.value("password").toString());
        }
        return 0;
    }else
    {
        qDebug() <<"fail " << db.lastError();
        return -1;
    }

各位看出什么问题了吗?在上面执行了一个模糊查询,但在运行代码的时候,就会打印下面的错误:

success
"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ':match' at line 1 QMYSQL: Unable to execute query"

其实,这里问题也很简单,就是query.exec()。这query.exec(sql),会执行sql中的sql语句,而在此之前,已经声明了使用prepared来解析sql语句,所以这就造成了冲突,解析的语句并没有被执行,反而执行了原语句sql。所以,只要记住,使用了prepared后,query.exec()不需要再加任何参数。

四、QT 事务 

        事务,是并发控制的单位。是用户定义的一个操作序列,这些操作要么都执行,要么都不执行,是一个不可分割的整体。通过事务,能将逻辑相关的一组操作绑定在一起,保证了数据的完整性。

        在QT中,事务相关的函数如下

1.查询是否支持事务:bool QSqlDriver::hasFeature(DriveFeature feature) const

2.开启事务: bool QSqlDatabase::transaction()

3.提交事务: bool QSqlDatabase::commit()

4.回滚事务: bool QSqlDatabase::rollback()

        具体代码如下:

//已有QSqlDatabase对象db
bool isOK = db.driver()->hasFeature(QSqlDriver::Transcations);
//如果支持事务
if(isOK)
{

   //打开事务
   db.transaction();

   QSqlQuery query1("insert into user (id,username,password) values (1,'peter','123')",db);
   QSqlQuery query2("insert into user (id,username,password) values (4,'andy','xxx')",db);
    
   ...
   if(query1.exec() && query2.exec())
   {
    db.commit();
   }
   else
   {
    db.rollback();
   }

}

        执行上面代码 ,由于原表中已经存在id=1的情况,所以query1执行失败,query2执行成功。但是,更新数据库发现,两个数据都没用更新在表中。

        使用事务,能在处理大规模数据时,提高效率和性能。以下是针对网上的两个问题进行的测试,可以酌情采纳。

        1. 开启事务、数据库更新、提交事务|回滚事务不在同一个函数是否可行

            测试代码如下:

void MainWindow::openTransaction(const QString &connectionName)
{
   QSqlDatabase db = getConnectionByName(connectionName);
   db.transaction();
   comRollTransaction(connectionName);
}

int MainWindow::updateDatabase(QSqlDatabase database)
{

    QSqlQuery query1("insert into user (id,username,password) values (1,'peter','123')",database);
    QSqlQuery query2("insert into user (id,username,password) values (4,'andy','xxx')",database);

    if(query1.exec() && query2.exec())
    {
        //如果两个query都执行成功,就提交事务
        return 0;
    }
    else
    {
        qDebug() << query1.lastError().text();
        qDebug() << query2.lastError().text();
        return -1;
    }

}

void MainWindow::comRollTransaction(const QString &connectionName)
{
    QSqlDatabase db = getConnectionByName(connectionName);
    int rs = updateDatabase(db);
    if(rs == 0)
    {
        db.commit();
    }else
    {
        db.rollback();
    }
}

测试结果和上面案例一样。由此可见,开启事务、数据库更新、提交事务|回滚事务不在同一个函数是可行的,因为事务是面向数据库连接的

        2.要向 user 表里插入 100000 个用户:1.开启事务每次插入 1000 个2.不使用事务插入 1 个

           测试代码如下:

QSqlDatabase db = getConnectionByName(connectionName);
    //默认的用户名
    QString name = "xxx";

    qDebug() << "当前时间:" <<QString::number(QDateTime::currentMSecsSinceEpoch());

    for(int i = 0; i < 100; i++)
    {
        QString sql = "insert into user (id,username,password) values (:userid,:namem,:word)";
        QSqlQuery query(db);
        query.prepare(sql);
        query.bindValue(":userid",QString("%1").arg(i+4));
        query.bindValue(":namem",name);
        query.bindValue(":word",QString("123%1").arg(i));

        if(!query.exec())
        {
            qDebug() <<"error";
        }
    }


//    db.transaction();
//    int count = 0;
//    for(int i = 0; i < 100; i++)
//    {
//        QString sql = "insert into user (id,username,password) values (:userid,:namem,:word)";
//        QSqlQuery query(db);
//        query.prepare(sql);
//        query.bindValue(":userid",QString("%1").arg(i+4));
//        query.bindValue(":namem",name);
//        query.bindValue(":word",QString("123%1").arg(i));

//        if(!query.exec())
//        {
//            db.rollback();
//        }

//        count++;

//        //对1000求余
//        if( i % 10 == 0)
//        {
//            db.commit();
//            db.transaction();
//        }

//    }

//    //如果还有剩余的插入,则提交剩下的事务
//    if(count % 10 != 0)
//    {
//        db.commit();
//    }

//    db.close();

    qDebug() << "当前时间:" <<QString::number(QDateTime::currentMSecsSinceEpoch());

由于100000条数据太大,这里改用100条数据。当使用一个一个数据插入时,耗时大概在387ms,而使用事务时,耗时在83ms,由此可见,事务的作用是非常大的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值