一覧SC-02-1 · 脅威モデリング

データの流れと信頼境界

構成要素の名称だけでなく、流れるデータと管理主体から、どこで何を検証する必要があるか説明します。

説明

脅威モデリングでは、システムが何を受け取り、どこへ渡し、誰が管理するかを整理します。信頼境界は、入力や主体を同じ条件で信用できなくなる境目です。ネットワーク上の線だけでなく、外部サービスや管理主体の違いも対象になります。

ブラウザ、入口、API、DB、外部決済、監査保存の6要素を持つ固定構成を使います。要素とデータの流れをSQLで結合し、境界を越える情報、外部依存、設定された検査を確認します。

構成要素の名称だけでなく、流れるデータと管理主体から、どこで何を検証する必要があるか説明します。

1要素と管理主体

zoneはこの設計資料で定義した信頼領域です。ownerは各要素の管理主体で、実際の利用者IDとは異なります。

from sc02 import connect固定構成を読み込む db = connect()独立した構成DBを作る print(db.execute("SELECT * FROM nodes ORDER BY id要素・信頼領域・管理主体を読む").fetchall())

browserは利用者側、paymentは外部事業者側です。自組織内でもAPI、DB、監査は異なる管理範囲を持っています。

2流れるデータを読む

同じHTTPSでも、認証情報、金額、処理結果では守る対象が異なります。

print(db.execute("SELECT id,source,destination,protocol,data送信元・宛先・プロトコル・データを並べる FROM flows ORDER BY id").fetchall())

f1からf6までに、向きと内容があります。双方向の通信でも、要求と通知で別の流れとして記録すると検査条件を明確にできます。

3境界をまたぐ流れを抽出する

送信元と宛先のzoneを結合し、異なるものを取り出します。

print(db.execute("SELECT f.id,f.source,a.zone,f.destination,b.zone流れの両端と信頼領域を読む FROM flows f JOIN nodes a ON a.id=f.source JOIN nodes b ON b.id=f.destination WHERE a.zone<>b.zone領域が異なる流れを抽出する ORDER BY f.id").fetchall())

6本とも異なる領域を通ります。境界があることだけで拒否するのではなく、越える情報の検査と許可を設計する必要があります。

4外部依存を絞る

外部事業者が管理するpaymentに入る流れと、そこから戻る通知を確認します。

print(db.execute("SELECT id,source,destination,data外部との往復を読む FROM flows WHERE source='payment' OR destination='payment'paymentへ関わる流れに絞る ORDER BY id").fetchall())

f4は金額と注文を送信し、f5は決済結果を受け取ります。外部APIへ送る情報の範囲と、戻り値を信用する条件は別々に確認します。

5境界ごとの検査を対応付ける

guardsはこの構成で想定する対策の一覧です。設定資料であり、その対策が正しく動いたという観測記録ではありません。

print(db.execute("SELECT f.id,f.data,g.controlデータと対策を同じ行で読む FROM flows f LEFT JOIN guards検査の欠落も見えるよう対応付ける g ON g.flow=f.id ORDER BY f.id").fetchall())

TLS、proxyの信頼範囲、tenant条件、署名、追記専用保存が別の流れへ対応しています。通信の暗号化だけで、注文の所有権や通知内容まで検証できるわけではありません。

6検査の欠落を見つける

設計変更でf5の署名検査が抜けた状態を作り、対応する流れを抽出します。

db.execute("DELETE FROM guards通知の検査が欠落した状態にする WHERE flow='f5'") print(db.execute("SELECT f.id,f.source,f.destination,f.data検査のない流れを読む FROM flows f LEFT JOIN guards g ON g.flow=f.id WHERE g.flow IS NULL対応する対策がない行を選ぶ").fetchall())

payment-resultを受け取るf5が残ります。構成図に通信線があるだけでは、この通知の受入条件を確認できません。

7対策の担当箇所を戻す

f5に署名検査を戻し、受信側APIの管理主体を取得します。

db.execute("INSERT INTO guards通知の署名検査を戻す VALUES('f5','signature')") print(db.execute("SELECT f.id,n.id,n.owner,g.control受信側と実装担当を対応付ける FROM flows f JOIN nodes n ON n.id=f.destination JOIN guards g ON g.flow=f.id WHERE f.id='f5'").fetchall())

受信側api、管理主体product、検査signatureが対応します。対策名だけでなく、どこで誰が実装するかまで設計へ残せました。

まとめ

信頼境界は領域と管理主体の違いから定義します。

同じ通信方式でも、流れるデータによって検証条件は異なります。

対策の一覧と、実際に機能した証拠は別々に確認します。

操作履歴と解説を振り返れます。

Python 3.15 / SQLite / 固定構成と公開試料HMAC鍵

1
from sc02 import connect db = connect() print(db.execute("SELECT * FROM nodes ORDER BY id").fetchall())
[('api', 'app', 'product'), ('audit', 'operations', 'security'), ('browser', 'untrusted', 'customer'), ('db', 'data', 'data-team'), ('edge', 'dmz', 'platform'), ('payment', 'external', 'provider')]
2
print(db.execute("SELECT id,source,destination,protocol,data FROM flows ORDER BY id").fetchall())
[('f1', 'browser', 'edge', 'HTTPS', 'credentials'), ('f2', 'edge', 'api', 'HTTP', 'user,order'), ('f3', 'api', 'db', 'SQL', 'order,tenant'), ('f4', 'api', 'payment', 'HTTPS', 'amount,order'), ('f5', 'payment', 'api', 'HTTPS', 'payment-result'), ('f6', 'api', 'audit', 'JSON', 'request-id,actor')]
3
print(db.execute("SELECT f.id,f.source,a.zone,f.destination,b.zone FROM flows f JOIN nodes a ON a.id=f.source JOIN nodes b ON b.id=f.destination WHERE a.zone<>b.zone ORDER BY f.id").fetchall())
[('f1', 'browser', 'untrusted', 'edge', 'dmz'), ('f2', 'edge', 'dmz', 'api', 'app'), ('f3', 'api', 'app', 'db', 'data'), ('f4', 'api', 'app', 'payment', 'external'), ('f5', 'payment', 'external', 'api', 'app'), ('f6', 'api', 'app', 'audit', 'operations')]
4
print(db.execute("SELECT id,source,destination,data FROM flows WHERE source='payment' OR destination='payment' ORDER BY id").fetchall())
[('f4', 'api', 'payment', 'amount,order'), ('f5', 'payment', 'api', 'payment-result')]
5
print(db.execute("SELECT f.id,f.data,g.control FROM flows f LEFT JOIN guards g ON g.flow=f.id ORDER BY f.id").fetchall())
[('f1', 'credentials', 'TLS'), ('f2', 'user,order', 'trusted-proxy'), ('f3', 'order,tenant', 'tenant-filter'), ('f4', 'amount,order', 'TLS'), ('f5', 'payment-result', 'signature'), ('f6', 'request-id,actor', 'append-only')]
6
db.execute("DELETE FROM guards WHERE flow='f5'") print(db.execute("SELECT f.id,f.source,f.destination,f.data FROM flows f LEFT JOIN guards g ON g.flow=f.id WHERE g.flow IS NULL").fetchall())
[('f5', 'payment', 'api', 'payment-result')]
7
db.execute("INSERT INTO guards VALUES('f5','signature')") print(db.execute("SELECT f.id,n.id,n.owner,g.control FROM flows f JOIN nodes n ON n.id=f.destination JOIN guards g ON g.flow=f.id WHERE f.id='f5'").fetchall())
[('f5', 'api', 'product', 'signature')]

演習完了