セキュリティ

ファンくる不正アクセス:421万件の対象確認と連携会員の範囲、会員登録APIの認可不備

AIの活用によるセキュリティの向上

覆面調査・モニターサービス「ファンくる」への不正アクセスで、流出が確定した会員情報は4,217,663件になりました。運営する株式会社ファンくるが2026年11月9日の第三報で公表した数字で、ファンくるの会員103万件に、連携先サービスの会員318万件が加わっています。前半では、自分は対象か、届いたメールは本物か、パスワードやポイントはどうなるかに公式の文言に基づいて答える構成です。後半は会員基盤を持つ事業者の開発者に向けて、原因となった会員登録フローの参照APIの構造を読み解き、全エンドポイントを未認証と他人のIDで呼び出す認可の回帰テストを実行結果つきで示します。

まとめ:ファンくるの421万件流出で、モニター会員と連携先の会員が先に確かめること

流出した会員は2種類あり、持ち出された項目が大きく違います。

区分 件数 流出した項目
ファンくるの直接会員 1,030,741件 氏名・メールアドレス等
パートナー連携会員 3,186,922件 連携先の識別ID・性別・登録日

直接会員の項目は、会員ID、メールアドレス、ハッシュ済みのパスワード、氏名、性別、郵便番号、電話番号の下4桁です。退会していた人は含まれません。連携会員の分には、氏名やメールアドレス、どの連携先の会員かを示すコードは含まれていないとされています。

会員がいま行うのは、ファンくるを名乗るメールのURLを開かないことです。第三報は、不審なメールのURLのクリック、添付ファイルの開封、パスワード等の入力をしないよう求めています。サービスは11月5日に再開しており、現在までに不正利用や二次被害は確認されていません。

モニター会員が確かめる対象の有無と偽メールの基準、パスワードとポイントの扱い

順次届く個別メールと、パートナー連携会員が連携先の案内を待つ理由

直接会員への連絡は、登録したメールアドレス宛てに届きます。11月4日の第二報は「お詫びとご案内を順次お送りいたします」としており、自分で照会するページの案内はありません。退会済みの人は対象外なので、いまも会員なら対象である前提で構えておくのが現実的です。

連携会員の扱いは違います。ファンくるは連携会員への個別の案内を各パートナー事業者と協議して進めるとし、連携先ごとのお知らせを確かめるよう求めました。2026年11月10日の時点で、連携先の社名は公表されていません。ファンくる側のデータには連絡先が含まれないので、案内は連携先のサービスから届く形になります。

URLを開かず入力しないを基準にした偽メールの判断と、本件の問い合わせ窓口

判断の基準は公式の呼びかけそのものです。ファンくるを名乗るメールでも、本文のURLは開かず、添付ファイルも開かず、パスワードや個人情報は入力しません。モニター案件や謝礼の確認を装う文面は、モニター会員にとって自然に見えるので注意が要ります。

氏名と郵便番号が正しく書かれたメールも、本物の証拠にはなりません。どちらも流出項目に含まれるからです。内容を確かめたいときは、ブックマークか検索からファンくるを開きます。本件の質問は専用の問い合わせ受付フォームで受け付けています。偽のメールとサイトが成り立つ仕組みはフィッシング詐欺の成立の仕組みと対策で解説しました。

ハッシュ済みパスワードの流出でも、使い回しを変えたほうがよい会員の条件

流出したパスワードは、復号できない形式(ハッシュ)で保存されていたと説明されています。第二報の時点で、漏えいした情報を元にした不正ログインやポイントの不正な交換は確認されていません。11月の発表には、パスワードの変更を求める記載もありません。

それでも、ファンくると同じパスワードを他のサービスで使っている人は、そちらを変えておくのが安全です。ハッシュの方式は公表されておらず、短いパスワードや辞書にある語は、ハッシュから総当たりで割り出される場合があるという理由によります。OWASPのPassword Storage Cheat Sheetが、計算に時間がかかるArgon2idやbcryptを勧めているのもこの理由です。方式の違いはbcryptのコスト係数とArgon2idへの移行で比べました。

11月9日まで延びた提出期限とポイント有効期限、モニター謝礼への影響

停止期間の救済として、2つの期限が11月9日(月)まで延長されました。アンケートやレシートなどモニター提出物の期限と、メンテナンス期間中に期限を迎えたポイントの有効期限です。第二報は、調査を済ませた人に対し、再開後に提出できるのでレシートや証明画像を捨てずに保管するよう案内していました。

期限の延長のほかに、補償や謝礼の上乗せについての記載はありません。11月10日の時点で、延長された期限はすでに過ぎています。提出が間に合わなかった案件があれば、問い合わせフォームで個別に相談することになります。

10月30日からの持ち出しと11月5日の再開まで、第一報から第三報で変わった公表内容

持ち出しが続いた約33時間と、検知から封じ込め・停止までの時刻

第二報には、時刻まで細かく書かれています。

日時 出来事
10月30日 10:52 第三者による持ち出しが始まる
10月31日 19:45 持ち出しが終わる
11月1日 0時頃 通常と異なるアクセスを検知
11月1日 1:36 封じ込めを完了
11月1日 10:43 システムを停止
11月5日 修正と監査を終えて再開

持ち出しは約33時間続き、検知はその終了から約4時間後でした。システムの停止は、検知した通信の分析で会員情報取得APIへの不正なアクセスと分かった後です。検知の時点で、データはすでに出ていたことになります。

103万件から421万件へ、第三報で加わったパートナー連携会員318万件の中身

第二報の件数は直接会員の1,030,741件だけでした。第三報で、連携会員の3,186,922件が加わっています。ファンくるは、連携先とのデータの重複登録を防いで整合性を保つため、共通の会員識別子と性別・登録日を自社の基盤に持つ仕様だったと説明しました。そのデータが、今回の参照の対象に含まれたわけです。

連携会員の分には、氏名、電話番号、メールアドレス、生年月日、ログイン認証情報、トークン、所属を示すコードが含まれていません。ファンくるは、外部から特定の連携先やブランドと紐付けることは不可能な状態だと確認したとしています。連携先の側から見ると、自社の会員IDが外部に出たことになるので、各社の案内が出るまで判断は保留です。

8月のリスト型攻撃5万9,389件と、11月のAPI事案を分ける手口の違い

ファンくるでは8月にも別の事案がありました。8月25日の第二報と9月18日の最終報を11月の発表と並べると、入口が違います。

比較項目 8月の事案 11月の事案
手口 リスト型攻撃 参照APIの脆弱性
期間 8月20日〜24日 10月30日〜31日
件数 59,389名 4,217,663件
主な被害 2アカウントでポイント交換 会員情報の持ち出し
パスワード 対象者分を無効化 変更の案内なし

8月は、他のサービスから漏れたIDとパスワードでログインを5,060,270回試す攻撃でした。ファンくるの仕組みの外で手に入れた認証情報が使われたので、対象者のパスワードは8月25日に無効化されています。11月は、ログインを経ずにAPIから会員情報が直接読まれた事案です。リスト型攻撃の検知と防御はパスワードリスト攻撃の検知指標と漏えい照合で扱いました。

脆弱性の種類やハッシュ方式など、11月10日時点で公表されていない5項目

2026年11月10日に確認した時点で、次の5つは公表されていません。この記事でも推測で補いません。

  • 参照APIの脆弱性の種類と、攻撃者がどう呼び出したか
  • パスワードのハッシュの方式
  • パートナー連携先の社名とサービス名
  • 第三者脆弱性診断の実施時期と結果
  • 10月22日のPHP脆弱性対応の告知と本件の関係

最後の項目は誤読されやすい点です。ファンくるは10月22日の告知でCVE-2026-109618・109619への対応を知らせましたが、第三報が示した原因は会員登録フローの参照APIのプログラムです。PHPの脆弱性との関係は書かれていません。PHPの脆弱性の内容はPHP2Shell(CVE-2026-109618・109619)の影響範囲と修正版にまとめています。

