Skip to content

Security: disnana/NanaSQLite

SECURITY.md

セキュリティポリシー (Japanese)

脆弱性の報告

セキュリティ上の脆弱性を発見した場合は、公開Issueを立てず、security@disnana.com に直接ご連絡ください。

サポート対象バージョン

現在、セキュリティアップデートを提供しているバージョンは以下の通りです。

バージョン サポート状況
>= v1.4.0
< v1.4.0

既知の脆弱性・修正済み問題

1. table 引数経由のSQLインジェクション(v1.3.4 で修正済み)

深刻度: 重大 (Critical)

概要: 旧バージョンでは、NanaSQLite クラスの初期化時に渡す table 引数が適切にサニタイズされていませんでした。攻撃者は悪意のあるテーブル名(例: "data; DROP TABLE users; --")を渡すことで、任意のSQLコマンドを実行できました。

修正内容: __init__ および table() メソッドで _sanitize_identifier を使用し、英数字とアンダースコアのみを許可するよう実装しました。不正な識別子には NanaSQLiteValidationError を送出します。

2. None 値保存時のデータ消失(v1.3.4 で修正済み)

深刻度: 高 (High) ― データ整合性

概要: None(JSON null)を明示的に保存した場合(db["key"] = None)、データはSQLiteに正しく書き込まれます。しかし DB 再オープン後の遅延ロード時に、__contains__ が値をキャッシュしないまま _cached_keys にキーを登録していたため、その後の __getitem__ でキーが「値なし」と判定され KeyError が発生し、データが消失したように見えました。

修正内容:

  • _read_from_db_NOT_FOUND センチネルを導入して「キー不在」と「値が None」を区別しました。
  • __contains__ が SQLite に対して SELECT 1 ... LIMIT 1 を発行し、このセンチネルを用いてキーの存在判定を行うことで、None 値が誤って「値なし」と解釈されないように変更しました。

3. create_table() のカラム型に対するSQLインジェクション脆弱性(v1.4.0 で修正済み)

深刻度: 重大 (Critical)

概要: APSW はセミコロン区切りの複数文を一度に実行するため、カラム型定義を通じて任意のSQLを実行可能でした。

修正内容: セミコロン (;)、ラインコメント (--)、ブロックコメント (/*) を含む文字列を拒否するバリデーションを追加しました。

4. UniqueHook の TOCTOU 競合状態(v1.5.4 で修正済み)

深刻度: 高 (High)

概要: NanaSQLite.__setitem__before_write フック呼び出しと、NanaSQLite.__delitem__before_delete フック呼び出しがロックの外側で実行されていたため、マルチスレッド環境ではチェックと実処理の間に別スレッドが割り込める TOCTOU 競合がありました。その結果、UniqueHook の一意性チェックをすり抜けて重複値を書き込んだり、削除前提の整合性チェックが競合により破られたりする可能性がありました。

修正内容: 非 v2 モードにおいて、before_writebefore_delete の呼び出しをどちらも _acquire_lock() コンテキスト内に移動し、フック実行と書き込み・削除処理を同一のロック保護下で行うようにしました。self._lockthreading.RLock のため、フック内からの再入呼び出しでもデッドロックは発生しません。v2 モードでの厳格な一意制約には SQLite UNIQUE 制約の使用を推奨します。

組み込みのセキュリティ機能

NanaSQLite は複数のセキュリティ層を備えています(v1.2.0以降)。

  • 厳格なSQL検証 (Strict SQL Validation): 未許可のSQL関数の実行を防止します。
  • ReDoS対策 (ReDoS Protection): SQL句の最大文字数制限により過負荷攻撃を防ぎます。また pip install nanasqlite[re2]google-re2(線形時間保証)を使用可能です。
  • 保存データの暗号化 (Encryption at Rest): cryptography ライブラリによる AES-GCMChaCha20Poly1305Fernet をサポートします。

\pagebreak

Security Policy (English)

Reporting a Vulnerability

If you discover a security vulnerability, please do NOT open a public issue. Instead, contact us at security@disnana.com.

Supported Versions

We currently provide security updates for the following versions:

Version Supported
>= v1.4.0
< v1.4.0

Known Vulnerabilities & Fixed Issues

1. SQL Injection via table Parameter (Fixed in v1.3.4)

Severity: Critical

Description: In older versions, the table parameter in the NanaSQLite class initialization was not properly sanitized. An attacker could execute arbitrary SQL commands by passing a malicious table name (e.g., "data; DROP TABLE users; --").

Fix: _sanitize_identifier is now applied to the table argument during __init__ and table() calls, allowing only alphanumeric characters and underscores. Invalid identifiers raise NanaSQLiteValidationError.

2. Data Loss on None Value Storage (Fixed in v1.3.4)

Severity: High (Data Integrity)

Description: When explicitly saving None (JSON null) as a value (db["key"] = None), the value was correctly persisted to SQLite. However, upon lazy loading after reopening the database, __contains__ registered the key in _cached_keys without loading the value into _data. A subsequent __getitem__ call found the key in _cached_keys but not in _data, causing a KeyError and apparent data loss.

Fix/Mitigation:

  • A sentinel object (_NOT_FOUND) was introduced in the internal read mechanism to accurately distinguish between missing records and explicitly stored None values.
  • __contains__ now performs a dedicated existence query without mutating the in-memory caches, avoiding inconsistent cache state while correctly handling explicitly stored None values in combination with the _NOT_FOUND sentinel.

3. SQL Injection via Column Type in create_table() (Fixed in v1.4.0)

Severity: Critical

Description: Because APSW executes semicolon-separated multi-statement strings in a single call, arbitrary SQL could be injected through column type definitions passed to create_table().

Fix: Added validation that rejects strings containing semicolons (;), line comments (--), and block comments (/*).

4. TOCTOU Race Condition in UniqueHook (Fixed in v1.5.4)

Severity: High

Description: NanaSQLite.__setitem__ called before_write hooks outside the _acquire_lock() context, and NanaSQLite.__delitem__ called before_delete hooks outside the lock as well. In a multi-threaded environment, two threads could simultaneously pass the UniqueHook uniqueness check and both write the same unique-field value, bypassing the constraint (Time-of-Check Time-of-Use race). Similarly, a pre-delete consistency check could be invalidated by a concurrent write before the deletion completed.

Fix: In non-v2 mode, both the before_write invocation in __setitem__ and the before_delete invocation in __delitem__ are now inside the _acquire_lock() block, making the hook check and the DB write/delete atomic. self._lock is a threading.RLock, so reentrant calls from hooks (e.g., db.items()) do not deadlock. For strict uniqueness in v2 mode, use SQLite UNIQUE constraints.

Built-in Security Features

NanaSQLite is designed with multiple security layers (v1.2.0+):

  • Strict SQL Validation: Prevents execution of unauthorized SQL functions.
  • ReDoS Protection: Maximum clause length limits prevent regex denial-of-service attacks. Install pip install nanasqlite[re2] to enable the google-re2 engine (linear-time complexity guarantee).
  • Encryption at Rest: Data-at-rest encryption via AES-GCM, ChaCha20Poly1305, and Fernet through the cryptography library.

There aren’t any published security advisories