Skip to content

sql-injection

SQL 注入是一种代码注入技术,用于攻击数据驱动的应用程序。当恶意 SQL 语句被插入到输入字段中以执行时(例如,将数据库内容转储给攻击者),就会发生 SQL 注入。它是最常见和最危险的 Web 应用程序漏洞之一。

当应用程序将用户提供的数据直接嵌入到 SQL 查询字符串中而没有进行适当的净化时,就会发生 SQL 注入。这使得攻击者可以跳出数据上下文,注入自己的 SQL 命令。

想象一个登录表单,应用程序像这样构建查询(伪代码示例):

username = get_user_input('username')
password = get_user_input('password')
query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "';"
execute_sql(query)

正常用户将输入 alice 作为用户名。查询变为:

SELECT * FROM users WHERE username = 'alice' AND password = 'some_password';

然而,攻击者可以在用户名字段中输入以下内容:

' OR '1'='1

应用程序将构造此恶意查询:

SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '...';

由于 '1'='1' 始终为真,WHERE 子句对每个用户都评估为真,攻击者可能无需密码即可登录为数据库中的第一个用户(通常是管理员)。

攻击者可以在用户名字段中输入此内容:

'; DROP TABLE users; --

生成的 SQL 将是:

SELECT * FROM users WHERE username = ''; DROP TABLE users; --' AND ...;

如果数据库允许执行多条语句,它将执行 SELECT,然后继续 删除整个 users 表。-- 开始一个注释,导致原始查询的其余部分被忽略。

防止 SQL 注入最有效的方法是 绝不 将用户输入直接连接到 SQL 字符串中。相反,请使用 预处理语句(也称为参数化查询)。

预处理语句分两个阶段工作:

  1. 1. 准备: 应用程序将 SQL 查询结构发送到数据库,其中包含用户提供值的占位符(?、$1 等)。数据库解析并编译此查询结构,不包含数据。
  2. 2. 执行: 应用程序随后单独发送用户提供的数据。数据库引擎将此数据插入到预编译的查询中,将其严格视为数据,而不是可执行代码。

预处理语句示例(Python 和 psycopg2)

Section titled “预处理语句示例(Python 和 psycopg2)”
import psycopg2
# 用户输入
user_name_input = "' OR '1'='1'" # 恶意输入
# 数据库连接
conn = psycopg2.connect(...)
cursor = conn.cursor()
# 1. 使用占位符准备语句(psycopg2 使用 %s)
sql = "SELECT * FROM users WHERE username = %s;"
# 2. 执行,将用户数据作为单独的参数传递
# 数据库驱动程序负责处理净化。
cursor.execute(sql, (user_name_input,))
# 数据库将恶意字符串视为要搜索的字面字符串,而不是要执行的代码。
# 查询未能找到用户,这是预期的结果。
results = cursor.fetchall()
  • 始终 使用带参数化查询的预处理语句。
  • 绝不 信任用户输入。将所有外部数据视为潜在的恶意数据。
  • 不要 尝试编写自己的净化或转义函数。这极其难以正确实现。请依赖数据库驱动程序提供的久经考验的函数。