关于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注入,所以才把预编译拿上来说。

相关推荐
数据技术说1 分钟前
MySQL 和 PostgreSQL 的 CDC 方案到底有什么区别?
数据库·架构
SelectDB13 分钟前
灵犀科技统一数据服务平台:基于 Apache Doris / SelectDB 实现存储降本 60%、计算提效 10 倍
数据库
网安蟹佬霸14 分钟前
CTF Web方向解题全攻略:从信息收集到getshell(附实战payload与脚本)
android·前端·网络·安全·web安全·网络安全·网安
让学习成为一种生活方式29 分钟前
KMC 3.2.4 安装与使用--生信工具108
数据库
今天AI了吗30 分钟前
从聊天到委派:AI Agent 如何推进长期任务
数据库·人工智能·python·sql·rust
—Miss. Z—1 小时前
第11章 故障管理
网络·数据库·oracle
WA内核拾荒者1 小时前
WhatsApp账号运营SOP的可视化编排与流水线监控
大数据·数据库·windows
保密资质顾问说1 小时前
网络安全等级保护与涉密信息系统集成资质的区别及选型分析
运维·网络·经验分享·安全·web安全
名字还没想好☜2 小时前
Kubernetes StatefulSet 实战:为什么数据库不能用 Deployment 部署
数据库·云原生·容器·kubernetes·statefulset
网安蟹佬霸2 小时前
Webshell免杀与检测实战:从一句话木马到加密流量
android·安全·web安全·网络安全·黑客·网安·渗透测试·