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

相关推荐
xcLeigh21 分钟前
网络安全 | 防火墙的工作原理及配置指南
安全·web安全
梦想平凡26 分钟前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
TianyaOAO36 分钟前
mysql的事务控制和数据库的备份和恢复
数据库·mysql
Ewen Seong1 小时前
mysql系列5—Innodb的缓存
数据库·mysql·缓存
码农老起1 小时前
企业如何通过TDSQL实现高效数据库迁移与性能优化
数据库·性能优化
安全小王子2 小时前
Kali操作系统简单介绍
网络·web安全
光路科技2 小时前
八大网络安全策略:如何防范物联网(IoT)设备带来的安全风险
物联网·安全·web安全
夏木~2 小时前
Oracle 中什么情况下 可以使用 EXISTS 替代 IN 提高查询效率
数据库·oracle
W21552 小时前
Liunx下MySQL:表的约束
数据库·mysql
黄名富2 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua