セキュリティ上の脆弱性を発見した場合は、公開Issueを立てず、security@disnana.com に直接ご連絡ください。
現在、セキュリティアップデートを提供しているバージョンは以下の通りです。
| バージョン | サポート状況 |
|---|---|
| >= v1.4.0 | ✅ |
| < v1.4.0 | ❌ |
深刻度: 重大 (Critical)
概要: 旧バージョンでは、NanaSQLite クラスの初期化時に渡す table 引数が適切にサニタイズされていませんでした。攻撃者は悪意のあるテーブル名(例: "data; DROP TABLE users; --")を渡すことで、任意のSQLコマンドを実行できました。
修正内容: __init__ および table() メソッドで _sanitize_identifier を使用し、英数字とアンダースコアのみを許可するよう実装しました。不正な識別子には NanaSQLiteValidationError を送出します。
深刻度: 高 (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値が誤って「値なし」と解釈されないように変更しました。
深刻度: 重大 (Critical)
概要: APSW はセミコロン区切りの複数文を一度に実行するため、カラム型定義を通じて任意のSQLを実行可能でした。
修正内容: セミコロン (;)、ラインコメント (--)、ブロックコメント (/*) を含む文字列を拒否するバリデーションを追加しました。
深刻度: 高 (High)
概要: NanaSQLite.__setitem__ の before_write フック呼び出しと、NanaSQLite.__delitem__ の before_delete フック呼び出しがロックの外側で実行されていたため、マルチスレッド環境ではチェックと実処理の間に別スレッドが割り込める TOCTOU 競合がありました。その結果、UniqueHook の一意性チェックをすり抜けて重複値を書き込んだり、削除前提の整合性チェックが競合により破られたりする可能性がありました。
修正内容: 非 v2 モードにおいて、before_write と before_delete の呼び出しをどちらも _acquire_lock() コンテキスト内に移動し、フック実行と書き込み・削除処理を同一のロック保護下で行うようにしました。self._lock は threading.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-GCM、ChaCha20Poly1305、Fernetをサポートします。
\pagebreak
If you discover a security vulnerability, please do NOT open a public issue. Instead, contact us at security@disnana.com.
We currently provide security updates for the following versions:
| Version | Supported |
|---|---|
| >= v1.4.0 | ✅ |
| < v1.4.0 | ❌ |
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.
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 storedNonevalues. __contains__now performs a dedicated existence query without mutating the in-memory caches, avoiding inconsistent cache state while correctly handling explicitly storedNonevalues in combination with the_NOT_FOUNDsentinel.
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 (/*).
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.
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 thegoogle-re2engine (linear-time complexity guarantee). - Encryption at Rest: Data-at-rest encryption via
AES-GCM,ChaCha20Poly1305, andFernetthrough thecryptographylibrary.