会員登録フローの参照APIが認証なしで会員情報を返した構造と、点検するAPIの範囲

「未認証リクエストを完全に遮断」から読める欠陥と、OWASP API1・API5

ここからは、会員基盤を持つ事業者の開発者に向けた内容です。第三報は、改修として「アクセス権限を厳密に検証する認可制御を実装し、未認証リクエストを完全に遮断」したと書いています。修正後に未認証の呼び出しを止めたのなら、修正前の参照APIは、ログインしていない呼び出しにも会員情報を返していたと読めます。

この形は、CWE-306(重要な機能に認証がない)やCWE-862(認可がない)に当たります。OWASP API Security Top 10のAPI1:2023は、リクエストに含まれる会員IDなどを書き換えて他人のデータを読む攻撃を挙げ、IDが連番でもUUIDでも見つけやすいという説明です。API5:2023は、全ての機能で既定をアクセス拒否にし、許可を明示する作りを求めています。ただし、ファンくるは脆弱性の種類を明らかにしていません。ここで挙げたのは公表文から読める範囲の分類です。

ログイン前の画面から呼ぶAPIが公開になりやすい理由と、返す項目を絞るAPI3

会員登録の画面は、ログインする前に表示されます。メールアドレスの重複確認や紹介コードの照合、連携先からの遷移で会員を引き当てる処理など、未ログインで呼ぶAPIが自然に生まれる構造です。「登録画面から呼ぶので公開」と決めた参照APIが、会員テーブルの行をそのまま返していれば、IDを変えて呼ぶだけで名簿が読めます。

対策は、公開にするAPIが返す項目を、画面に要るものだけにすることです。API3:2023は、オブジェクトの全項目を返す実装を問題として挙げています。重複確認なら、必要な応答は「使えるかどうか」を示す真偽値だけです。その真偽値もアドレスの存在を教えてしまうので、件数の制限を併せて置きます。アカウント列挙の防ぎ方はスタディサプリの事案とアカウント列挙対策で、会員情報取得APIが悪用された別の事案はソースネクストの対象確認と会員APIの点検で扱いました。

連携先の会員を共通IDと性別・登録日で抱えた設計と、仮名化で減らせる範囲

318万件の連携会員が巻き込まれた理由は、ファンくるの説明では重複登録の防止でした。重複の判定に要るのは「同じ人か」だけで、連携先が発行したIDの値そのものは要りません。連携先ごとの鍵でHMACにかけた値を持てば、判定はできて、流出しても連携先のIDには戻せません。

# 連携先から受け取る会員IDを、連携先ごとの鍵でHMAC化してから保存する(Python 3.9以上・標準ライブラリのみ)
import hashlib
import hmac

# 検証用の鍵。実運用ではKMSやシークレット管理から連携先ごとに読み込み、DBとは別に置く
KEYS = {"partner-a": b"demo-key-a-change-me", "partner-b": b"demo-key-b-change-me"}


def pseudonym(partner, external_id):
    digest = hmac.new(KEYS[partner], external_id.encode("utf-8"), hashlib.sha256).hexdigest()
    return digest[:32]  # 重複判定に使う長さだけを残す


for partner, external_id in [("partner-a", "U000123"), ("partner-a", "U000123"), ("partner-b", "U000123")]:
    print(partner, external_id, "->", pseudonym(partner, external_id))

同じ環境で2026年11月10日に実行した結果です。

partner-a U000123 -> d7c68e796814eade1dcc67d3526b932d
partner-a U000123 -> d7c68e796814eade1dcc67d3526b932d
partner-b U000123 -> 95ec1ea76f0ad924121af163f61948ba

