本日0件のお問い合わせがありました。

✉️FIELDにお問い合わせ

システム開発アプリケーション開発

個人情報はなぜ流出するのか|言語・フレームワークを問わない攻撃の仕組みと対策

Go、Python、Swift、Java、Node.js、Ruby。開発に使う言語やフレームワークが違っても、個人情報の流出につながる問題には共通点があります。

それは、利用者の識別、データへのアクセス権、入力の扱い、秘密情報の管理、保存先やクラウドの公開設定――こうした「境界」のどこかで、本来許可していないアクセスが成立してしまうことです。

もちろん、言語やフレームワークごとに固有の脆弱性や設定の作法はあります。それでも、「ログインしている人なら、誰の個人情報でも取得できる」という実装は、どの言語で書いても同じように危険です。フレームワークが認証機能を用意してくれていても、「この利用者が、この会社の、この顧客情報を見てよいのか」という業務上の判断までは自動で正しくなりません。

この記事では、攻撃者がシステムの構造をどう把握するのか、そのあと実際にどんな不備で情報が漏れるのか、そしてどう確認し、どう防ぐのかを、言語に依存しない共通の視点で整理します。

なお、記事中のコードやデータは説明用です。検証の例は、自分で管理する環境とダミーデータで行うことを前提にしています。


情報流出は「サーバーへの侵入」だけで起きるわけではない

個人情報の流出というと、攻撃者がサーバーに侵入してデータベースを丸ごと持ち出す場面を思い浮かべがちです。しかし実際には、普通にAPIを呼び出すだけで他人の情報が返ってくるケースが少なくありません。

架空の注文管理サービスで考えてみます。

  1. 一般ユーザーとして会員登録する
  2. 自分の注文詳細を開き、そのときのAPI通信を確認する
  3. 同じAPIに、別の利用者の注文IDを指定して送る
  4. サーバーが注文の持ち主を確認しないまま、注文情報を返してしまう

ここでは、管理者のパスワードを盗む必要も、サーバー上でプログラムを動かす必要もありません。サーバー自身が、通常の機能として情報を渡してしまっているのです。

これは、データ単位の権限確認が抜けている「BOLA(Broken Object Level Authorization)」や「IDOR(Insecure Direct Object Reference)」と呼ばれる問題です。押さえておきたいのは、通信が正常に処理されたことと、そのアクセスが正当であることは別という点です。


攻撃者はどうやってAPIや内部構造を把握するのか

使われている技術から、標準機能の場所を調べる

Webアプリケーションは、HTTPヘッダー、Cookieの名前、エラーページ、HTMLやJavaScriptの特徴などから、使っている技術を推測されることがあります。これを「フィンガープリンティング」と呼びます。中継サーバーや設定変更の影響もあるため、特徴が一致したからといって確実に特定できるわけではありません。

それでも、フレームワークがわかれば、その技術の標準機能や初期設定を調べやすくなります。

  • Python の FastAPI には、初期設定で APIドキュメントを表示する /docs や、仕様を JSON で返す /openapi.json があります。公開されていれば、APIのパスや入力形式を知る手がかりになります。ただし、API仕様の公開そのものが個人情報の流出を意味するわけではありません。
  • Java の Spring Boot には、運用監視のための Actuator があります。公開範囲や認証の設定を誤れば内部情報への入口になり得ますが、初期状態でHTTP経由で公開されるのは health に限られています。

ここで区別しておきたいのは、「標準機能の場所を知っていること」と「そのシステムの全APIやデータベース構造を知っていること」は別物だということです。技術の特定は調査の手がかりにすぎず、そのあと何が取れてしまうかは、公開設定とアクセス制御で決まります。

ブラウザの通信を観察する

Webサービスでは、ブラウザの開発者ツールで、画面が送受信している通信をそのまま確認できます。Chrome の Network パネルなら、リクエスト先、HTTPメソッド、送信データ、ヘッダー、レスポンス本文まで見られます。

