小巧、快速、可靠。
任選三項。
黑魔法防禦術

1. SQLite 永遠驗證其輸入

即使提供惡意格式錯誤的 SQL 輸入或資料庫檔案,SQLite 也絕不會崩潰、溢位緩衝區、洩漏記憶體或表現出任何其他有害行為。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. 考慮將 SQLITE_MAX_ALLOCATION_SIZE 編譯時間選項設定為小於其預設值 2147483391 (0x7ffffeff) 的值。視應用程式而定,100000000 (1 億) 甚至更小的值並非不合理。

  7. 對於嵌入式系統,請考慮使用 -DSQLITE_ENABLE_MEMSYS5 選項編譯 SQLite,然後透過 sqlite3_config(SQLITE_CONFIG_HEAP) 介面提供 SQLite 一個固定記憶體區塊作為其堆積。這將防止惡意 SQL 使用過多記憶體來執行阻斷服務攻擊。如果提供 (例如) 5 MB 的記憶體供 SQLite 使用,一旦消耗了這麼多記憶體,SQLite 將開始傳回 SQLITE_NOMEM 錯誤,而不是吸收應用程式其他部分所需的記憶體。這也會將 SQLite 的記憶體沙盒化,因此應用程式其他部分的寫入後釋放錯誤不會對 SQLite 造成問題,反之亦然。

  8. 若要控制 printf() SQL 函數 中的記憶體使用量,請使用「-DSQLITE_PRINTF_PRECISION_LIMIT=100000」或其他類似合理的值進行編譯。此 #define 會限制 printf() 函數中 %-替換的寬度和精度,進而防止惡意的 SQL 陳述式透過「printf('%1000000000s','hi')」等建構使用大量 RAM。

    請注意,SQLite 內部使用其內建的 printf() 來協助它格式化 sqlite_schema 表格 中的 sql 欄位。因此,沒有表格、索引、檢視或觸發器定義可以比精度限制大很多。您可以設定小於 100000 的精度限制,但請小心,無論您使用什麼精度限制,都必須至少與架構中最長的 CREATE 陳述式一樣長。

1.2. 不受信任的 SQLite 資料庫檔案

讀取或寫入來源不明的 SQLite 資料庫檔案的應用程式應採取以下列舉的預防措施。

即使應用程式並未故意接受來自不受信任來源的資料庫檔案,也要注意本地資料庫檔案遭到變更的攻擊。為了最佳安全性,任何可能曾經由不同安全性網域中的代理寫入的資料庫檔案都應視為可疑。

  1. 如果應用程式包含任何具有副作用或可能洩漏特權資訊的 自訂 SQL 函數自訂虛擬表格,則應用程式應使用下列一種或多種技術來防止惡意製作的資料庫架構暗中執行這些 SQL 函數和/或虛擬表格以進行不法目的

    1. 在每個 資料庫連線 開啟後立即對其呼叫 sqlite3_db_config(db,SQLITE_DBCONFIG_TRUSTED_SCHEMA,0,0)。
    2. 在每個資料庫連線開啟後立即執行 PRAGMA trusted_schema=OFF 陳述式。
    3. 使用 -DSQLITE_TRUSTED_SCHEMA=0 編譯時間選項編譯 SQLite。
    4. 透過設定所有自訂 SQL 函數的 SQLITE_DIRECTONLY 旗標,以及所有自訂虛擬表格的 SQLITE_VTAB_DIRECTONLY 旗標,來停用自訂 SQL 函數和虛擬表格的秘密使用。
  2. 如果應用程式不使用觸發器或檢視,請考慮使用下列方式停用未使用的功能:

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

對於讀取風險異常高的資料庫檔案,例如從遠端機器接收的資料庫檔案,以及可能來自匿名貢獻者的資料庫檔案,下列額外的預防措施可能是合理的。然而,這些額外的防禦措施會伴隨著效能成本,因此可能不適用於每個情況

  1. 在開啟資料庫檔案後,並在執行任何其他 SQL 陳述之前,以 PRAGMA integrity_checkPRAGMA quick_check 在資料庫上執行。拒絕並拒絕處理任何包含錯誤的資料庫檔案。

  2. 啟用 PRAGMA cell_size_check=ON 設定。

  3. 不要啟用記憶體對映 I/O。換句話說,請確保 PRAGMA mmap_size=0

2. 摘要

為了在潛在敵意的輸入下安全地使用 SQLite,上述預防措施並非必要。然而,它們確實提供了額外的防禦層,以對抗零時差攻擊,並鼓勵將資料從不受信任的來源傳遞到 SQLite 的應用程式。

此頁面最後修改於 2024-01-16 14:06:27 UTC