脅威・脆弱性とリスク
対策がどの成立条件を変えたのかを示し、検証した経路での効果と、未検証のリスクを区別します。
説明
脅威となる操作があっても、到達経路や弱点、利用できる権限がそろわなければ、同じ損失にはつながりません。一つの経路を閉じても別の経路が残る場合があります。リスクを調べるときは、資産・成立条件・影響を分けて記録します。
注文1万件を持つ資産に対し、公開APIの危険な問合せと、管理APIの漏れたトークンという2経路を固定資料で調べます。成立条件をSQLで集計し、一つずつ条件を変えながら残る経路を確認します。
対策がどの成立条件を変えたのかを示し、検証した経路での効果と、未検証のリスクを区別します。
1資産と業務上の所有者
守る対象はordersの1万件で、業務上の所有者はsalesです。件数は固定シナリオの影響対象であり、損失額ではありません。
from sc01 import connect
db = connect("risk")固定の資産・条件表を用意する
print(db.execute("SELECT * FROM assets資産の範囲と所有者を読む").fetchall())資産の識別子、規模、責任を持つ部門がそろいました。これから、この資産へ届く経路を調べます。
2二つの入口を確認する
webはpublic-api、adminはadmin-apiから同じordersへ届く経路です。
print(db.execute("SELECT * FROM paths ORDER BY name入口と保護対象の対応を読む").fetchall())入口が別でも対象は同じ資産です。公開APIだけの対策では管理API側の条件は変わりません。
3成立条件を並べる
各経路には3条件があり、1は成立、0は不成立を表します。このモデルでは同じ経路の全条件が成立すると対象へ到達できます。
print(db.execute("SELECT * FROM conditions ORDER BY path,name経路ごとの必要条件を一覧にする").fetchall())webには到達性・危険な問合せ・DB書込み権限、adminには到達性・漏れたトークン・書込みscopeがあります。単に弱点名を列挙するだけでは成立条件が見えません。
4全条件がそろう経路を抽出する
0と1のMINを取り、全条件が1の経路を選びます。
query = "SELECT path FROM conditions GROUP BY path HAVING MIN(satisfied)=1すべて成立した経路を選ぶ ORDER BY path"
print(db.execute(query).fetchall())残る経路を読むadminとwebの両方が残ります。これは確率や重大度を計算した結果ではなく、固定条件の成立判定です。
5公開APIの弱点を修正する
webのunsafe_queryを0にします。到達性とDB権限はそのまま残します。
db.execute("UPDATE conditions SET satisfied=0webの弱点条件だけを解消する WHERE path='web' AND name='unsafe_query'")
print(db.execute(query).fetchall())対策後の残存経路を読むadminだけが残ります。問合せの修正はweb経路を止めましたが、別の管理経路まで止めたわけではありません。
6漏れた管理トークンを失効する
adminのleaked_tokenを0へ変えます。
db.execute("UPDATE conditions SET satisfied=0漏れたトークンを利用できなくする WHERE path='admin' AND name='leaked_token'")
print(db.execute(query).fetchall())定義済み経路が残っていないことを読むこの条件表では成立する経路が空になります。表に含めていない入口や未知の弱点まで検査できたわけではありません。
7弱点が再発した場合を確認する
webのunsafe_queryを1へ戻し、DB書込み権限を0にします。弱点だけでなく、必要な権限も成立条件であることを確かめます。
db.execute("UPDATE conditions SET satisfied=1弱点がある状態へ戻す WHERE path='web' AND name='unsafe_query'")
db.execute("UPDATE conditions SET satisfied=0改変に必要な書込み権限を外す WHERE path='web' AND name='db_write'")
print(db.execute(query).fetchall())権限を含めた成立条件を再評価する書込みの経路は成立しません。ただし、この表の対象は書込みによる改変です。読取りの漏えいを評価するには、別の操作と権限条件を定義する必要があります。
まとめ
入口・弱点・権限・資産を対応させると、対策が変える条件を説明できます。
一つの経路の修正後も、別経路が残る場合があります。
条件表の対象範囲を超えてリスクがなくなったと結論付けません。
操作履歴と解説を振り返れます。
Python 3.15 / SQLite / 固定業務・監査資料
from sc01 import connect
db = connect("risk")
print(db.execute("SELECT * FROM assets").fetchall())[('orders', 10000, 'sales')]print(db.execute("SELECT * FROM paths ORDER BY name").fetchall())[('admin', 'admin-api', 'orders'), ('web', 'public-api', 'orders')]print(db.execute("SELECT * FROM conditions ORDER BY path,name").fetchall())[('admin', 'leaked_token', 1), ('admin', 'reachable', 1), ('admin', 'write_scope', 1), ('web', 'db_write', 1), ('web', 'reachable', 1), ('web', 'unsafe_query', 1)]query = "SELECT path FROM conditions GROUP BY path HAVING MIN(satisfied)=1 ORDER BY path"
print(db.execute(query).fetchall())[('admin',), ('web',)]db.execute("UPDATE conditions SET satisfied=0 WHERE path='web' AND name='unsafe_query'")
print(db.execute(query).fetchall())[('admin',)]db.execute("UPDATE conditions SET satisfied=0 WHERE path='admin' AND name='leaked_token'")
print(db.execute(query).fetchall())[]
db.execute("UPDATE conditions SET satisfied=1 WHERE path='web' AND name='unsafe_query'")
db.execute("UPDATE conditions SET satisfied=0 WHERE path='web' AND name='db_write'")
print(db.execute(query).fetchall())[]
演習完了