画面に出していない数字が、APIからは丸見えだった — 制作日誌 #7
ビジネス

画面に出していない数字が、APIからは丸見えだった — 制作日誌 #7

自作アプリの「原価」は管理者にしか表示していなかった。しかしAPIには管理者チェックが無く、ログイン済みなら誰でも直接取得できた。塞いだあと、401だけの確認では不十分だと気づいた話。

KIYODO00
#個人開発#セキュリティ#API#検証#制作日誌

自作のiOSタスク管理アプリには、AI機能の原価(1人あたり運営側にいくらかかっているか)を表示する画面がある。当然これは自分だけが見るもので、一般のユーザーには出していない。

ある日ふと「これ、本当に自分にしか見えていないか?」と疑って調べたところ、画面は無事だったが、APIに穴が1つ空いていた。

何が漏れていたのか

構成はこうなっている。

  • 設定画面の「AI原価(今月)」 … ログイン中のメールアドレスが自分のものか判定して表示を切り替えている
  • 分析タブの原価 … 管理者専用の画面で、サーバー側でも管理者チェックをしている
  • GET /api/prep/cost … 原価を返すAPI本体

問題は3つ目だった。このAPIはログインしているかどうかしか見ていなかった。管理者かどうかのチェックが、丸ごと抜けていたのだ。

つまり、画面上にはどこにも出てこないが、ログイン済みの一般ユーザーがこのURLを直接叩けば、「自分の使い方が運営にいくらのコストを発生させているか」が取得できた。

不幸中の幸いだったのは、漏れる範囲がその人自身の分だけだったこと。他人の原価も、全体の売上も、集計値も、このAPIからは出ない。とはいえ、原価を知られるということは、価格設定の内側を見せることなので、塞ぐべき情報であることに変わりはない。

なぜ起きたか

理由は単純で、「画面に出していないから安全」と考えていたからだ。

表示するかどうかを決めているのは、アプリ側のコードだ。しかしアプリ側のコードは、利用者から見れば単なる提案でしかない。APIのURLさえ分かれば、アプリを経由せずに直接叩ける。画面での出し分けは、権限管理ではなく見せ方の調整にすぎない。

これは頭では分かっていたつもりだった。実際、管理者専用の分析画面のほうには、ちゃんとサーバー側のチェックを入れていた。抜けたのは、**「開発中に自分だけが使うつもりで作ったAPI」**のほうだった。後から一般ユーザーもログインする本番環境に載ったとき、権限チェックを足すのを忘れていた。

直したこと

やったのは1行だ。原価を返す処理の先頭で、開発者アカウントでなければ 403(権限が無い、というエラー)を返すようにした。

デプロイして完了。一般ユーザーの画面には何の変化もなく、追加費用も発生していない。

修正そのものは5分の作業だった。時間がかかったのは、この後の検証のほうだ。

「401が返ったから安全」は検証になっていない

最初、私は次のように確認しようとした。

ログインせずにAPIを叩く → 401(認証が必要、というエラー)が返る → OK

これは検証になっていない。401は「ログインしていない人が弾かれた」ことしか証明していない。今回の穴は、そもそも**「ログインしている一般ユーザー」**が通れてしまう、というものだった。ログインしていない状態でいくら試しても、その経路は再現できない。

同じ理屈で、でたらめなトークン(本人であることを示す文字列)を送っても意味がない。偽物は認証の段階で落ちるので、その先の権限チェックまで到達しない。権限チェックのバグは、認証を通過して初めて姿を現す。

そこで、審査用に用意してある一般ユーザーのアカウントで実際にログインし、本物のトークンを取得して、本番のAPIを叩いた。

結果は次の通り。

叩き方期待実際
認証なし401401
でたらめなトークン401401
一般ユーザーの本物のトークン403403

ここまでやって、初めて「塞がった」と言える。

横断で確認する

1つ見つかったということは、他にもある可能性が高い。そこで原価に類する数字を返しうるAPIを全部洗い出し、それぞれに管理者チェックがあるかを1件ずつ確認した。今回は残り4つあり、いずれもガードは入っていた。

この「1件見つけたら同じ種類を全部洗う」という手順は、面倒でも省かないほうがいい。同じ思い込み(画面に出していないから安全)で書いたコードは、たいてい複数箇所にあるからだ。

得た教訓

  • 画面での出し分けは権限管理ではない。 権限はサーバーで決める
  • 自分専用のつもりで作ったAPIが一番危ない。 本番に一般ユーザーが載った時点で前提が変わる
  • 401で満足しない。 権限の穴は、認証を通った先にある。本物のトークンで検証する
  • 1件見つけたら同種を全部洗う。 穴は思い込み単位でまとまって存在する

よくある質問

Q. 原価が見えると具体的に何がまずいのですか? 価格設定の内側が見えます。「この機能は原価いくらで、いくらで売っているか」が分かる状態は、事業判断の材料を外に出しているのと同じです。直接的な被害が想像しにくくても、出さないに越したことはありません。

Q. 個人開発でここまでやる必要がありますか? 穴の大小に関わらず、確認の手順そのものは同じです。今回のように「本物のトークンで叩く」という検証を一度用意しておけば、以後は使い回せます。むしろ個人開発こそ、レビューしてくれる他人がいないので、検証手順を持っておく価値が高いと思っています。

Q. どうやって気づいたのですか? 「これ、本当に自分にしか見えていないか?」と疑ったからです。コードを書いた本人は「隠したつもり」の記憶があるので、疑うきっかけがないと気づけません。定期的に自分の実装を疑う時間を取るのが現実的な対策だと思います。

この記事で触れているアプリ

本稿の題材は、筆者が個人開発している iOS アプリ THE TASK:AI下調べToDoリスト(App Store・無料)です。書いたタスクをAIが着手できる状態まで補完する、という発想で作っています。他の回は制作日誌シリーズにまとまっています。

あわせて読みたい

コメント (0)

まだコメントはありません。最初の一言を残しませんか?