同じ連携先の同じIDは同じ値になり、重複を判定できます。連携先が違えば、IDが同じでも値は一致しません。鍵をDBと同じ場所に置くと、DBごと盗まれたときに意味がなくなるので、鍵はシークレット管理の側に分けます。性別と登録日も、重複の判定に使っていないなら持たない選択肢があります。提携先とのAPIで認可をどう分けるかはライフネット生命の事案と提携先APIの認可で整理しました。

全エンドポイントを未認証と他人のIDで呼ぶ認可の回帰テストと、SAST監査の限界

公開APIの一覧と、未認証・他人IDの試験を回すPythonと実行結果

ファンくるの事案から開発側が持ち帰る試験は2つです。全てのエンドポイントを認証なしで呼んで個人情報が返らないか、ログインした状態で他人のIDを指定して読めないか。次のスクリプトは、修正前と修正後のAPIをダミーで並べ、この2つを全件に当てます。ルートを登録するときに既定を認証必須にし、公開にするなら public=True を書かせて一覧に出すのが要です。

# 全エンドポイントを「未認証」と「他人のID」で呼び、個人情報が返る経路を洗い出す(Python 3.9以上・標準ライブラリのみ)
# 会員データとAPIはすべて検証用のダミー。実運用では自社のルート表と試験用アカウントに置き換える
import sys
from urllib.parse import parse_qs

AT = chr(64)  # アットマークの文字
MEMBERS = {
    "1001": {"member_id": "1001", "email": f"hanako{AT}example.com", "name": "山田花子", "zip": "1000001"},
    "1002": {"member_id": "1002", "email": f"taro{AT}example.net", "name": "佐藤太郎", "zip": "5300001"},
    # 連携先から受け取った会員。識別子と性別・登録日だけを持つ
    "P-9001": {"member_id": "P-9001", "gender": "F", "registered": "2025-04-01"},
}
SESSIONS = {"token-1001": "1001"}  # ログイン中の会員1001のトークン
PERSONAL = {"email", "name", "zip", "gender", "registered"}
ROUTES = {}


def route(path, public=False):
    # 既定は認証必須。公開にするときは public=True を明示させ、一覧で見えるようにする
    def register(fn):
        ROUTES[path] = (fn, public)
        return fn
    return register


def pick(member, fields):
    return {k: member[k] for k in fields if k in member}


@route("/api/v1/register/lookup", public=True)  # 修正前:登録画面から呼ぶので公開にした参照API
def lookup_before(user, q):
    member = MEMBERS.get(q.get("member_id", [""])[0])
    return (200, member) if member else (404, {})


@route("/api/v2/register/check", public=True)  # 修正後:使えるかどうかだけを返す
def check_after(user, q):
    taken = any(m.get("email") == q.get("email", [""])[0] for m in MEMBERS.values())
    return 200, {"available": not taken}


@route("/api/v1/members/detail")  # 認証はあるが、指定IDの持ち主かを見ていない
def detail_before(user, q):
    member = MEMBERS.get(q.get("member_id", [""])[0])
    return (200, member) if member else (404, {})


@route("/api/v1/members/me")  # 自分の分だけを、返す項目を決めて返す
def me_after(user, q):
    return 200, pick(MEMBERS[user], ("member_id", "name", "email"))


def call(path, query, token=None):
    fn, public = ROUTES[path]
    user = SESSIONS.get(token)
    if not public and user is None:
        return 401, {}
    return fn(user, parse_qs(query))


def audit(victim="1002", partner="P-9001", token="token-1001"):
    findings = []
    for path, (fn, public) in sorted(ROUTES.items()):
        for target in (victim, partner):
            status, body = call(path, f"member_id={target}")
            if status == 200 and PERSONAL & body.keys():
                findings.append((path, f"未認証で{target}の項目を返す", sorted(PERSONAL & body.keys())))
        if public:
            continue  # 公開APIは上の未認証の試験で判定済み
        status, body = call(path, f"member_id={victim}", token=token)
        if status == 200 and body.get("member_id") == victim:
            findings.append((path, f"他人({victim})のIDで取得できる", sorted(PERSONAL & body.keys())))
    return findings