たとえば注文詳細画面を開いたとき、次の通信が発生したとします。

GET /api/v1/orders/1001
Authorization: Bearer 利用者のアクセストークン
Accept: application/json

ここから、注文詳細を取るAPI、注文IDの指定方法、認証情報の渡し方がわかります。

画面に「他人の注文を表示する」ボタンがなくても、同じ通信は別のプログラムから自由に送れます。画面上の制限とAPIのアクセス制御は別物であり、管理用のボタンを非表示にしただけでは、管理機能を守ったことにはなりません。

Android・iOS アプリを解析する

スマホアプリでも、通信先やリクエストの組み立て方を調べられる場合があります。方法は大きく2つで、アプリを動かさずに中身を調べる「静的解析」と、動作中の様子を観察する「動的解析」です。

  • Android では、APK に含まれるコードやリソースを調べます。たとえば JADX は、DEX コードを人が読みやすい Java に近い形へ逆コンパイルするツールです。元のソースが完全に戻るとは限りませんが、通信先や処理の流れをつかむには十分なことがあります。
  • iOS(Swift など) のアプリも、バイナリ内の文字列、関数、参照関係などが調査対象になります。Android と同じようには読めませんが、「コンパイルしてあるから通信仕様はわからない」とは考えられません。
  • 動的解析 では、検証用プロキシで通信を観察したり、Frida などで実行時の処理を調べたりします。HTTPS の中身を見られるかどうかは、端末の証明書の信頼設定や証明書ピンニングに左右されます。同じネットワークにいるだけで誰でも中身を読める、という意味ではありません。

アプリの解析で分かるのは、まずアプリ側が持っている情報と処理です。サーバーのソースコードまで手に入るわけではありません。それでも、APIの呼び出し方がわかれば、サーバーが不正な要求をきちんと拒否するかどうかは試せてしまいます。


APIの構造が分かったあと、どんな不備が流出につながるのか

データ単位の権限確認がない

最も基本的で、最も多い問題です。「誰がログインしているか」は確認していても、「そのデータを読んでよいか」を確認していないケースです。

リクエスト結果判定
Aのトークン + Aの注文IDAの注文が返る正常
Aのトークン + Bの注文IDBの注文が返るAに閲覧権限がなければ情報流出

認証は「あなたは誰か」の確認、認可は「あなたに何を許すか」の判断です。この2つを混同すると、正規のユーザーによる不正な取得を防げません。

対象になるのは注文IDだけではありません。顧客ID、応募者ID、請求書ID、ファイルIDも同じです。また、IDを連番から UUID に変えて推測しにくくしても、何らかの経路でIDを知った相手に対する権限確認は依然として必要です。

会社・組織の境界を、送られてきた値だけで決めている

複数の会社が使う業務システム(マルチテナント)では、利用者同士だけでなく、会社同士の分離も重要です。

リクエストに含まれる company_id や tenant_id をそのまま信じて検索すると、値を書き換えるだけで別の会社の情報に届いてしまう設計になり得ます。

会社を切り替える機能そのものが悪いわけではありません。問題は、「指定された会社にこの利用者が所属しているか」「その操作を許可されているか」を確かめずに、指定値を信用することです。利用者情報や権限を内部サービスへヘッダーで渡す場合も、外部から同じヘッダーを自由に付けられないよう信頼境界を設計する必要があります。

画面では隠している情報を、APIでは返している

データ全体へのアクセスは正しくても、返す項目が多すぎるケースです。

公開プロフィール画面には名前しか出していないのに、APIがメールアドレス、住所、電話番号、社内メモまで含んだ JSON を返している――。画面で表示していなくても、その情報はすでに利用者の端末に届いています。

対策は、画面で隠すことではなく、サーバー側で返す項目を絞ることです。公開用・本人用・担当者用・管理者用で、返してよい項目を分けて定義します。

送信する項目を足すと、重要な設定まで変えられる

書き込みでも同じことが起きます。

