SQLite - 注入
SQLite - 防止 SQL 注入
Section titled “SQLite - 防止 SQL 注入”SQL 注入(SQL Injection)是一种严重的安全漏洞,当应用程序将不可信的用户输入直接整合到其 SQL 查询中时,就会发生这种漏洞。恶意用户可以提供特制输入,使其脱离数据上下文并修改 SQL 查询本身,可能导致数据窃取、未经授权的访问、数据损坏或整个系统被攻破。
本章将教你预防这种攻击的现代标准方法。基本原则是:永远不要相信用户输入,永远不要通过将字符串与用户提供的数据拼接来格式化查询。
一个易受攻击的应用程序示例
Section titled “一个易受攻击的应用程序示例”让我们想象一个简单的 Python 脚本,它从数据库中查找用户。一种常见但危险且有缺陷的方法是使用 f-string 或字符串拼接来构建查询:
# 警告:此代码存在 SQL 注入漏洞。请勿使用。import sqlite3
# 用户输入(例如,来自 Web 表单)username_input = "' OR '1'='1' --"
conn = sqlite3.connect('example.db')cursor = conn.cursor()
# 漏洞在这里:将用户输入直接嵌入到查询字符串中。query = f"SELECT * FROM users WHERE username = '{username_input}' AND active = 1;"
print(f"Executing: {query}")# Executing: SELECT * FROM users WHERE username = '' OR '1'='1' --' AND active = 1;
cursor.execute(query)results = cursor.fetchall()
print("Results:", results) # 这很可能会返回表中的所有用户!在上面的示例中,攻击者提供了输入 ' OR '1'='1' --。当此输入插入到查询中时,WHERE 子句变为 WHERE username = '' OR '1'='1' -- ...。'1'='1' 条件始终为真,而 -- 开始一个注释,导致原始查询的其余部分被忽略。攻击者已成功绕过登录检查。
解决方案:参数化查询
Section titled “解决方案:参数化查询”防止 SQL 注入的正确和行业标准方法是使用参数化查询(也称为预处理语句)。这项技术将 SQL 命令与用户提供的数据分离。
通过参数化,你可以编写带有数据占位符(SQLite 中为 ?)的 SQL 查询。然后,将实际用户数据作为单独的参数提供给执行函数。数据库驱动程序(database driver)确保数据被严格视为值,而不是可执行代码。
安全示例 (Python)
Section titled “安全示例 (Python)”以下是使用 Python 的 sqlite3 库的先前脚本的安全、修正版本:
# 这是执行查询的正确且安全的方法。import sqlite3
username_input = "David" # 正常输入# username_input = "' OR '1'='1' --" # 恶意输入
conn = sqlite3.connect('example.db')cursor = conn.cursor()
# 1. SQL 查询使用变量占位符 (?)。query = "SELECT * FROM users WHERE username = ? AND active = 1;"
# 2. 用户输入作为单独的元组传递给 execute 方法。# 库处理安全替换。cursor.execute(query, (username_input,))
results = cursor.fetchall()
print("Results:", results) # 即使是恶意输入,这也会安全地查找不到用户并返回 []。当恶意输入提供给参数化查询时,数据库会查找名称为字面字符串 ' OR '1'='1' -- 的用户。由于没有用户具有此名称,查询会正确返回空结果,攻击因此被阻止。
安全示例 (使用 PDO 的现代 PHP)
Section titled “安全示例 (使用 PDO 的现代 PHP)”同样的原则也适用于其他语言。以下是使用 PDO (PHP Data Objects) 在现代 PHP 中实现的方法:
<?php// 这是现代 PHP 中正确且安全的方法。
$userInput = "' OR '1'='1' --"; // 恶意输入
try { $pdo = new PDO('sqlite:example.db'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 1. 使用占位符准备语句。 $stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username AND active = 1;');
// 2. 将用户输入绑定到占位符并执行。 $stmt->execute([':username' => $userInput]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($results); // 将正确返回一个空数组。
} catch (PDOException $e) { echo 'Connection failed: ' . $e->getMessage();}?>已弃用和不安全的方法
Section titled “已弃用和不安全的方法”较旧的教程可能会提到诸如 sqlite_escape_string() 函数或 PHP 中的 magic_quotes_gpc 等概念。这些都已弃用,不应使用。
手动转义输入容易出错且通常不完整,会留下安全漏洞。黑名单(Blocklisting)或白名单(allowlisting)字符也是一种有缺陷的策略,可能被聪明的攻击者绕过。**始终使用数据库驱动程序提供的参数化查询。**这是对抗 SQL 注入最简单、最稳健的防御措施。