def main():
    public = [p for p, (_, pub) in sorted(ROUTES.items()) if pub]
    print(f"エンドポイント {len(ROUTES)} 本のうち公開 {len(public)} 本: {', '.join(public)}")
    findings = audit()
    for path, kind, fields in findings:
        print(f"要修正: {path} {kind} {fields}")
    print(f"検出 {len(findings)} 件")
    sys.exit(1 if findings else 0)


if __name__ == "__main__":
    main()

Python 3.14.10(Windows)で2026年11月10日に実行した結果です。終了コードは1でした。

エンドポイント 4 本のうち公開 2 本: /api/v1/register/lookup, /api/v2/register/check
要修正: /api/v1/members/detail 他人(1002)のIDで取得できる ['email', 'name', 'zip']
要修正: /api/v1/register/lookup 未認証で1002の項目を返す ['email', 'name', 'zip']
要修正: /api/v1/register/lookup 未認証でP-9001の項目を返す ['gender', 'registered']
検出 3 件

修正前の参照APIは、ログインなしで他の会員の氏名やメールアドレスを返し、連携会員の性別と登録日まで返しています。修正後の /api/v2/register/check は真偽値しか返さないので検出されません。/api/v1/members/detail は認証こそ通しているものの、IDの持ち主かどうかを見ていないため、他人の情報が読めました。CIでこのテストを回し、検出が1件でもあれば止めます。実運用では、ルート表を自社のフレームワークから取り出し、試験用の会員を2人用意して同じ試験を当てます。

全Web APIのSAST監査で拾える範囲と、認可の欠陥が残りやすい理由

ファンくるは、全てのWeb APIを対象にソースコード監査(SAST)を行い、同種の脆弱性がないことを確かめたとしています。SASTはコードを実行せずに調べるので、認証のデコレータが付いていないルートや、入力をそのまま問い合わせに使う箇所は機械的に拾えます。

苦手なのは「この会員の情報を、誰が見てよいか」という業務上の規則です。コードには規則が書かれていないので、ツールは正しいかどうかを判定できません。上の /api/v1/members/detail も、認証が付いている以上、パターンだけでは見逃されます。SASTの守備範囲はSASTとDAST・SCAの違いで、サーバ側で認可を止める実装はIDORと権限昇格を止める実装で解説しました。ファンくるも全件監査とは別に、外部の専門ベンダーによる第三者脆弱性診断の準備を進めていると発表しました。業務上の規則に沿って人が試す工程は、SASTとは別に要るということです。

持ち出しの終了から約4時間後の検知を踏まえた、未認証APIの件数監視

今回の検知は、持ち出しが終わった後でした。公開APIの監視は、リクエスト数ではなく「1つの呼び出し元が読んだ異なる会員IDの数」で見ます。登録画面の正規の利用なら、1人が引き当てる会員は数件で止まるからです。

具体的には、未認証の呼び出しについて、IPアドレスや端末の単位で10分間と24時間の上限を置き、超えたら429を返して通知します。上限の方式はAPIレート制限の5方式と429の設計で比べました。認可、レート制限、トークンの失効をまとめた手順はAPI不正アクセス対策の実装手順にあります。

個人情報保護委員会への確報期限と、会員基盤を持つ事業者が診断を入れる条件

1,000人超と不正目的に当たる漏えい等報告と、発覚日から60日以内の確報

ファンくるは個人情報保護委員会へ速報を出し、管轄の警察署へ相談と被害報告をしたと発表しました。個人情報保護委員会の案内では、本人の数が1,000人を超える漏えい等は報告の対象です。不正な目的で行われたおそれがある場合の確報の期限は、発覚日から60日以内とされています。

連携先の各社も、自社の会員の識別IDが出た以上、報告の要否を自社で判断します。今回の連携会員の項目が個人データに当たるかは、連携先が持つ他の情報と容易に照合できるかで変わるためです。報告と本人通知の流れは個人情報保護委員会への報告義務の実務で確認できます。

