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

相关推荐
寂寞旅行5 小时前
向量数据库Milvus的使用
数据库·milvus
闻哥5 小时前
Redis事务详解
java·数据库·spring boot·redis·缓存·面试
道亦无名6 小时前
aiPbMgrSendAck
java·网络·数据库
l1t7 小时前
duckdb polyglot 插件的使用
sql·duckdb
面向对象World9 小时前
正点原子Mini Linux 4.3寸800x480触摸屏gt115x驱动
linux·服务器·数据库
dinga198510269 小时前
mysql之联合索引
数据库·mysql
ShoreKiten9 小时前
ctfshowweb361--一道题从0入门SSTI模板注入
web安全·flask·ssti·ctfshow
微风中的麦穗9 小时前
【SQL Server 2019】企业级数据库系统—数据库SQL Server 2019保姆级详细图文下载安装完全指南
大数据·数据库·sqlserver·云计算·个人开发·运维必备·sqlserver2019
zjttsh10 小时前
MySQL加减间隔时间函数DATE_ADD和DATE_SUB的详解
android·数据库·mysql
顾北1210 小时前
SpringCloud 系列 04:Gateway 断言 / 过滤器 / 限流 一站式落地指南
java·开发语言·数据库