software-data-integrity-failures について
FastAPI と Flask アプリケーションのソフトウェアおよびデータ完全性リスクを調べる、Python 開発者向けのセキュリティ監査スキルです。安全でないデシリアライズ、マスアサインメント、未検証の外部リソースや更新など、実行時の脅威を扱います。
主な機能
- 01危険なデシリアライズパターンの検出
- 02マスアサインメントのリスク確認
- 03外部アセットの SRI 確認
- 04更新とダウンロードの検証監査
- 05テストチェックリストとスキャンコマンド
活用シーン
- 01Flask または FastAPI のコードベース監査
- 02信頼できないデータのデシリアライズ確認
- 03外部スクリプトの整合性ハッシュ確認
- 04更新ワークフローへの整合性検証追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ scholarly360/ owasp-top10-web-skills --skill software-data-integrity-failuresインストール後、Claude Code で/software-data-integrity-failuresを入力して読み込まれたか確認します。
同梱ファイルreferences/deserialization-exploits.md
詳細
A08:2025 — ソフトウェアまたはデータの整合性の不備
概要
OWASP ランク: #8(2025) | 対象 CWE: 14 | 平均発生率: 2.75%
このカテゴリーでは、自分の環境内にあるソフトウェア、コード、データのアーティファクトの整合性を検証できていないことに焦点を当てます。これは、上流のサプライチェーンに焦点を当てる A03 とは異なります。中心となる懸念は、読み込み、デシリアライズ、実行するものを信頼できるかという点です。
A03(サプライチェーン)との主な違い:
- A03 = 上流の整合性(依存関係、CI/CD パイプライン)
- A08 = 実行時の整合性(実行時に実際にデシリアライズ、インクルード、自動更新するもの)
Python における重大なリスク領域
1. 安全でないデシリアライズ — Python 固有の最大のリスク
信頼できない入力に対して pickle.loads() を使うと、__reduce__ を介した任意コード実行につながります。これは Python における A08 のベクトルとして、最も危険なものです。
注意すべき危険な関数/モジュール:
| モジュール/関数 | リスク | 安全な代替手段 |
|---|---|---|
pickle.loads() / pickle.load() | __reduce__ を介した RCE | JSON + Pydantic |
cPickle.loads() | pickle と同様 | JSON + Pydantic |
dill.loads() | pickle の上位互換で、同じ RCE リスク | JSON + Pydantic |
jsonpickle.decode() | Python オブジェクトをデシリアライズ | json.loads() のみ |
shelve.open() | pickle を使用 | Redis/SQL ストア |
yaml.load(data, Loader=None) | !!python/object を介したコード実行 | yaml.safe_load() |
numpy.load(f, allow_pickle=True) | pickle 化された配列を介した RCE | allow_pickle=False |
torch.load(f) | デフォルトで pickle を使用 | weights_only=True |
検出パターン(静的解析):
# Flag any of these patterns in source code
DANGEROUS_PATTERNS = [
r'pickle\.loads?\(',
r'cPickle\.loads?\(',
r'dill\.loads?\(',
r'jsonpickle\.decode\(',
r'shelve\.open\(',
r'yaml\.load\([^)]*(?!safe_load)',
r'numpy\.load\([^)]*allow_pickle\s*=\s*True',
r'torch\.load\([^)]*(?!weights_only\s*=\s*True)',
]
pickle を避けられない場合 — HMAC による整合性チェック:
import hmac, hashlib, pickle, os
SECRET = os.environ["PICKLE_HMAC_SECRET"].encode()
def safe_serialize(obj) -> bytes:
data = pickle.dumps(obj)
mac = hmac.new(SECRET, data, hashlib.sha256).digest()
return mac + data # prepend 32-byte MAC
def safe_deserialize(payload: bytes):
mac, data = payload[:32], payload[32:]
expected = hmac.new(SECRET, data, hashlib.sha256).digest()
if not hmac.compare_digest(mac, expected):
raise ValueError("Integrity check failed — data tampered")
return pickle.loads(data)
2. マスアサインメント(CWE-915)
request.json やフォームデータが、フィールドのフィルタリングなしに ORM の .update() に直接渡されると発生します。これにより、攻撃者は is_admin=True や role="superuser" などのフィールドを設定できます。
Flask — 脆弱なパターン:
# ❌ DANGEROUS — attacker can inject any field
user = User.query.get(user_id)
user.__dict__.update(request.json)
db.session.commit()
# Also dangerous:
User.query.filter_by(id=user_id).update(request.json)
Flask — 安全なパターン:
# ✅ SAFE — explicit allowlist
ALLOWED_FIELDS = {"name", "email", "bio"}
data = {k: v for k, v in request.json.items() if k in ALLOWED_FIELDS}
User.query.filter_by(id=user_id).update(data)
FastAPI — Pydantic はデフォルトでマスアサインメントを防止します:
# ✅ FastAPI with Pydantic — only declared fields accepted
class UserUpdate(BaseModel):
name: str | None = None
email: str | None = None
bio: str | None = None
# is_admin, role etc. cannot be set — they're not in the schema
@router.patch("/users/{user_id}")
async def update_user(user_id: int, body: UserUpdate, db: Session = Depends(get_db)):
db.query(User).filter(User.id == user_id).update(body.model_dump(exclude_unset=True))
検出パターン: Flask では、中間に許可リストによるフィルター処理を挟まずに、request.json または request.form の値が .update()、__dict__.update()、ORM の一括更新呼び出しに渡されていないかを調べます。
3. 外部スクリプト/スタイルシートのサブリソース完全性(SRI)
Flask/FastAPI アプリが外部 CDN からスクリプトやスタイルシートを読み込む HTML テンプレートをレンダリングする場合、CDN の侵害による悪意あるコードの挿入を防ぐため、対象リソースに SRI ハッシュを含める必要があります。
脆弱な Jinja2 テンプレート:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
安全な Jinja2 テンプレート:
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
crossorigin="anonymous">
</script>
SRI ハッシュの生成方法:
# Using openssl
curl -sL https://cdn.example.com/lib.js | openssl dgst -sha384 -binary | openssl base64 -A
# Or use srihash.org / unpkg.com automatically provides them
検出: Jinja2/HTML テンプレートを調べ、integrity= 属性のない <script src="https://..."> および <link href="https://..."> タグを検出します。
4. 検証なしの自動更新(CWE-494)
暗号署名を検証せずにコードや設定の更新をダウンロードして実行するアプリケーションは、中間者攻撃の危険にさらされます。
注意すべきパターン:
# ❌ Downloads and executes without verification
import urllib.request, subprocess
urllib.request.urlretrieve("https://example.com/update.sh", "/tmp/update.sh")
subprocess.run(["bash", "/tmp/update.sh"])
# ❌ Loads remote config without integrity check
import json, urllib.request
config = json.loads(urllib.request.urlopen("https://config.example.com/settings.json").read())
安全なパターン — 署名検証:
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
import urllib.request
def download_and_verify(url: str, sig_url: str, public_key_path: str) -> bytes:
data = urllib.request.urlopen(url).read()
sig = urllib.request.urlopen(sig_url).read()
with open(public_key_path, "rb") as f:
pubkey = serialization.load_pem_public_key(f.read())
pubkey.verify(sig, data, padding.PKCS1v15(), hashes.SHA256()) # raises on failure
return data
テスト用チェックリスト
FastAPI または Flask アプリケーションの A08 準拠を監査するときは、次のチェックリストを使用してください。
デシリアライズ
- 信頼できない入力に対して
pickle.loads()/pickle.load()を使用していない - 信頼できない入力に対して
cPickle、dill、jsonpickle.decode()を使用していない -
yaml.load()の代わりにyaml.safe_load()を使用している -
numpy.load()をallow_pickle=Falseを指定して呼び出している -
torch.load()をweights_only=Trueを指定して呼び出している - 信頼できないキー/データに対して
shelveを使用していない - pickle を避けられない場合:HMAC による事前検証を実装している
マスアサインメント
- Flask:許可リストなしに
request.jsonを ORM の.update()に直接渡していない - Flask:
model.__dict__.update(untrusted_data)のようなパターンを使用していない - FastAPI:すべてのリクエストボディに Pydantic モデルを使用している(デフォルトで防止)
- 特権フィールド(
is_admin、role、balance)を公開更新スキーマに含めていない
外部の完全性
- すべての外部
<script>タグにintegrity=SRI 属性が設定されています - すべての外部
<link rel="stylesheet">タグにintegrity=SRI 属性が設定されています - リモートからダウンロードしたファイルは、実行前に暗号署名またはハッシュで検証されています
- リモートから取得したコンテンツに対する動的な
eval()またはexec()はありません
CI/CD の完全性(A03 との境界)
- デプロイ設定/スクリプトへのすべての変更でコードレビューが必須です
- ビルド成果物には署名が付与され、デプロイ前に署名が検証されます
- フォークからのプルリクエスト用パイプラインは CI/CD シークレットにアクセスできません
自動スキャンツール
| ツール | 検出対象 | 実行方法 |
|---|---|---|
| Bandit | pickle、yaml.load、subprocess、eval | bandit -r . -t B301,B302,B506 |
| Semgrep | デシリアライゼーション、マスアサインメントのパターン | semgrep --config=p/python |
| pip-audit | 既知の脆弱性があるデシリアライゼーションライブラリ | pip-audit |
| 手動 grep | 危険な関数をすばやく検索 | セクション 1 のパターンを参照 |
A08 の Bandit ルール ID:
B301—pickleの使用B302—marshalの使用(同様のリスク)B506— セーフローダーを使わないyaml.loadB403—pickleのインポート(参考情報)
主な CWE
| CWE | 説明 | 主な攻撃経路 |
|---|---|---|
| CWE-502 | 信頼できないデータのデシリアライゼーション | pickle、yaml、jsonpickle |
| CWE-915 | 動的に決定されるオブジェクト属性の不適切な変更制御 | マスアサインメント |
| CWE-829 | 信頼できない制御領域からの機能の取り込み | SRI のない CDN スクリプト |
| CWE-345 | データの真正性検証が不十分 | HMAC/署名チェックの欠如 |
| CWE-494 | 完全性チェックを行わないコードのダウンロード | 検証なしの自動アップデート |
参考資料
- OWASP A08:2025 — ソフトウェアおよびデータの完全性の不備
- Python の Pickle セキュリティ
- SRI ハッシュジェネレーター
- Python 向け Bandit SAST
- エクスプロイトの PoC と詳細な分析については、
references/deserialization-exploits.mdを参照してください