プロフィール変更画面では名前しか編集できなくても、APIが受け取った項目を無条件に保存する作りだと、画面に存在しない role、is_admin、company_id まで更新できてしまうことがあります。これは Mass Assignment と呼ばれる問題です。

対策は、更新してよい項目を明示的に決めることです。「文字列として正しい」「数値として正しい」という形式チェックだけでは足りません。形式が正しくても、その利用者が変えてよい項目とは限らないからです。

管理者向けの機能を、一般ユーザーも実行できる

たとえば、次のような顧客一覧の出力機能があったとします。

/api/v1/admin/customers/export

この機能がログイン確認だけで動くなら、一般ユーザーにも顧客一覧を持っていかれる可能性があります。URLに admin と書いてあることは、管理者権限の確認にはなりません。

その機能を実行してよいかどうかを、サーバー側で必ず確認します。検索、CSV出力、一括ダウンロードなどは、通常の画面とは別に点検しておくべき機能です。

REST 以外のAPIでも、同じ問題は起きる

URLにIDが入る REST API だけの話ではありません。たとえば GraphQL では、1つのエンドポイントから関連データをまとめて取得できます。入口でログインを確認していても、取得するオブジェクトや項目、関連データごとのアクセス制御が甘ければ、許可していない情報が返ってしまいます。

APIの形式を変えても、データ・項目・操作に対する権限確認が不要になるわけではありません。


言語に関係なく、どうやって確認できるのか

守る側の検証でも、画面を触るだけでは不十分です。権限の異なるアカウントを用意し、APIを直接呼び出して、その違いが正しく反映されているかを確かめます。

大切なのは、「本人のデータが取れる」ことだけでなく、「他人のデータが取れない」ことをテストすることです。

ローカル環境に次のダミーデータがあるとします。

  • ユーザーAの注文:1001
  • ユーザーBの注文:1002

Bearer トークン認証のAPIなら、curl で応答を比べられます。サーバーが Go でも Python でも Java でも Node.js でも、確認方法は同じです。

# 自分で管理するローカル環境とテスト用トークンを使う
TOKEN_A='ユーザーAのテスト用トークン'

# A が自分の注文を取得する(許可されるはず)
curl --silent --show-error --max-time 5 --include \
  -H "Authorization: Bearer ${TOKEN_A}" \
  -H "Accept: application/json" \
  "http://127.0.0.1:8080/api/v1/orders/1001"

# 同じ A の認証情報で、B の注文を指定する(拒否されるはず)
curl --silent --show-error --max-time 5 --include \
  -H "Authorization: Bearer ${TOKEN_A}" \
  -H "Accept: application/json" \
  "http://127.0.0.1:8080/api/v1/orders/1002"

1つ目はAの注文が返り、2つ目は拒否されることを期待します。あわせて、BのトークンならBの注文が正常に取れることも確認してください。対象データが存在しないせいで拒否されているだけなら、権限確認のテストにはなっていません。

確認する条件は、たとえば次のように整理できます。

確認する条件期待する結果
本人が本人のデータを取得する許可された項目だけが返る
AがBの非公開データを取得する拒否され、情報が返らない
未認証・無効なトークンで取得する拒否される
別会社のデータを指定するその会社への権限がなければ拒否される
一般ユーザーが管理者機能を呼ぶ拒否される
更新できない項目を送信する拒否または無視され、重要な項目が変わらない

見るべきは HTTPステータスだけではありません。 レスポンス本文と、実際にデータが変わったかどうかも確認します。200 が返ったからといってすぐに流出と決まるわけではなく、逆に 403 や 404 でも本文に個人情報が入っていれば問題です。500 のサーバーエラーを「うまく拒否できた」と扱ってはいけません。

サーバー側で必要な処理を、特定の言語に依存しない擬似コードで書くと次のようになります。

利用者   = 認証情報を検証して取得する
操作対象 = 「利用者がアクセスできる範囲」の中から検索する

対象が見つからない、または操作権限がなければ拒否する

