「画面は動く」が一番危ない — 操作権限を UI / API の二重で守る
UI 側でボタンを隠しただけで安心していると、API を直接叩かれた瞬間に終わります。レガシー移行で踏みやすい権限制御の落とし穴と、二重防御をルール化した話です。
なぜ書いたか
VB系の業務アプリには、「特定の高権限ユーザーだけが押せるボタン」が結構な数あります。Blazorに移行するときに、UI側のボタン表示条件は割と素直に再現できるのですが、API側に同じ条件を入れ忘れる という事故を実際に踏みました。今回はその話と、それ以降にルール化した二重防御の話です。
何が起きたか
ある画面で、Tabごとに削除ボタンが付いていました。元実装では片方のTabの削除は「高権限ユーザー専用」でした。Blazor移植後にレビューを回したら、こう返ってきました。
P1: Tab2 の削除に UI / API 両方の権限制御が不足している。
→ 一般ユーザーが高権限者専用操作を実行可能。
UI側はボタン表示条件を見落としていて、一般ユーザーでもボタンが押せる状態でした。さらにAPI側にもロールチェックが入っていなかったので、仮にボタンを隠せたとしても、APIを直接叩けば誰でも削除できる、という状態です。
対策: 二重防御をデフォルトに
UIを隠しただけで満足すると、ほぼ確実にこのパターンを踏みます。なので、権限制御を入れるときは 必ず両方 を満たすようにルール化しました。
- UIガード: ボタンの
disabled/ 非表示条件に権限フラグを入れる - API認可: コントローラ側でも同じ条件を強制する。直API呼び出しを想定して書く
Blazor側の例だと、こんな書き方になります。
@if (CanUseRestrictedDelete)
{
<button @onclick="OnDelete">削除</button>
}
API側はコントローラのアクションでロール検査を入れ、UI側のフラグと 同じ判定式 を使います。判定がふたつの場所で別々に書かれていると、片方だけ修正されて差分が出るので、可能な限り共通化します。
わかったこと
- UI側で権限を制御していても、それは 見えなくしているだけ で、防御していません
- API側の認可ガードは、UI側のガードとは「目的が違う」ものとして両方あるべきです(UIは誤操作防止、APIは不正アクセス防御)
- レビュー観点に「権限制御はUI / APIの両方にあるか」を 固定で入れる と、見落としが激減します
次にやること / 未解決の問題
- 権限フラグの計算ロジックを共通モジュールに寄せる作業がまだ進んでいない。今は画面ごとに似たような条件式が散らばっている
- 認可ロジックの自動テストはまだ薄い。最低でも「権限なしでAPIを直叩きしたら403」のテストを各エンドポイントに足したい
渡邊 賢
等差級数的Commit 運営 / ICD VIETNAM.LLC General Manager
AI駆動開発と段階的なレガシーモダン化をテーマに、日々の試行錯誤をこのブログに記録しています。
プロフィール詳細 arrow_forward似たような課題に困っている方、一緒に考えませんか。
AI駆動開発・Vibe Coding・レガシーマイグレーションに関するご相談を受け付けています。