一覧SC-12-2 · 境界設計と情報保護

障害時のアクセス制御と更新処理

判定できない状態を許可と混同せず、障害時に保つべき状態を説明します。

説明

認可サービスへ問い合わせられないとき、許可した場合と拒否した場合では保護する性質が変わります。また、更新途中で監査記録に失敗したときも、途中の状態を残すか戻すか決める必要があります。

残高を10減らす固定操作で、許可・拒否・タイムアウトを比較します。認可障害時の方針と、業務更新・監査記録のトランザクションを変え、保存状態まで確かめます。

判定できない状態を許可と混同せず、障害時に保つべき状態を説明します。

1残高と監査記録の初期状態

残高は100、監査記録は0件です。許可された操作だけ10減らす規則にします。

from sc12 import * db = failure_db()独立した残高と監査表を作る print(db.execute("SELECT amount FROM balance").fetchone()) print(db.execute("SELECT COUNT(*) FROM audit").fetchone())

操作の成否を文字列だけでなく、残高と監査記録の両方で確認します。

2正常な許可で処理する

認可結果allowを得てから、残高と監査を同じトランザクションで更新します。

print(operation(db,"allow","secure")許可された操作を確定する) print(db.execute("SELECT amount FROM balance").fetchone(),db.execute("SELECT COUNT(*) FROM audit").fetchone())

committed、残高90、監査1件です。許可結果に対応する更新が残っています。

3明示的な拒否を受ける

認可サービスがdenyを返します。

print(operation(db,"deny","open")明示的な拒否と障害を分ける) print(db.execute("SELECT amount FROM balance").fetchone(),db.execute("SELECT COUNT(*) FROM audit").fetchone())

残高90、監査1件を保ちます。障害時にopenを選ぶ設定でも、明示的な拒否まで許可へ変える設計ではありません。

4認可障害を許可として扱う

downはTimeoutErrorを発生させます。openの方針では、この例の処理を続けます。

print(operation(db,"down","open")タイムアウト時にも処理を続ける) print(db.execute("SELECT amount FROM balance").fetchone(),db.execute("SELECT COUNT(*) FROM audit").fetchone())

許可を確認できていないのに残高80、監査2件へ進みました。サービス継続と引換えに、認可できない主体の操作も通る可能性があります。

5認可障害を拒否として扱う

同じタイムアウトでsecureを指定します。

print(operation(db,"down","secure")判定不能時に操作を拒否する) print(db.execute("SELECT amount FROM balance").fetchone(),db.execute("SELECT COUNT(*) FROM audit").fetchone())

残高80、監査2件を保ちます。保護状態は保てますが、正当な利用者もこの操作を続けられません。対象操作ごとに必要な継続性を検討します。

6更新後の監査書込みで失敗する

認可は成功しますが、残高更新の後で監査の障害を発生させます。

print(operation(db,"allow","secure",fail_audit=True業務更新後に監査障害を発生させる)) print(db.execute("SELECT amount FROM balance").fetchone(),db.execute("SELECT COUNT(*) FROM audit").fetchone())

rolled_backで残高80、監査2件です。トランザクションにより、減額だけが残る状態を防ぎました。外部の監査サービスと自動で同じ原子性になるわけではありません。

7復旧後に一度確定する

認可と監査が正常になった条件で実行します。

print(operation(db,"allow","secure")復旧後の処理を確定する) print(db.execute("SELECT amount FROM balance").fetchone()) print(db.execute("SELECT id,operation FROM audit確定した監査記録を照合する ORDER BY id").fetchall())

残高70、監査3件です。前の失敗分は残っていません。応答喪失などで成功済み操作を再試行するケースには、別途要求IDによる重複対策も必要です。

まとめ

明示的な拒否と判定不能を別の状態として扱います。

障害時の許可・拒否が保護と継続性へ与える影響を確認します。

業務更新と付随記録の途中失敗でも、一貫した保存状態を保ちます。

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

Python 3.15 / cryptography 50.0.2 / 公開試料と固定時刻

1
from sc12 import * db = failure_db() print(db.execute("SELECT amount FROM balance").fetchone()) print(db.execute("SELECT COUNT(*) FROM audit").fetchone())
(100,)
(0,)
2
print(operation(db,"allow","secure")) print(db.execute("SELECT amount FROM balance").fetchone(),db.execute("SELECT COUNT(*) FROM audit").fetchone())
committed
(90,) (1,)
3
print(operation(db,"deny","open")) print(db.execute("SELECT amount FROM balance").fetchone(),db.execute("SELECT COUNT(*) FROM audit").fetchone())
denied
(90,) (1,)
4
print(operation(db,"down","open")) print(db.execute("SELECT amount FROM balance").fetchone(),db.execute("SELECT COUNT(*) FROM audit").fetchone())
committed
(80,) (2,)
5
print(operation(db,"down","secure")) print(db.execute("SELECT amount FROM balance").fetchone(),db.execute("SELECT COUNT(*) FROM audit").fetchone())
denied
(80,) (2,)
6
print(operation(db,"allow","secure",fail_audit=True)) print(db.execute("SELECT amount FROM balance").fetchone(),db.execute("SELECT COUNT(*) FROM audit").fetchone())
rolled_back
(80,) (2,)
7
print(operation(db,"allow","secure")) print(db.execute("SELECT amount FROM balance").fetchone()) print(db.execute("SELECT id,operation FROM audit ORDER BY id").fetchall())
committed
(70,)
[(1, 'debit-10'), (2, 'debit-10'), (3, 'debit-10')]

演習完了