書き込みでは、変更を許可した項目だけを受け付ける
読み取りでは、閲覧を許可した項目だけを返す

このルールを、詳細画面だけでなく、一覧・検索・更新・削除・ファイル取得のすべてに適用します。ミドルウェアなどで入口の認証を共通化していても、対象データごとの認可が抜けていないかは別に確認が必要です。


APIの権限確認以外からも、個人情報は漏れる

入力値が「データ」ではなく「命令」として処理される

SQLインジェクションは、入力値をSQLの文字列に不適切に連結した結果、その入力がSQLの命令や条件の一部として解釈されてしまう問題です。検索欄やURLのパラメーターが入口になり、意図しない範囲のデータを取られる可能性があります。

基本の対策は、パラメーター化(プレースホルダー)でSQLの構造と入力値を分けることです。ORM を使っていても、一部で生のSQLに入力値をつなげていれば、その箇所は安全とは言えません。また、SQLインジェクションを防いでも、検索条件に所有者の確認がなければ、前述の認可の不備はそのまま残ります。

同じように、信頼できないデータを危険なオブジェクト復元処理(デシリアライズ)に渡すと、意図しない処理の実行につながることがあります。単にデータを読み込むだけなのか、オブジェクトや動作まで復元する仕組みなのかを区別し、外部からの入力には安全な形式を選ぶ必要があります。

URL取得機能を踏み台に、内部システムへアクセスさせる

画像の取り込み、Webページのプレビュー、URLからのファイル取得といった機能では、サーバーが利用者の指定した接続先にアクセスします。

接続先の制限が甘いと、攻撃者が直接は届かない内部サービスへ、サーバーを経由してアクセスさせられることがあります。これが SSRF(Server-Side Request Forgery) です。取得先やサーバーの権限によっては、内部情報やクラウドの認証情報にまで届いてしまいます。

対策は、許可する接続先と通信方式の制限、内部アドレスへの不要な通信の禁止、リダイレクト先や名前解決後の接続先の確認、取得処理の権限分離です。URLの文字列を一度チェックするだけでは不十分な場合がある点に注意してください。

アプリや配布ファイルから、共通の秘密情報を取り出される

全利用者に配るアプリに、サーバー用の強い権限を持つ秘密鍵を埋め込んでいると、解析で取り出されたとき、その鍵の権限が及ぶ範囲すべてにアクセスされる可能性があります。

文字列を分割したり難読化したりすれば解析の手間は増えますが、配布した秘密を絶対に取り出せなくする方法ではありません。 利用者ごとに認証・認可する設計を基本にし、全員に配るコードに管理用の共通秘密を持たせないことが重要です。

なお、「APIキー」という名前だけでは、それが秘密なのか、どれほど危険なのかは判断できません。確認すべきは、その値を持っていると何ができるのか、どのデータや操作に権限があるのかです。用途と権限を整理し、不要な権限を削り、保管場所を分け、失効・更新の手順まで用意しておきます。

正規ユーザーの認証情報やセッションを盗まれる

API自体に認可の不備がなくても、管理者のログイン情報やログイン済みのセッションを盗まれれば、その管理者に許された機能はすべて使われてしまいます。

たとえば、盗んだセッションで管理画面に入り、正規のCSV出力機能から顧客一覧を取得する――。これはAPIの権限確認を直すだけでは防げません。米国の CISA の脅威情報でも、WebセッションCookieの窃取が実際の手口として取り上げられています。

対策は、フィッシングに強い認証方式(パスキーなど)、端末の保護、管理権限の最小化、セッションの有効期限と失効、重要な操作の前の再認証などの組み合わせです。「ログインするときの対策」と「ログインしたあとのセッションの保護」は、別の課題として扱う必要があります。


保存先や周辺の設定から漏れるケース

データベースやファイルの保存先が公開されている

個人情報を含むファイルが誰でも取得できる設定になっていれば、Webアプリのログインを通る必要すらありません。

