6月1日 00:06

SQLite 数据库怎么做安全防护才够用?

SQLite 的安全防护不能只盯着一条 PRAGMA key。它是嵌入式数据库,没有内置账号、角色和授权语句,也不会像服务端数据库那样替你隔离网络访问。真正的安全边界通常在三层:数据库文件谁能读、应用代码如何执行 SQL、密钥和备份怎么管理。只要数据库文件能被随便拷走,SQL 写得再漂亮也挡不住离线分析。

追问

SQLite 本身有没有用户权限控制?

没有传统意义上的 CREATE USERGRANTREVOKE,权限模型要放在应用层和文件系统层。SQLite 的定位是进程内数据库文件,优势是部署简单、依赖少,代价是它不负责多用户访问控制。取舍是服务端应用必须自己做鉴权,系统层面限制数据库文件只能被应用用户读取。常见踩坑是把 .db 文件放在 Web 静态目录、共享下载目录或移动端可直接导出的位置。

bash
chmod 600 app.db chown appuser:appuser app.db

数据库文件要不要加密?

如果数据库可能离开受控服务器,或者运行在桌面端、移动端、边缘设备上,加密就很有必要。常见方案是 SQLCipher,打开数据库后先设置密钥,再访问表。边界在于加密保护的是“文件被拷走后的离线读取”,不能阻止已经拿到应用进程权限的人通过正常接口读数据。密钥不要硬编码在仓库里,移动端应放 Keychain、Keystore,服务端应放环境变量或密钥管理系统。

sql
PRAGMA key = '从安全存储读取的密钥'; PRAGMA cipher_page_size = 4096; PRAGMA kdf_iter = 256000;

怎么防 SQL 注入?

所有外部输入都应该走参数化查询,不要用字符串拼接 SQL。SQLite 支持预编译语句,Python、Node、Go、Java 的驱动也都有绑定参数的能力。取舍是动态表名、排序字段这类位置不能直接绑定参数,需要用白名单映射。最常见的坑是 WHERE 条件用了参数化,但 ORDER BY、LIMIT 或表名又拼回去了,攻击面仍然存在。

python
cursor.execute("SELECT id, name FROM users WHERE email = ?", (email,)) allowed_sort = {"created": "created_at", "name": "name"} sort_col = allowed_sort.get(sort_key, "created_at") cursor.execute(f"SELECT id, name FROM users ORDER BY {sort_col} LIMIT ?", (limit,))

哪些 PRAGMA 配置值得开启?

foreign_keys 应该在每个连接打开后启用,否则外键约束可能不会按预期生效。secure_delete 可以让删除的数据被覆盖,适合包含敏感信息的库,但会带来额外写入成本。可用版本里建议关闭 trusted_schema,减少恶意 schema 影响应用自定义函数的风险。边界是 PRAGMA 只能补强数据库行为,不能替代权限设计、参数化查询和输入校验。

sql
PRAGMA foreign_keys = ON; PRAGMA secure_delete = ON; PRAGMA trusted_schema = OFF; PRAGMA journal_mode = WAL;

备份、WAL 和日志会泄露数据吗?

会,而且经常比主库更容易被忽略。SQLite 使用 WAL 模式时,除了 app.db,还会有 app.db-walapp.db-shm,备份和权限检查要把它们一起算进去。取舍是 WAL 并发读写体验更好,但文件数量增加,运维脚本不能只复制一个 db 文件。日志里也不要打印完整 SQL 参数,尤其是 token、手机号、身份证号和地址。

bash
sqlite3 app.db ".backup 'backup/app.db'" chmod 600 backup/app.db

SQLite 安全不是一个开关,而是文件权限、加密、参数化查询、约束、备份和密钥管理的组合。服务端项目优先守住文件和应用层权限,客户端项目优先减少敏感数据落盘。这样即使某一层出问题,也不至于让整个数据库裸奔。

标签:Sqlite