数字型注入和字符型注入的核心区别在于后端sql语句拼接时是否对输入值使用引号包裹,这直接决定了攻击者能否闭合字符串上下文。数字型注入更危险是因为它允许攻击者在无需闭合引号的情况下,直接在数值上下文中通过union操作或报错函数进行数据提取,且通常能绕过基于waf的简单引号检测规则。

为什么数字型注入不需要闭合引号
在数字型注入中,后端代码直接将用户输入拼接到SQL语句中,没有使用单引号或双引号包裹输入值。这意味着攻击者可以直接在输入中插入SQL关键字,而不需要担心字符串语法的闭合问题。例如,如果后端代码是 SELECT * FROM users WHERE id = $id,攻击者只需输入 1 OR 1=1 即可使条件恒真,无需像字符型注入那样先闭合引号再添加逻辑。

import sqlite3
def get_user_numeric(user_id):
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
# 假设 user_id 来自用户输入,未做过滤
query = f"SELECT * FROM users WHERE id = {user_id}"
try:
cursor.execute(query)
return cursor.fetchall()
except Exception as e:
return str(e)
# 模拟攻击:数字型注入
print(get_user_numeric("1 OR 1=1"))
字符型注入如何闭合引号
字符型注入发生在后端代码使用引号包裹用户输入时,如 SELECT * FROM users WHERE name = '$name'。攻击者必须首先闭合前端的引号,然后才能注入恶意SQL代码。例如,输入 ' OR '1'='1 可以闭合引号并使条件恒真。这种注入方式需要攻击者了解具体的引号类型(单引号、双引号等),并且可能受到后端编码或转义机制的影响。

import sqlite3
def get_user_string(name):
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
# 假设 name 来自用户输入,未做过滤
query = f"SELECT * FROM users WHERE name = '{name}'"
try:
cursor.execute(query)
return cursor.fetchall()
except Exception as e:
return str(e)
# 模拟攻击:字符型注入
print(get_user_string("' OR '1'='1"))
数字型注入如何利用UNION操作
数字型注入的一个显著优势是它可以轻松利用UNION操作来提取其他表的数据。由于不需要闭合引号,攻击者可以直接在输入中添加UNION SELECT语句,从而将查询结果合并到原始查询中。例如,输入 1 UNION SELECT username, password FROM admin_users 可以直接获取管理员的凭据。这种方式比字符型注入更直接,因为攻击者不需要处理复杂的引号嵌套问题。