たとえば Amazon S3 には、意図しない公開を防ぐ Block Public Access があります。ただし、「有効にした」という事実だけで安心せず、対象の保存先、アクセス経路、権限設定まで確認することが大切です。アプリの認証と、保存先そのもののアクセス制御は、別々に点検します。

設定ファイル、バックアップ、ソースコード管理、ビルド成果物に秘密情報が残っていることもあります。漏れたファイルを消しても、すでにコピーされた鍵は使えるままです。秘密情報が流出したら、失効と再発行まで行ってください。

キャッシュが、別の利用者に応答を渡してしまう

APIの権限確認が正しくても、その手前にある共有キャッシュ(CDNやプロキシ)が利用者を区別しなければ、A向けの応答をBに配ってしまうことがあります。

個人向けの応答を共有キャッシュに載せる場合は特に注意が必要です。機密情報を返す応答は「キャッシュさせない」ことも含めて設計し、アプリが返すHTTPヘッダーだけでなく、CDNやプロキシ側の設定まで確認します。Cookieが付いているから自動的に安全な扱いになる、というわけではありません。

ログや監視サービスにも、個人情報が複製されている

リクエスト本文やレスポンスを丸ごとログに残すと、パスワード、トークン、住所などが、ログ基盤や外部の監視サービスにも保存されてしまいます。

すると、本番データベースの権限はきびしく管理していても、ログを見られる人や外部サービスからは情報に届いてしまう状態になります。監査に必要な情報は残しつつ、秘密情報や不要な個人情報は除外・マスキングし、保存期間と閲覧権限を管理します。

古いAPIや検証環境が残っている

現行のAPIを直しても、旧バージョンが残っていれば、そこから同じデータに届いてしまいます。

/api/v2/customers → 修正済み(現行のWeb画面が使用)
/api/v1/customers → 古い実装のまま(旧バージョンのスマホアプリが使用)

検証用の環境が本番データにつながっている場合も同じです。現行の画面が使うAPIだけでなく、旧版・管理用・検証用・外部連携用まで含めて、公開されている入口と接続しているデータを一覧にすることが対策になります。


ライブラリや外部サービスを経由して漏れるケース

自社で書いたコードに問題がなくても、使っているフレームワーク、ライブラリ、プラグインに脆弱性があれば、そこが攻撃の入口になります。

必要なのは、依存しているソフトウェアの一覧とバージョンを把握し、脆弱性情報を確認して更新し続けることです。直接入れたものだけでなく、そのライブラリがさらに依存している先も対象です。WordPress のプラグインも同じです。

また、Webページで読み込んでいる外部の JavaScript が改ざんされると、フォームに入力された情報をブラウザ上で読み取られる可能性があります。この経路ではデータベースに保存される前の情報が狙われるため、サーバー側のAPI認可だけでは防げません。

外部スクリプトは必要最小限にし、変更管理、実行範囲の分離、使える場面では SRI(Subresource Integrity)による整合性チェック、CSP(Content Security Policy)などを組み合わせます。許可済みのスクリプト自体が乗っ取られる場合もあるため、1つの設定だけに頼らないことが大切です。


「入れたから安全」とは言えない対策

よく「対策済み」として挙がるものでも、それぞれ守れる範囲は限られています。

対策守れることそれだけでは防げないこと
HTTPS通信経路の盗聴・改ざんサーバーが他人の情報を返してしまう不具合
CORSブラウザ上のクロスオリジン通信の制御ブラウザ以外のプログラムからの直接呼び出し
JWT改ざんされにくいトークンの受け渡し署名・有効期限・発行者・対象の検証漏れ、データ単位の認可漏れ
WAF・自動スキャン典型的な攻撃パターンの検知「有効なAのトークンで実在する注文番号を指定する」といった、業務ルール上の不正

暗号化された通信のまま、誤った相手に情報を渡すことは普通に起こり得ます。WAF やスキャナーには役割がありますが、その注文とAの関係を知らなければ、そのリクエストが正当かどうかは判断できません。自動診断に加えて、そのシステム固有の権限ルールを検証する必要があります。


