A08 ソフトウェアまたはデータの完全性の不備
コード、設定、データが、期待した出どころから変えられずに届いたかを確かめずに、正しいものとして扱う状態。確かめる位置は、使う直前に置く。
要点
- 言語に備わったオブジェクトの直列化を、信頼しない入力に使わない。復元の途中でコードが動き、型の確認は間に合わない。
- 要求の本文をそのままモデルに結び付けない。更新してよい項目を許可の一覧で決める。
- 判断に使う状態はサーバーに置く。クライアントに預けるなら署名し、期限を付け、使う前に確かめる。
- 外から取り込むスクリプトと配布物は、固定した版とハッシュか署名で照合してから動かす。
成立の条件
OWASPは、信頼しない出どころのプラグイン、ライブラリ、CDNへの依存、完全性を確かめずに部品を取り込むCI/CD、十分な確かめの無い自動の更新、攻撃者が読み書きできる直列化のデータを挙げる[1]。共通するのは、変えられ得る経路を通ったものを、出どころの確認なしに信用すること。
| 取り込むもの | 変えられる場所 | 使う直前の確かめ |
|---|---|---|
| 直列化のデータ | Cookie、隠しフィールド、APIの本文、キュー | データだけの形式と型の固定。預けるなら署名 |
| 要求の本文の項目 | 利用者の手元 | 結び付けてよい項目の許可の一覧 |
| 外部のスクリプト | CDN、別の事業者のサーバー | サブリソース完全性のハッシュ |
| ビルドとデプロイの部品 | 配布元、取得の経路、パイプライン | 版の固定、ハッシュの照合、署名の検証 |
信頼しないデータのデシリアライズ
Java の ObjectInputStream のような言語に備わった直列化は、どのクラスを作るかをデータの側が決める。復元の途中で、届いたクラスの復元用のメソッドが呼ばれ、クラスパスにある部品のメソッドが連鎖して動く。アプリの型の確認は、そのあとのキャストで初めて行われる[2]。
悪い例と直した例。架空の買い物かごを Cookie で持つ Java のアプリ
// 悪い例 Cookie の値をそのまま復元する
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(Base64.getDecoder().decode(cookie)));
Cart cart = (Cart) in.readObject();
// 直した例 1 データだけの形式にし、受け取る型をアプリが決める
Cart cart = mapper.readValue(json, Cart.class);
// 直した例 2 既存の仕組みを外せないとき。許すクラスと深さを決め、それ以外を拒む
in.setObjectInputFilter(
ObjectInputFilter.Config.createFilter("maxdepth=5;com.example.cart.*;!*"));
- OWASPは、言語に備わった直列化の形式を避け、JSONのようなデータだけの形式を使うことで大きく危険が減るとする[2]。
- Python の
pickleは、信頼するデータ以外を復元してはならないと公式の文書が警告する[9]。.NET のBinaryFormatterは、Microsoft が危険で安全にできないとしている[2]。 - Java の直列化のフィルターは、復元の前にクラスを許可の一覧で絞る仕組み。一覧は広げず、アプリの型だけにする[8]。
- 見分け方。Java の直列化のデータは、16進で
AC ED 00 05、Base64 でrO0から始まる[2]。
一括代入
要求の本文を、そのままモデルやドメインのオブジェクトに結び付けると、画面に無い項目も書き換えられる。Rails と Node.js では Mass Assignment、Spring MVC と ASP.NET MVC では Autobinding と呼ばれる[3]。
悪い例と直した例。架空のプロフィールの更新API
// 悪い例 本文の項目をすべて反映する
await User.update(req.body, { where: { id: req.user.id } });
// 本文が {"name": "Sato", "role": "admin"} なら role も変わる
// 直した例 変えてよい項目だけを取り出して渡す
const { name, email } = req.body;
await User.update({ name, email }, { where: { id: req.user.id } });
- 結び付けてよい項目は、許可の一覧で明示する。拒否の一覧だけに頼ると、あとで足した機微な項目が書き換えられる[3]。
- 入力の受け皿を、ドメインのオブジェクトと別の型にする。利用者が編集してよい項目だけを持つ型にすれば、余計な項目は結び付く先が無い[3]。
署名の無いクライアントの状態
Cookie や隠しフィールドに、料金の区分、権限、残り回数のような状態を置き、戻ってきた値で判断すると、利用者が値を書き換えて送れる。Base64 にしても隠したことにはならない。CWE-565 と CWE-784 に当たる。
悪い例と直した例。Node.js の架空のアプリ
// 悪い例 戻ってきた Cookie の中身で機能を開く
const state = JSON.parse(Buffer.from(req.cookies.state, "base64url").toString());
if (state.plan === "pro") enableProFeatures();
// 直した例 1 判断に使う状態はサーバーに置き、Cookie にはセッションIDだけを置く
const { plan } = await db.one("SELECT plan FROM accounts WHERE id = $1", [req.session.accountId]);
// 直した例 2 クライアントに預けるしかないとき。HMAC で署名し、一定の時間で比べ、期限を確かめる
const [body, mac] = String(req.cookies.state).split(".");
const expect = crypto.createHmac("sha256", KEY).update(body).digest();
const got = Buffer.from(mac ?? "", "base64url");
if (got.length !== expect.length || !crypto.timingSafeEqual(got, expect)) throw new Forbidden();
const state = JSON.parse(Buffer.from(body, "base64url").toString());
if (state.exp < Date.now() / 1000) throw new Forbidden();
署名は改ざんを見つけるだけで、中身は読める。秘密を入れない。同じ値を送り直されても困らないよう、期限と、必要なら使い捨ての番号を入れる。OWASPは、信頼しないクライアントからの署名の無い直列化のデータを信用せず、改ざんと再送を見つける完全性の確かめを求める[1]。
外から取り込むコードの無検証
ページが読み込む外部のスクリプトと、ビルドの途中で取ってくるスクリプトは、配布元か経路が書き換えられれば、そのままアプリの権限で動く。
<!-- 悪い例 版の無いURLを、そのまま読み込む -->
<script src="https://cdn.example.net/lib/latest/lib.js"></script>
<!-- 直した例 版を固定し、ハッシュを付ける。別のオリジンからは CORS で取る -->
<script src="https://cdn.example.net/lib/4.2.1/lib.js"
integrity="sha384-…" crossorigin="anonymous"></script>
サブリソース完全性は、integrity 属性に書いたハッシュと取得した中身が一致しなければ、その資源を使わない仕組み。別のオリジンの資源では、CORS で取ることが条件になる[4]。中身が変わる URL には付けられないので、版を固定したURLにする。自社のオリジンから配ることも選択肢になる[6]。
ビルドの側も同じ。2021年4月、Codecov は、CIで取得して動かす Bash Uploader が1月31日から断続的に書き換えられ、CIの環境変数を外部へ送る行が足されていたと公表した。気づいたのは、GitHub に公開されたハッシュと、取得したスクリプトのハッシュを比べた利用者だった[7]。
# 悪い例 取ってきたスクリプトをそのまま動かす
curl -s https://uploader.example.com/upload.sh | bash
# 直した例 版を固定して取り、記録しておいたハッシュと照合してから動かす
curl -sO https://uploader.example.com/v1.4.2/upload.sh
echo "<記録した SHA-256> upload.sh" | sha256sum -c - && bash upload.sh
守り方
- 信頼しない入力に、言語に備わった直列化を使わない。データだけの形式にし、受け取る型をアプリが決める[2]。
- 入力の結び付けは許可の一覧にし、受け皿の型をドメインのオブジェクトと分ける[3]。
- 判断に使う状態はサーバーに置く。預けるなら署名と期限を付け、使う前に確かめる[1]。
- ソフトウェアとデータが期待した出どころから変えられずに来たことを、署名かハッシュで確かめる。外部のスクリプトにはサブリソース完全性を付ける[1][4]。
- CI/CDは、依存の版を固定してハッシュを照合し、成果物に署名する。パイプラインの資格情報は必要な最小の権限にする[5]。
- コードと設定の変更はレビューを通してから取り込む[1]。
次は緩和の策で、根本の直し方を済ませたうえで重ねる。
- CSP で、スクリプトを読み込めるオリジンを絞る。外部のスクリプトが増えたことに気づける。
- サブドメインを外部の事業者へ向ける時は、親ドメインに付けたCookieがその事業者に送られることを前提に、Cookieの範囲を狭める[1]。
診断で見る所
- 直列化の入口
ObjectInputStream、pickle.loads、PHP のunserialize、BinaryFormatterと、型の情報を本文から読む設定のJSONライブラリ。通信ではrO0で始まる値とapplication/x-java-serialized-object[2]。 - 結び付け 本文をそのままモデルに渡す箇所。画面に無い項目、たとえば
role、isAdmin、ownerId、priceを足して送り、反映されるか[3]。 - クライアントの状態 Cookie、隠しフィールド、ローカルストレージの値を復号して読み、書き換えて送り、判断が変わるか。
- 外部の取り込み
integrityの無い外部のスクリプト、版の無いURL、CIのcurl | bash、照合の無い取得。
関連するCWE
A08には14個のCWEが対応づけられている[1]。このページの節に関わるものを挙げる。デシリアライズと一括代入の定義は、MITREの各ページにある[10][11]。
| CWE | 名前 | このページの節 |
|---|---|---|
| CWE-502 | Deserialization of Untrusted Data | デシリアライズ |
| CWE-915 | Improperly Controlled Modification of Dynamically-Determined Object Attributes | 一括代入 |
| CWE-565 | Reliance on Cookies without Validation and Integrity Checking | クライアントの状態 |
| CWE-784 | Reliance on Cookies without Validation and Integrity Checking in a Security Decision | クライアントの状態 |
| CWE-345 | Insufficient Verification of Data Authenticity | 全体 |
| CWE-494 | Download of Code Without Integrity Check | 外から取り込むコード |
| CWE-829 | Inclusion of Functionality from Untrusted Control Sphere | 外から取り込むコード |
| CWE-830 | Inclusion of Web Functionality from an Untrusted Source | 外から取り込むコード |
参照
- A08:2025 Software or Data Integrity Failures OWASP Top 10:2025
- Deserialization Cheat Sheet OWASP Cheat Sheet Series
- Mass Assignment Cheat Sheet OWASP Cheat Sheet Series
- Subresource Integrity W3C
- CI/CD Security Cheat Sheet OWASP Cheat Sheet Series
- Third Party JavaScript Management Cheat Sheet OWASP Cheat Sheet Series
- Bash Uploader Security Update Codecov。2021年4月公表、4月29日更新
- Serialization Filtering Oracle。Java SE 21 Core Libraries
- pickle Python documentation
- CWE-502 MITRE CWE
- CWE-915 MITRE CWE
- A03:2025 Software Supply Chain Failures OWASP Top 10:2025