第三者の診断を先に入れる3つの条件と、自社の回帰テストで足りる範囲

結論から書くと、上の回帰テストはどの規模でも入れます。数十行で書け、認可の外し忘れを毎回のリリースで止められるからです。

次のいずれかに当てはまるなら、テストに加えて第三者の診断を先に入れます。ログイン前の画面から呼ぶ公開APIを自社で開発している、連携先や提携先の会員データを自社の基盤に持っている、会員が数万人を超えている、のいずれかです。外部サービスの標準フォームだけで会員登録を受け、会員データを自社で持たない小規模なサイトは、診断より先に管理者の多要素認証と公開APIの棚卸しを済ませれば十分です。公開APIと連携データの扱いまで含めて第三者の目で確かめたい場合は、脆弱性診断・セキュリティ診断でご相談いただけます。

よくある質問

ファンくるの不正アクセスについて、会員から出やすい質問をまとめました。内容は2026年11月2日、4日、9日の公表文に基づきます。

ファンくるの不正アクセスで自分が対象かどうかはどう確かめますか?

直接会員には、登録したメールアドレス宛てに順次お詫びと案内が届きます。自分で照会するページの案内はありません。流出したのは退会者を除く会員の情報なので、いまも会員であれば対象の可能性を考えて行動してください。連携先のサービスを通じて登録した人は、ファンくるではなく連携先から届くお知らせを確かめます。質問はファンくるの専用の問い合わせフォームで受け付けています。

パートナー連携会員とは何で、どのサービスの会員が含まれますか?

ファンくると連携する事業者のサービスで登録した会員で、ファンくるは重複登録を防ぐ目的で共通の識別子と性別、登録日を持っていました。流出したのはこの3項目で、氏名、電話番号、メールアドレスは含まれないとされています。2026年11月10日の時点で、連携先の社名やサービス名は公表されていません。個別の案内は、ファンくると各連携先が協議して進めると発表されています。

ファンくるのパスワードは変更したほうがよいですか?

11月の発表に、パスワードの変更を求める記載はありません。パスワードは復号できない形式で保存されており、第二報の時点で不正ログインは確認されていないとされました。ただし、ハッシュの方式は公表されていないので、同じパスワードを他のサービスで使っているなら、そちらを別のものに変えておくと安心です。8月の不正ログインの対象者は、すでにパスワードが無効化されています。

ポイントや謝礼に影響はありますか?

11月の事案で、ポイントの不正な交換は確認されていません。停止期間に期限を迎えたポイントと、モニター提出物の期限は11月9日まで延長されました。延長のほかに、補償や謝礼の上乗せについての記載はありません。8月の事案では2アカウントで不正なポイント交換が確認されましたが、これは別の事案です。ポイントの確認を装うメールのURLは開かないでください。

8月の不正ログインとは別の事件ですか?

別の事案です。8月は他のサービスから漏れたIDとパスワードを使うリスト型攻撃で、59,389名のアカウントにログインされました。11月は会員登録フローの参照APIの脆弱性を突かれ、ログインを経ずに会員情報が読まれています。期間も手口も件数も違うので、2つの件数を足して考える必要はありません。発表も別々で、8月の事案は9月18日の最終報で区切られ、11月の事案は11月9日の第三報まで出ています。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.09 テックブログ エディオン不正アクセス第2報:カード番号の対象確認とログ・決済画面の改ざん対策
  2. 2025.04.30 テックブログ IMSIキャッチャー(偽基地局)とは?2Gフォールバックの手口と日本での対策
  3. 2026.11.10 テックブログ ニトリの情報漏洩:最大3,762万件の対象確認となりすまし連絡の見分け方
  4. 2026.11.10 テックブログ Claude Fable 5.2とは:料金・破壊的変更4点とFable 5.1からの移行手順【2026年11月】
  5. 2026.11.09 テックブログ LANSCOPE不正アクセス第2報:製品への影響と偽連絡の確認、停止中のライセンス認証の窓口

RELATED POSTS 関連記事

目次