关于sql注入预编译的思考

SQL注入简单原理

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

预编译原理

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

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

PHP

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

<?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注入攻击。

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语句,并将其保存在数据库中以供后续使用。

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

探讨一下这些地方为什么不能被参数化,这里在网上冲浪的时候读到了另外一片作者的文章,可以很好的解释一下为什么这些地方不能被参数化。
PDO预编译与sql注入
在作者的文章中可以看出,例如order by语句中,预编译会自动给你追加引号,把你的输入给参数化。而order by这条语句如果把参数自动追加引号的话就会造成语法错误,查询出错。所以这个地方无法被预编译,或者说是假预编译。
作者在文章中有一句话说的很好:没有参数绑定的预编译等于没有预编译。

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值