オープンソースの会計ツール Akaunting において、読み取り専用権限を持つ会計担当者が請求書をキャンセルし、支払い履歴を消去できる脆弱性が発見されました。これにより、小規模ビジネスが気づかないうちにデータ損失を被るリスクがあります。この欠陥はバージョン 3.2.0 で修正されましたが、「権限チェックをメソッド名のハードコードされたリストに紐付ける」という設計ミスは、ロールベースのアクセス制御(RBAC)に依存するあらゆるシステムにとって依然として脅威となります。
バグが混入した経緯
Akaunting の API は、メソッド名の許可リスト(allowlist)を参照することでユーザーの権限を検証しています。このリストには、通常の CRUD 操作(作成、読み取り、更新、削除)は含まれていましたが、ステータスを変更するいくつかのエンドポイントが漏れていました。
markSentmarkCancelledmarkReceived
これらのハンドラーがリストに含まれていなかったため、実行時にフレームワークが権限チェックのルーチンを呼び出すことがありませんでした。読み取り専用ロールを持つユーザーが markCancelled エンドポイントに対して単純な GET リクエストを送信すると、システムはそれを正当な状態変更として処理してしまいました。
請求書のキャンセルは、単にドキュメントを無効としてマークするだけではありません。その請求書に紐付いている支払い記録も削除してしまいます。その結果、編集権限のないユーザーが取引の財務履歴を消し去ることができてしまうのです。
テストで判明したこと
この脆弱性は、Akaunting の公式 Docker イメージで確認されました。
- 請求書を更新するための標準的な PUT リクエストは 403 Forbidden を返し、通常の更新パスが保護されていることが確認されました。
- キャンセル用エンドポイントへの GET リクエストは、認可エラーなしで成功し、脆弱性が露呈しました。
なぜ重要なのか
明確な監査証跡(オーディットトレイル)なしに財務諸表が改ざんされる可能性があるため、不正の発見が困難になり、善意のミスを修正することも難しくなります。
修正内容
バージョン 3.2.0 では、権限マップが拡張され、以前は漏れていたステータス変更アクションが含まれるようになりました。このリリース以降、送信済み、キャンセル済み、受信済みなどのドキュメントの状態を変更するあらゆるリクエストは、標準的な更新と同じロール検証を通過する必要があります。これにより、「読み取り専用ロールはデータを変更できない」という本来の期待通りの動作が回復しました。
開発者への教訓
- メソッド名を安全性と同一視しないこと。 新しいエンドポイントを追加しても、自動的に保護が継承されるわけではありません。各パブリックメソッドに副作用がないか監査してください。
- 許可リストの完全性は、リストそのものの網羅性に依存する。 「許可された」動詞の静的なリストは、見落としの隙を生みます。
- 意図と HTTP メソッドを切り離すこと。 GET は読み取り専用であるべきですが、ここでは状態変更が行われていました。データの変更(Mutation)は POST、PUT、DELETE、PATCH に限定してください。
- 権限の網羅性チェックを自動化すること。 静的解析ツールを使用して、認可呼び出しが欠落しているコントローラーメソッドをフラグ立てし、リリース前にギャップを特定できます。
- 最小権限の原則に基づいたアカウントでテストすること。 Docker ベースのテストでは読み取り専用ユーザーが使用されました。CI パイプラインでこのようなシナリオを再現することで、同様の問題を早期に発見できます。
次にすべきこと
Akaunting のコミュニティはすでに修正済みバージョンをリリースしています。管理者は自身のインスタンスのバージョンを確認し、速やかにアップデートを適用してください。
ロールベースのシステムを構築する開発者にとって、教訓は明確です。考えられるすべてのアクションを記憶することに依存する権限モデルは、設計上脆弱です。どの操作が状態を変更するかを明示的に宣言し、フレームワークレベルでチェックを強制し、定期的にコードベースを監査してください。そうして初めて、「読み取り専用」というラベルが財務記録を損なわないことを信頼できるようになります。
