Small. Fast. Reliable.
Choose any three.
防御黑暗艺术

1. SQLite总是验证其输入

SQLite绝不应该崩溃,溢出缓冲区,泄漏内存或表现出任何其他有害行为,即使出现恶意格式错误的SQL输入或数据库文件也是如此。SQLite应该始终检测错误的输入并引发错误,而不是崩溃或损坏内存。由SQL输入或数据库文件引起的任何故障都被认为是严重的错误,当引起SQLite开发人员的注意时,将立即予以解决。SQLite经过广泛的模糊测试,以帮助确保它能够抵抗这些类型的错误。

但是,仍然会发生错误。如果您正在编写将不受信任的SQL输入或数据库文件发送到SQLite的应用程序,则可以采取其他步骤来帮助减少攻击面并防止由未检测到的错误引起的零日攻击。

1.1。不受信任的SQL输入

接受不受信任的SQL输入的应用程序应采取以下预防措施:

  1. 设置SQLITE_DBCONFIG_DEFENSIVE标志。这样可以防止普通的SQL语句故意破坏数据库文件。SQLite应该证明不会同时受到恶意SQL输入和恶意破坏的数据库文件的攻击。但是,拒绝仅脚本攻击者访问损坏的数据库输入可提供额外的防御层。

  2. 减少SQLite对输入施加的限制。这可以帮助防止由于异常大的输入而导致的拒绝服务攻击和其他形式的恶作剧。您可以在编译时使用-DSQLITE_MAX _...选项执行此操作,也可以在运行时使用 sqlite3_limit()接口执行此操作。大多数应用程序可以在不影响功能的情况下极大地减少限制。下表提供了一些建议,尽管确切的值会根据应用程序而有所不同:

    极限设定默认值高安全价值
    LIMIT_LENGTH1,000,000,0001,000,000
    LIMIT_SQL_LENGTH1,000,000,000100,000
    LIMIT_COLUMN2,000100
    LIMIT_EXPR_DEPTH1,00010
    LIMIT_COMPOUND_SELECT5003
    LIMIT_VDBE_OP250,000,00025,000
    LIMIT_FUNCTION_ARG1278
    LIMIT_ATTACH100
    LIMIT_LIKE_PATTERN_LENGTH50,00050
    LIMIT_VARIABLE_NUMBER99910
    LIMIT_TRIGGER_DEPTH1,00010
  3. 考虑使用sqlite3_set_authorizer()接口来限制将要处理的SQL的范围。例如,不需要更改数据库架构的应用程序可以添加sqlite3_set_authorizer()回调,该回调导致任何CREATE或DROP语句失败。

  4. SQL语言功能非常强大,因此恶意SQL输入(或由应用程序错误引起的错误SQL输入)总是有可能提交运行很长时间的SQL。为了防止这种情况成为拒绝服务攻击,请考虑使用 sqlite3_progress_handler()接口在每个SQL语句运行时定期调用回调,并让该回调返回非零值以在语句运行时间过长时中止该语句。 。或者,在单独的线程中设置一个计时器,并在计时器关闭时调用sqlite3_interrupt(),以防止SQL语句永远运行。

  5. 使用sqlite3_hard_heap_limit64()接口限制SQLite将分配的最大内存量。这有助于防止拒绝服务攻击。要找出应用程序实际需要多少堆空间,请对典型输入运行该堆空间,然后使用sqlite3_memory_highwater()接口测量最大瞬时内存使用量 。将硬堆限制设置为观察到的最大瞬时内存使用量加上一些余量。

  6. 对于嵌入式系统,请考虑使用-DSQLITE_ENABLE_MEMSYS5选项编译SQLite, 然后通过sqlite3_configSQLITE_CONFIG_HEAP)接口为SQLite提供固定的内存块以用作其堆 。这将防止恶意SQL通过使用过多的内存来执行拒绝服务攻击。如果(例如)提供了5 MB的内存供SQLite使用,那么一旦消耗了这么多内存,SQLite将开始返回SQLITE_NOMEM错误,而不是占用应用程序其他部分所需的内存。这也将SQLite的内存沙箱化,以使应用程序其他部分的写后写错误不会对SQLite造成问题,反之亦然。

  7. 要在printf()SQL函数中控制内存使用,请使用“ -DSQLITE_PRINTF_PRECISION_LIMIT = 1000 ”或类似的较小值进行编译。此#define限制了printf()函数中%替换的宽度和精度,从而防止了敌对的SQL语句通过诸如“ printf('%1000000000s','hi') ”之类的结构消耗大量RAM 。

1.2。不受信任的SQLite数据库文件

读取或写入来源不确定的SQLite数据库文件的应用程序应采取以下预防措施。

即使应用程序不故意接受来自不受信任来源的数据库文件,也要提防更改本地数据库文件的攻击。为了获得最佳安全性,任何一个代理可能已经在其他安全域中写入过的任何数据库文件都应视为可疑文件。

  1. 如果应用程序包含任何具有副作用或可能泄漏特权信息的自定义SQL函数自定义虚拟表,则该应用程序应使用以下一种或多种技术来防止恶意制作的数据库模式秘密运行这些SQL函数和/或出于邪恶目的的虚拟表:

    1. 打开每个数据库连接后,立即调用sqlite3_db_config(db,SQLITE_DBCONFIG_TRUSTED_SCHEMA,0,0)。
    2. 打开每个数据库连接后,立即运行PRAGMAtrusted_schema = OFF语句。
    3. 使用-DSQLITE_TRUSTED_SCHEMA = 0编译时选项编译SQLite 。
    4. 通过设置禁止暗中使用自定义的SQL函数和虚拟表SQLITE_DIRECTONLY标志上所有的自定义SQL函数和SQLITE_VTAB_DIRECTONLY所有自定义虚拟桌旗。
  2. 如果应用程序不使用触发器或视图,请考虑通过以下方式禁用未使用的功能:

    sqlite3_db_config(db,SQLITE_DBCONFIG_ENABLE_TRIGGER,0,0);
    sqlite3_db_config(db,SQLITE_DBCONFIG_ENABLE_VIEW,0,0);
    

对于读取异常高风险的数据库文件,例如从远程计算机(可能是从匿名贡献者处接收到的数据库文件),可能需要采取以下额外的预防措施。但是,这些额外的防御措施会带来性能成本,因此可能并非在每种情况下都适用:

  1. 在打开数据库文件之后并在运行任何其他SQL语句之前,将数据库作为第一个SQL语句在数据库上 运行PRAGMA integrity_checkPRAGMA quick_check。拒绝并拒绝处理任何包含错误的数据库文件。

  2. 启用PRAGMA cell_size_check = ON设置。

  3. 不要启用内存映射的I / O。换句话说,请确保PRAGMA mmap_size = 0

2.总结

为了安全地将SQLite与可能具有敌意的输入一起使用,不需要上述预防措施。但是,它们确实为抵御零时差攻击提供了额外的防御层,并鼓励将数据从不受信任的来源传递到SQLite的应用程序受到鼓励。