关于sql注入预编译的思考

文章目录

SQL注入简单原理

是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。

预编译原理

将sql语句预先编译一次,后面用户输入的数据将参数化执行,不会再编译一次,更安全。

常见后端语言的预编译函数

PHP

在PHP中,PDO(PHP Data Objects)扩展提供了预编译语句的功能。PDOStatement对象表示一条预编译的SQL语句,可以通过PDO对象的prepare()方法创建。

php 复制代码
<?php  
try {  
    // 创建PDO实例  
    $pdo = new PDO("mysql:host=localhost;dbname=database_name", "username", "password");  
  
    // 预编译SQL语句  
    $stmt = $pdo->prepare("SELECT * FROM employees WHERE age > :age");  
  
    // 绑定参数值  
    $stmt->bindParam(':age', $age, PDO::PARAM_INT);  
    $age = 30;  
  
    // 执行查询并获取结果集  
    $stmt->execute();  
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);  
  
    // 处理结果集  
    foreach ($results as $row) {  
        echo $row['name'] . "\n";  
    }  
} catch (PDOException $e) {  
    echo "Error: " . $e->getMessage();  
}  
?>

JAVA

在Java中,JDBC(Java Database Connectivity)提供了预编译语句(PreparedStatement)的功能。PreparedStatement是Statement的一个子接口,它代表了一条预编译的SQL语句。使用PreparedStatement可以显著提高数据库操作的性能,并防止SQL注入攻击。

java 复制代码
import java.sql.Connection;  
import java.sql.DriverManager;  
import java.sql.PreparedStatement;  
import java.sql.ResultSet;  
  
public class PrecompiledExample {  
    public static void main(String[] args) {  
        try {  
            // 创建数据库连接  
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/database_name", "username", "password");  
  
            // 创建预编译语句  
            String sql = "SELECT * FROM employees WHERE age > ?";  
            PreparedStatement preparedStatement = connection.prepareStatement(sql);  
  
            // 设置参数值  
            preparedStatement.setInt(1, 30);  
  
            // 执行查询并获取结果集  
            ResultSet resultSet = preparedStatement.executeQuery();  
  
            // 处理结果集  
            while (resultSet.next()) {  
                // 处理每一行数据  
                System.out.println(resultSet.getString("name"));  
            }  
  
            // 关闭资源  
            resultSet.close();  
            preparedStatement.close();  
            connection.close();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

Python

在Python中,常见的数据库操作库如MySQL Connector/Python提供了预编译功能。这通常通过prepare()方法实现,该方法会解析和优化SQL语句,并将其保存在数据库中以供后续使用。

python 复制代码
import mysql.connector  
  
# 创建数据库连接  
cnx = mysql.connector.connect(user='username', password='password', host='localhost', database='database_name')  
  
# 创建游标对象  
cursor = cnx.cursor()  
  
# 预编译SQL语句  
stmt = cursor.prepare("SELECT * FROM employees WHERE age > %s")  
  
# 执行预编译的SQL语句,并传入参数  
stmt.execute((30,))  
  
# 获取查询结果  
results = stmt.fetchall()  
  
# 输出查询结果  
for row in results:  
    print(row)  
  
# 关闭游标和连接  
cursor.close()  
cnx.close()

预编译的安全性

预编译可以有效防止sq注入,主要原理是将sql语句预先编译一次,用户无论输入什么都只会被当成参数执行,而不会将sql语句闭合,构造新的sql语句攻击

例外

预编译不能完全防止sql注入攻击,追究其根本原因是有些地方的sql语句无法被参数化,就算预编译后,仍存在能被闭合的地方。参考一下微信这位作者的文章。
预编译真的能完美防御SQL注入吗?

看完作者的描述,我们知道了无法被参数化的地方只有几种

  1. 表名、列名
  2. order by、group by
  3. limit
  4. join
  5. ...

探讨一下这些地方为什么不能被参数化,这里在网上冲浪的时候读到了另外一片作者的文章,可以很好的解释一下为什么这些地方不能被参数化。
PDO预编译与sql注入

在作者的文章中可以看出,例如order by语句中,预编译会自动给你追加引号,把你的输入给参数化。而order by这条语句如果把参数自动追加引号的话就会造成语法错误,查询出错。所以这个地方无法被预编译,或者说是假预编译。

作者在文章中有一句话说的很好:没有参数绑定的预编译等于没有预编译。

(题外话)为啥预编译会出现这种例外呢?

开发之初预编译可能就不是让你用作保护数据库安全的一种手段,更多的,预编译是一种减少数据库查询时间,减少语法树构成的一种方法。顺带的只是我们认为预编译通过把sql语句参数化执行可以有效的防止sql注入,所以才把预编译拿上来说。

相关推荐
Mephisto.java27 分钟前
【大数据学习 | HBASE高级】hbase-phoenix 与二次索引应用
大数据·sql·oracle·sqlite·json·hbase
only-lucky34 分钟前
QT之QML从入门到精通(第七章)
java·数据库·qt
Mephisto.java1 小时前
【大数据学习 | flume】flume之常见的source组件
大数据·sql·oracle·json·database·flume
m0_637146931 小时前
如何在FastAPI中灵活使用路径参数、查询参数和请求体参数进行接口设计
数据库·oracle·fastapi
靖顺1 小时前
【OceanBase 诊断调优】—— OceanBase 数据库统计信息被禁用,状态为 broken 的原因和解决方法
数据库·oceanbase
夜光小兔纸1 小时前
Oracle 单机及 RAC 环境 db_files 参数修改
数据库·oracle
帝恩思科技1 小时前
网络游戏安全现状及相关应对方案
运维·服务器·网络·安全·web安全
可怜的Tom被玩弄于股掌之中1 小时前
Bugku CTF_Web——字符?正则?
网络·安全·web安全·网络安全
骑鱼过海的猫1232 小时前
【redis】redis
java·数据库·redis