開発・運用で共通ルールにすべき3つのこと

1. 利用者の役割とアクセスできる範囲を、実装の前に決める

「本人」「担当者」「同じ会社の管理者」「システム全体の管理者」では、読めるデータも変えられる項目も違います。この違いをそのままテスト条件にし、機能の追加や改修のたびに確認します。

2. API・保存先・依存ソフトウェア・外部連携を、すべて管理対象にする

現行の画面やコードだけでなく、旧API、出力済みのファイル、検証環境まで把握しておかないと、直したつもりのシステムに別の入口が残ります。

3. 不正な取得に気づける記録を残す

ログインの失敗やエラーだけでなく、「誰が、どの機能で、どの種類のデータにアクセスしたか」を後から追えるようにします。一方で、監視のために個人情報やトークンを過剰に記録し、新たな流出元を作らないよう注意します。


まとめ

個人情報を守るうえで中心になるのは、特定の言語を選ぶことでも、APIの場所を秘密にすることでもありません。

「APIの構造は調べられる。画面を使わずに呼び出される。入力値は書き換えられる。それでも、許可していないデータや操作には届かない」

この前提を、実装と検証の共通ルールにすることが何より大切です。

  • 認証(誰か)と認可(何を許すか)を分けて、データ・項目・操作ごとに確認する
  • 返す項目・受け付ける項目は、サーバー側で明示的に決める
  • 入力は命令として解釈させない。URL取得や復元処理は接続先・形式を絞る
  • 秘密情報は配布物に入れず、権限を絞り、漏れたら失効させる
  • 保存先・キャッシュ・ログ・旧API・検証環境・依存ライブラリまで点検対象にする
  • 「本人は取れる」だけでなく「他人は取れない」をテストで確かめ続ける

合同会社FIELDは、この記事で取り上げたセキュリティ課題のすべてに対応できます。

Go、Python、Swift、Java、Node.js、Ruby、PHP(WordPress を含む)など言語やフレームワークを問わず、権限設計・マルチテナントの分離といった設計段階から、既存システムやスマホアプリのAPIの診断、認可漏れ・SQLインジェクション・SSRF・Mass Assignment などの改修、秘密情報やクラウド保存先・キャッシュ・ログの設定見直し、依存ライブラリの管理、権限ごとの自動テストの整備まで、一貫してお手伝いします。

「自社のサービスは大丈夫だろうか」と少しでも気になった方は、まずはお気軽にご相談ください。


参考にした主な資料

  • OWASP API Security Top 10(API1: Broken Object Level Authorization / API3: Broken Object Property Level Authorization / API5: Broken Function Level Authorization / API9: Improper Inventory Management)
  • OWASP Cheat Sheet Series(Authorization、Authorization Testing Automation、Multi Tenant Security、Mass Assignment、GraphQL、SQL Injection Prevention、Deserialization、SSRF Prevention、Secrets Management、Session Management、JSON Web Token、Logging、Vulnerable Dependency Management、Third Party JavaScript Management など)
  • OWASP Web Security Testing Guide(Fingerprint Web Application Framework)
  • OWASP Mobile Application Security Testing Guide(MASTG)
  • FastAPI 公式ドキュメント(OpenAPI・/docs)、Spring Boot 公式ドキュメント(Actuator Endpoints)
  • Chrome for Developers(Network パネル)、curl 公式ドキュメント
  • Android Developers(アプリのセキュリティに関するベストプラクティス)
  • AWS ドキュメント(Amazon S3 Block Public Access)
  • MDN Web Docs(HTTP キャッシュ、Cache-Control)
  • CISA(WebセッションCookieの窃取に関する脅威情報)

Yamamoto Yuya

プロフェッショナルとしての高いスキルと知識を持ち、誠実さと責任感を大切にする。常に向上心を持ち、新たな挑戦にも積極的に取り組む努力家。