SQLite 注入

SQLite 注入(SQL Injection) 是指攻击者通过 恶意构造输入,绕过程序逻辑,执行非预期的 SQL 语句,从而 读取、修改、删除数据库数据,甚至 获取系统权限

虽然 SQLite 是嵌入式数据库,但 同样存在注入风险,尤其在动态拼接 SQL 时。


一、注入原理(以 SQLite 为例)

危险写法(拼接字符串)

name = "Alice'; DROP TABLE users; --"
sql = f"SELECT * FROM users WHERE name = '{name}'"
# 最终 SQL:
# SELECT * FROM users WHERE name = 'Alice'; DROP TABLE users; --'

执行后:

  1. 查询 Alice
  2. 删除整个 users 表!

二、常见注入类型

类型示例输入效果
经典注入' OR '1'='1登录绕过
注释注入admin'--注释掉后续条件
堆叠注入1; DROP TABLE users;执行多条语句
UNION 注入' UNION SELECT sqlite_version(), NULL--泄露版本
时间盲注' AND (SELECT sqlite_version() LIKE '3%') AND '1'='1判断版本

三、SQLite 特有注入点

特性注入风险
ATTACH DATABASE 'file.db' AS aux文件写入(需权限)
PRAGMA table_info(...)泄露表结构
sqlite_master读取所有表/视图/索引
LOAD_EXTENSION()加载恶意扩展(需启用)

示例:读取所有表名

' UNION SELECT name, sql FROM sqlite_master WHERE type='table'--

四、如何防御(重点!)

1. 使用参数化查询(预编译语句)最有效

Python 示例(sqlite3 模块)

import sqlite3

conn = sqlite3.connect('app.db')
cursor = conn.cursor()

name = "Alice'; DROP TABLE users; --"
# 正确:使用 ? 占位符
cursor.execute("SELECT * FROM users WHERE name = ?", (name,))

即使输入是恶意字符串,也被当做 纯文本,不会执行

其他语言

语言正确写法
Node.jsdb.get("SELECT * FROM users WHERE name = ?", [name])
JavaPreparedStatement pst = conn.prepareStatement("SELECT ... WHERE name = ?"); pst.setString(1, name);
PHP$stmt = $pdo->prepare("SELECT ... WHERE name = ?"); $stmt->execute([$name]);

2. 避免动态拼接 SQL

# 危险
sql = f"SELECT * FROM users WHERE name = '{user_input}'"

# 安全
cursor.execute("SELECT * FROM users WHERE name = ?", (user_input,))

3. 输入验证 + 白名单

import re

def is_valid_username(name):
    return bool(re.match("^[a-zA-Z0-9_]{3,20}$", name))

if not is_valid_username(name):
    raise ValueError("Invalid username")

4. 最小权限原则

  • 不要用 root 或高权限用户连接数据库
  • 生产环境 禁用 ATTACH DATABASE
  • 限制文件系统写入权限
-- 禁用危险函数(应用层控制)
PRAGMA secure_delete = ON;
-- 不要启用 load_extension

5. 使用 ORM(自动防注入)

ORM示例
SQLAlchemysession.query(User).filter(User.name == name).first()
PeeweeUser.get(User.name == name)
Django ORMUser.objects.get(name=name)

自动使用参数化查询


五、检测是否可注入(测试技巧)

# 1. 经典测试
' OR '1'='1
' OR 1=1--

# 2. 报错注入
' AND sqlite_version()--

# 3. 时间盲注
' AND (SELECT COUNT(*) FROM sqlite_master)>0 AND sleep(5)--

# 4. UNION 测试列数
' UNION SELECT 1--
' UNION SELECT 1,2--
' UNION SELECT 1,2,3--

六、真实案例分析

场景:登录绕过

-- 后端 SQL(错误)
SELECT * FROM users WHERE username = '{input}' AND password = '{pwd}'

-- 输入:
username: admin'--
password: anything

-- 最终 SQL:
SELECT * FROM users WHERE username = 'admin'--' AND password = '...'
→ 登录成功!

修复

cursor.execute(
    "SELECT * FROM users WHERE username = ? AND password = ?",
    (username, password)
)

七、SQLite 安全配置建议

-- 1. 生产环境关闭调试
PRAGMA query_only = ON;  -- 禁止写操作(只读模式)

-- 2. 加密数据库(SQLCipher)
-- 3. 限制文件权限
chmod 600 database.db

-- 4. 定期备份 + 版本控制

八、快速自检清单

检查项是否安全
使用 ? 或命名参数
动态拼接 SQL
输入未验证
使用 ORM
允许 ATTACH DATABASE⚠️
数据库文件可被外部访问

总结:防注入黄金法则

永远不要信任用户输入
永远不要拼接 SQL 字符串
永远使用参数化查询

# 正确模板
cursor.execute("SELECT ... WHERE col = ?", (value,))
# 或
cursor.execute("INSERT INTO t VALUES (?, ?)", (a, b))

需要我帮你:

  • 审计一段代码是否存在注入风险
  • 把动态 SQL 改写为参数化版本
  • 生成安全的用户登录/注册 SQL

把你的代码或 SQL 发我!

文章已创建 2326

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部