本节摘要:SQLite 没有端口、没有账号体系,它的攻击面与 MySQL、PostgreSQL 完全不同:不防"网络上的陌生人",防的是"拿到文件的人"与"能发语句的代码路径"。本节按四个层面组织加固清单:语句层防注入、文件层做访问控制、存储层上加密扩展、schema 层守纪律,并对照三库的权限与加密模型。
进程内引擎的语句注入风险与 Web 场景同源:把用户输入拼进 SQL 字符串。区别在于后果半径——注入成功的代码运行在应用进程里,能读写的正是应用能读写的全部文件。防御在机制层面完成(第 3 章讲过的占位符):
/* 错误:拼接。输入 ' OR '1'='1 直接改写语义 */ char sql[512]; snprintf(sql, sizeof sql, "SELECT * FROM users WHERE name='%s'", input); /* 正确:占位符。输入永远是数据,不是代码 */ sqlite3_stmt *stmt; sqlite3_prepare_v2(db, "SELECT * FROM users WHERE name = ?1", -1, &stmt, 0); sqlite3_bind_text(stmt, 1, input, -1, SQLITE_TRANSIENT);
两个常被忽略的语句面风险。ATTACH:ATTACH DATABASE ?1 AS aux 让一条语句引入外部文件——任何能执行任意 SQL 的路径都能借此读写新文件。加固手段是用 sqlite3_set_authorizer 回调白名单化允许的语句类型与访问对象,或干脆禁止动态 SQL。PRAGMA 函数化:新版本允许 pragma_table_info() 等以表函数形态出现在查询里,schema 探测更容易了,配合只读连接使用是稳妥姿势。
SQLite 的访问控制就是文件系统的访问控制。这带来模型上的根本简化与几个必须执行的纪律:
-wal 与 -shm 的权限要与主文件一致——它们在检查点间隙承载最新数据,泄了伴生文件等于泄了库。sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY, 0) 让"误写"在接口层就被拒绝;配合文件层只读挂载双保险。对照三库:MySQL 与 PostgreSQL 把"谁能读哪行"做成精细的授权体系(账号、库、表、列级,行级策略),SQLite 把这件事完全交给文件系统——粒度粗到只有"能碰与不能碰",但边界清晰到没有旁路。单用户嵌入式场景里,这套粗粒度反而更难配错。
官方的分发包不含加密;需要静态加密时两条路:
PRAGMA key = '口令' 即启用,整库含 WAL 全加密,兼容性与官方构建一致。两个共同的工程要点:口令的传递与内存清零要纳入应用的安全设计(口令在进程内存里的残留是常见疏漏);加密页与页大小的关系——加密不改变 B-Tree 结构但可能调整每页有效载荷(预留校验与 IV 空间),建库前就要定好,中途启用加密等于换库。对照服务端:MySQL 的表空间加密与 PostgreSQL 的 TDE 变体同为静态加密,粒度是表空间或集群;SQLite 的加密粒度是整库,没有列级加密——敏感列的细粒度需求在应用层做字段级加密再入库存 BLOB。
最后一批暗门在 DDL 与配置习惯里:
PRAGMA trusted_schema = OFF; -- 3.31+:schema 里的对象不得调用任意应用函数 PRAGMA defensive = ON; -- 3.26+:阻止直接写 sqlite_schema 等内部表 -- 迁移脚本纪律:每个 DDL 变更走版本号迁移表,禁止运行期动态建表 -- 视图收敛:给不可信查询方只暴露视图,不暴露基表
trusted_schema 与 defensive 两个开关是官方对"嵌入式场景里 SQL 来自多方"这一现实的回应——前者的旧世界(默认信任 schema 内置函数)在恶意构造的数据库文件上可被利用为代码执行入口。对照服务端:PostgreSQL 对 public schema 的默认权限收紧走的是同一条安全演进路线,教训相通——schema 即代码,代码要 Review,运行期不改 schema。
**SQLite 适合存密码吗?**永远不要存明文密码,这一点与所选数据库无关。正确分层:口令本身用应用层的自适应哈希(bcrypt、argon2 这类)处理后入库,SQLite 只存哈希结果;字段加密解决的是"内容保密"而不是"认证"。SQLCipher 或 SEE 加密解决的是另一层——拿到数据库文件的攻击者连哈希都看不到,暴力破解无从下手。三层各司其职:哈希防库内泄露、整库加密防文件窃取、参数绑定防注入拼接。
**只读场景还有哪些防呆手段?**三道闸从软到硬。接口层 SQLITE_OPEN_READONLY 拒绝一切写语句;文件层把库文件设为只读权限,连引擎的写系统调用都会失败;WAL 模式的库不适合设文件只读(写事务的检查点需要碰主文件),只读分发件应先用 TRUNCATE 检查点收尾并切回 DELETE 模式再锁死——随应用分发的参考数据库大多走这条路。
安全收官,最后一节处理部署:从链接到升级的实战清单。