A08 ソフトウェアまたはデータの完全性の不備

コード、設定、データが、期待した出どころから変えられずに届いたかを確かめずに、正しいものとして扱う状態。確かめる位置は、使う直前に置く。

OWASP Top 10:2025 A08:2025 Software or Data Integrity Failures。対応づけられたCWEは14個[1]。依存部品の選び方と更新は A03 ソフトウェアサプライチェーンの不備で扱う[12]。2026年11月時点の記載で書いています。

要点

  • 言語に備わったオブジェクトの直列化を、信頼しない入力に使わない。復元の途中でコードが動き、型の確認は間に合わない。
  • 要求の本文をそのままモデルに結び付けない。更新してよい項目を許可の一覧で決める。
  • 判断に使う状態はサーバーに置く。クライアントに預けるなら署名し、期限を付け、使う前に確かめる。
  • 外から取り込むスクリプトと配布物は、固定した版とハッシュか署名で照合してから動かす。

成立の条件

OWASPは、信頼しない出どころのプラグイン、ライブラリ、CDNへの依存、完全性を確かめずに部品を取り込むCI/CD、十分な確かめの無い自動の更新、攻撃者が読み書きできる直列化のデータを挙げる[1]。共通するのは、変えられ得る経路を通ったものを、出どころの確認なしに信用すること。

取り込むもの変えられる場所使う直前の確かめ
直列化のデータCookie、隠しフィールド、APIの本文、キューデータだけの形式と型の固定。預けるなら署名
要求の本文の項目利用者の手元結び付けてよい項目の許可の一覧
外部のスクリプトCDN、別の事業者のサーバーサブリソース完全性のハッシュ
ビルドとデプロイの部品配布元、取得の経路、パイプライン版の固定、ハッシュの照合、署名の検証

信頼しないデータのデシリアライズ

Java の ObjectInputStream のような言語に備わった直列化は、どのクラスを作るかをデータの側が決める。復元の途中で、届いたクラスの復元用のメソッドが呼ばれ、クラスパスにある部品のメソッドが連鎖して動く。アプリの型の確認は、そのあとのキャストで初めて行われる[2]。

直列化のデータを復元する途中で既存のクラスのメソッドが連鎖して動き、型の確認はそのあとになる Cookie の値 rO0ABXNy… readObject() 復元を始める クラスを作る データの側が型を選ぶ キャスト 型の確認 既存の部品のメソッドが連鎖して動く ファイルの操作、外部への接続、コマンドの実行に届き得る キャストに来る前に終わっている
図1 復元の途中で動くコードと、型を確かめる位置

悪い例と直した例。架空の買い物かごを 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]。

外から取り込むコードの無検証

ページが読み込む外部のスクリプトと、ビルドの途中で取ってくるスクリプトは、配布元か経路が書き換えられれば、そのままアプリの権限で動く。

ブラウザはCDNから取った中身のハッシュを integrity の値と比べ、一致しなければ実行しない 自社のページ src=cdn.example.net/lib.js integrity=sha384-記録した値 CDN 中身が書き換えられている ブラウザが中身のハッシュを計算 integrity の値と比べる 取ってきた中身 一致しなければ実行しない
図2 サブリソース完全性で、取り込んだスクリプトを使う直前に確かめる
<!-- 悪い例 版の無い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-502Deserialization of Untrusted Dataデシリアライズ
CWE-915Improperly Controlled Modification of Dynamically-Determined Object Attributes一括代入
CWE-565Reliance on Cookies without Validation and Integrity Checkingクライアントの状態
CWE-784Reliance on Cookies without Validation and Integrity Checking in a Security Decisionクライアントの状態
CWE-345Insufficient Verification of Data Authenticity全体
CWE-494Download of Code Without Integrity Check外から取り込むコード
CWE-829Inclusion of Functionality from Untrusted Control Sphere外から取り込むコード
CWE-830Inclusion of Web Functionality from an Untrusted Source外から取り込むコード

参照

  1. A08:2025 Software or Data Integrity Failures OWASP Top 10:2025
  2. Deserialization Cheat Sheet OWASP Cheat Sheet Series
  3. Mass Assignment Cheat Sheet OWASP Cheat Sheet Series
  4. Subresource Integrity W3C
  5. CI/CD Security Cheat Sheet OWASP Cheat Sheet Series
  6. Third Party JavaScript Management Cheat Sheet OWASP Cheat Sheet Series
  7. Bash Uploader Security Update Codecov。2021年4月公表、4月29日更新
  8. Serialization Filtering Oracle。Java SE 21 Core Libraries
  9. pickle Python documentation
  10. CWE-502 MITRE CWE
  11. CWE-915 MITRE CWE
  12. A03:2025 Software Supply Chain Failures OWASP Top 10:2025