---
title: "スカイライナーの不正アクセス：最大約337万件のID・パスワード取得と、暗号化保存の点検手順"
url: "https://www.issoh.co.jp/tech/details/19751/"
published: 2026-11-14
updated: 2026-11-14
categories: ["セキュリティ"]
publisher: "株式会社一創"
---

# スカイライナーの不正アクセス：最大約337万件のID・パスワード取得と、暗号化保存の点検手順

京成電鉄は2026年11月13日、スカイライナー座席予約システムへの不正アクセスにより、スカイライナーチケットレスサービスの利用者情報が最大約337万件取得された可能性があると公表しました。対象はユーザーID、パスワード、カナ氏名、電話番号、メールアドレスの5項目です。この記事では、公表で確定した事実と未公表の項目を分け、利用者が今やることを短く示します。後半は予約・会員サービスを持つ開発者向けに、「パスワードは暗号化されている」という説明の読み方、暗号化保存からハッシュへの一括移行、パスワードリスト攻撃の検知、予約システムを全部止めずに守る設計を、実行できるPythonのコード付きで解説します。

## まとめ：スカイライナーの不正アクセスで公表された事実と、自社の予約システムで確かめる作業

確定しているのは、11月11日深夜の第三者による不正アクセス、ユーザーIDとパスワードを含む5項目が最大約337万件取得された可能性、座席予約システム全般の停止の3点です。侵入経路、パスワードの暗号化の方式と鍵の扱い、再開の時期は公表されていません。

利用者は、スカイライナーと同じパスワードを使っている他のサービスを先に変更します。IDとメールアドレスが一緒に出ているので、別のサービスで試される前提で動くのが安全です。

予約や会員のサービスを持つ開発者が自社で確かめる作業は、優先度の高い順に次の4つです。

1. パスワードが復号できる暗号化で保存されていないかを確かめ、復号できるなら一括でハッシュへ置き換える
2. ログイン履歴から、多数のIDを少しずつ試す送信元を抽出できる状態にする
3. 攻撃を検知したときに、会員ログインだけを止めて購入経路は残せるよう、停止の単位を機能ごとに分けておく
4. 漏えい時に全会員の再設定を強制するかどうかの基準と、報告の期限を先に決めておく

1つ目が最優先です。

## 11月13日の公表で確定した取得項目・最大約337万件と、侵入経路など未公表の範囲

### 11月11日深夜の不正アクセスから座席予約システム全般の停止までの経緯

京成電鉄の[11月13日の公表](https://www.keisei.co.jp/cms/files/keisei/MASTER/0110/OVIfkjTf.pdf)によると、不正アクセスが起きたのは11月11日（水）の深夜です。前日の[11月12日の公表](https://www.keisei.co.jp/cms/files/keisei/MASTER/0110/qWTY2f7x.pdf)で、同社はセキュリティ強化に伴う緊急メンテナンスとして、次の4つを一時中止しました。

- スカイライナーチケットレスサービス
- Skyliner e-ticket
- Skyliner e-ticket（顔認証システム）
- 旅行代理店でのスカイライナー券の購入

駅窓口と券売機での発売は続けています。すでにネットで購入していた人は駅で別途購入し、駅係員に申し出れば後日払い戻しを受けられる扱いです。クレジットカードの取り消しの反映には時間がかかると案内されています。11月13日の時点では、座席予約システム全般の利用を止めているとし、再開時期は示していません。

### ユーザーID・パスワード・カナ氏名・電話番号・メールアドレスの5項目と件数の読み方

取得された可能性があるのは、チケットレスサービスの利用者のユーザーID、パスワード、カナ氏名、電話番号、メールアドレスです。件数は最大約337万件とされています。

「最大」と「可能性」の2語は、読まれたレコードをログで絞り込みきれていない段階の書き方です。対象の利用者には登録メールアドレス宛に後日個別に連絡するとしているので、件数は今後の調査で小さくなる余地があります。一方、公表文はクレジットカード情報について触れていません。カード情報が取られていないと書かれているわけではないため、利用者は明細の確認を続けるのが無難です。

開発者の目で見て重いのは、IDとパスワードとメールアドレスが同じ行に揃っている点です。ログインに必要な組がそのまま出ている形で、パスワードを復元できるかどうかが被害の大きさを左右します。

### 利用者が今やる、同じパスワードを使う他サービスの変更と偽メールへの備え

使い回し先の変更が先です。スカイライナー側のシステムは止まっているので、利用者がすぐ変えられるのは他のサービスのほうです。メールアカウント、決済や送金に関わるサービス、交通や旅行の予約サービスの順に手を付けます。

もう1つは偽メールです。電話番号とメールアドレスとカナ氏名が揃っていると、本人の名前入りで「払い戻しの手続き」や「パスワードの再設定」をうたうメールやSMSを送れます。同社は対象者へメールで連絡するとしていますが、届いたメールのリンクから個人情報やカード番号を入力するのは避け、京成電鉄の公式サイトから案内をたどってください。同じ構図の旅行予約サービスの事案では、本物のメールの見分け方が論点になりました。

## 「パスワードは暗号化されているため復元されない」を開発者が読むときの前提条件

### 復号できる暗号化と元に戻せないハッシュ化、OWASPとNISTが示す保存方式の違い

公表文には、パスワードは暗号化されているため第三者によって復元されることはない、とあります。この「暗号化」がハッシュ化を指すのか、鍵で元に戻せる暗号化を指すのかは書かれていません。開発者が自社に当てはめるときは、2つを分けて考える必要があります。

[OWASPのPassword Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password%5FStorage%5FCheat%5FSheet.html)は、ハッシュは一方向の関数でパスワードの照合に向き、暗号化は双方向の関数なので攻撃者が平文を取り出せる、と区別しています。パスワードを暗号化して保存するのは平文が必要な例外的な場合に限り、可能なら暗号化保存が要らない設計にするよう求める内容です。[NIST SP 800-63B-4](https://pages.nist.gov/800-63-4/sp800-63b.html)の3.1.1.2も、パスワードはソルト付きでハッシュ化して保存すること（SHALL）と定め、ソルトは32ビット以上としています。

両者の違いの基礎は[ハッシュ化と暗号化の違いの解説](https://www.issoh.co.jp/column/details/13487/)で整理しています。

### 鍵の保管場所で決まる、暗号化したパスワードが漏えい後に復元される条件

可逆の暗号化だった場合、復元されないと言えるのは鍵が取られていないときだけです。条件は鍵の置き場所で決まります。

| 鍵の置き場所           | DBが漏えいしたときの見立て      |
| ---------------- | ------------------- |
| アプリの設定ファイルや環境変数  | 同じサーバーに入られたら鍵も取られうる |
| DBの別テーブル         | DBと一緒に取られる前提で扱う     |
| KMSやHSM（復号を都度依頼） | 復号の権限まで奪われたかで判断する   |
| 鍵を持たないハッシュ化      | 候補を総当たりする以外に戻す手段がない |

アプリが照合のたびに復号する設計だと、アプリのサーバーは鍵を使える状態にあります。侵入された箇所がアプリ側なら、鍵も届く範囲にあったと見るのが自然です。スカイライナーの事案で鍵がどこにあり、何が読まれたかは公表されていないので、この表は自社を点検するための一般論として読んでください。自社で同じ説明を公表するなら、方式と鍵の保管を具体的に書けるかどうかが試金石になります。

## 可逆暗号で保存したパスワードを一括でscryptのハッシュへ置き換える移行手順

### 平文を取り出せる今だけ全件を置き換えられる、ハッシュ間の移行との違い

MD5や古いSHA-256のハッシュから新しい方式へ移すときは、平文を持てるのは利用者がログインした瞬間だけです。そのため、ログイン成功時に1件ずつ置き換え、ログインしない休眠会員は別の手当てが要ります。この型の移行は[ハンゲの不正アクセスと保存方式の点検手順](https://www.issoh.co.jp/tech/details/19731/)でコード付きで扱いました。

可逆の暗号化で保存しているなら事情が逆になります。鍵があるので、利用者のログインを待たずに、今日中に全件をハッシュへ置き換えられます。休眠会員も残りません。移行先はOWASPが第一に挙げるArgon2id（メモリ19MiB・反復2回・並列度1が最低構成）か、使えない環境ならscrypt（N=2^17・r=8・p=1）です。次の例は追加の依存が要らない[標準ライブラリのhashlib.scrypt](https://docs.python.org/3/library/hashlib.html)を使います。

### sqlite3で動かす一括置き換えバッチと、移行後に鍵と暗号文を消す順序

次のコードは、暗号文の列を持つ会員テーブルを[sqlite3](https://docs.python.org/3/library/sqlite3.html)で作り、全件をscryptのハッシュへ置き換えて暗号文の列を空にします。復号の関数は既存システムの処理に差し替える部分で、ここでは動作確認のためにBase64で代用しています。Base64は暗号ではありません。

```
# 可逆暗号で保存したパスワードを、一括でscryptのハッシュへ置き換える移行バッチ（Python 3.9以上・標準ライブラリのみ）
import base64
import hashlib
import hmac
import os
import sqlite3

N, R, P = 2**17, 8, 1  # OWASPが示すscryptの最低ライン（N=2^17・r=8・p=1）
MAXMEM = 256 * 1024 * 1024


def decrypt_legacy(blob):
    # 既存システムの復号処理に差し替える部分。ここでは動作確認用にBase64で代用しており、暗号ではない
    return base64.b64decode(blob).decode()


def scrypt_hash(password):
    salt = os.urandom(16)
    dk = hashlib.scrypt(password.encode(), salt=salt, n=N, r=R, p=P, maxmem=MAXMEM, dklen=32)
    return f"scrypt${N}${R}${P}${base64.b64encode(salt).decode()}${base64.b64encode(dk).decode()}"


def verify(stored, password):
    _, n, r, p, salt, dk = stored.split("$")
    calc = hashlib.scrypt(password.encode(), salt=base64.b64decode(salt), n=int(n), r=int(r), p=int(p),
                          maxmem=MAXMEM, dklen=32)
    return hmac.compare_digest(calc, base64.b64decode(dk))


db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE users (user_id TEXT PRIMARY KEY, pw_enc TEXT, pw_hash TEXT)")
for uid, pw in [("u001", "skyline-2026"), ("u002", "narita#0731"), ("u003", "P@ssw0rd")]:
    db.execute("INSERT INTO users (user_id, pw_enc) VALUES (?, ?)", (uid, base64.b64encode(pw.encode()).decode()))

# 平文を取り出せる今だけ、ログインを待たずに全件を置き換えられる
rows = db.execute("SELECT user_id, pw_enc FROM users WHERE pw_hash IS NULL").fetchall()
for uid, enc in rows:
    db.execute("UPDATE users SET pw_hash = ?, pw_enc = NULL WHERE user_id = ?", (scrypt_hash(decrypt_legacy(enc)), uid))
db.commit()

left = db.execute("SELECT COUNT(*) FROM users WHERE pw_enc IS NOT NULL").fetchone()[0]
print(f"置き換え {len(rows)}件 / 暗号文の残り {left}件")
stored = db.execute("SELECT pw_hash FROM users WHERE user_id = 'u002'").fetchone()[0]
print("u002 正しいパスワード:", verify(stored, "narita#0731"))
print("u002 誤ったパスワード:", verify(stored, "narita#0730"))
print("保存形式の先頭:", stored.split("$")[:4])
```

Python 3.14.10（Windows）で2026年11月14日に実行した結果です。値は説明用に作ったもので、京成電鉄のデータや方式を再現したものではありません。

```
置き換え 3件 / 暗号文の残り 0件
u002 正しいパスワード: True
u002 誤ったパスワード: False
保存形式の先頭: ['scrypt', '131072', '8', '1']
```

3件とも暗号文の列が空になり、照合は新しいハッシュで通っています。N=2^17は1件あたり128MiBのメモリを使うため、数百万件を一括で処理するなら並列数と所要時間を小さな件数で測ってから流します。337万件を1件0.2秒で処理すると、単純計算で約187時間です。ワーカーを分けて並列にする前提で見積もってください。

順序にも決まりがあります。置き換えが全件終わり、ログインが新しいハッシュで通ることを確かめてから、暗号文の列を消し、最後に鍵を廃棄します。鍵を先に消すと戻れません。バックアップやレプリカに残った暗号文も、鍵の廃棄と同じ日に消す対象です。

## ID・パスワードとメールアドレスの組が出回ったときの、パスワードリスト攻撃の検知

### ログイン履歴を10分単位で集計し、多数のIDを試す送信元を抽出するSQL

漏えいしたIDとパスワードの組は、他のサービスのログイン画面へまとめて試されます。パスワードリスト攻撃です。仕組みと検知の指標は[パスワードリスト攻撃の解説](https://www.issoh.co.jp/tech/details/18730/)にまとめました。受ける側のサービスで最初に見るのは、1つの送信元が短時間に何種類のIDを試しているかと、その成功率です。本人の打ち間違いは同じIDで数回失敗し、攻撃は別々のIDを1回ずつ試して大半が失敗します。

```
# ログイン履歴から、パスワードリスト攻撃らしい送信元を10分単位で抽出する（Python 3.9以上・標準ライブラリのみ）
import sqlite3

db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE login_log (ts TEXT, ip TEXT, user_id TEXT, ok INTEGER)")

# 動作確認用のダミーログ。203.0.113.7 は別々のIDを1件ずつ試し、198.51.100.20 は本人の打ち間違い
logs = [(f"2026-11-14 02:{m:02d}:00", "203.0.113.7", f"user{m:03d}", 1 if m % 25 == 0 else 0) for m in range(50)]
logs += [("2026-11-14 02:03:00", "198.51.100.20", "user900", 0),
         ("2026-11-14 02:04:00", "198.51.100.20", "user900", 0),
         ("2026-11-14 02:05:00", "198.51.100.20", "user900", 1)]
db.executemany("INSERT INTO login_log VALUES (?, ?, ?, ?)", logs)

sql = """
SELECT ip,
       substr(ts, 1, 15) || '0' AS window_start,
       COUNT(*) AS attempts,
       COUNT(DISTINCT user_id) AS accounts,
       ROUND(1.0 * SUM(ok) / COUNT(*), 2) AS success_rate
FROM login_log
GROUP BY ip, window_start
HAVING COUNT(DISTINCT user_id) >= 5 AND 1.0 * SUM(ok) / COUNT(*) < 0.3
ORDER BY accounts DESC
"""
for row in db.execute(sql):
    print(row)

# 攻撃でログインに成功したIDは、パスワードの再設定とセッションの失効の対象にする
hit = db.execute("SELECT user_id FROM login_log WHERE ip = '203.0.113.7' AND ok = 1").fetchall()
print("再設定と失効の対象:", [h[0] for h in hit])
```

同じ環境で実行した結果です。

```
('203.0.113.7', '2026-11-14 02:00', 10, 10, 0.1)
('203.0.113.7', '2026-11-14 02:10', 10, 10, 0.0)
('203.0.113.7', '2026-11-14 02:20', 10, 10, 0.1)
('203.0.113.7', '2026-11-14 02:30', 10, 10, 0.0)
('203.0.113.7', '2026-11-14 02:40', 10, 10, 0.0)
再設定と失効の対象: ['user000', 'user025']
```

打ち間違いの送信元は1つのIDしか試していないので抽出されず、10分ごとに10種類のIDを試した送信元だけが残りました。閾値の5種類と成功率0.3は説明用の値です。本番では平常時の分布を1週間ほど集計してから決めます。攻撃側が送信元を分散させる場合に備え、サービス全体のログイン成功率が急に下がっていないかも並べて見ます。

### 検知した後の段階対応、CAPTCHAと多要素認証とIP制限の効き方の差

[OWASPのCredential Stuffing Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Credential%5FStuffing%5FPrevention%5FCheat%5FSheet.html)は、多要素認証を可能な限り入れることを最も効く対策としています。IPアドレスの遮断は回避が容易なので単独の主防御にしない、CAPTCHAは突破するツールやサービスがある、とも書いています。

実務で効く順に並べると次のとおりです。

1. 攻撃で成功したIDのセッションを失効させ、パスワードの再設定を求める
2. 新しい端末からのログインに、メールやSMSでの確認を挟む
3. 検知した送信元にだけCAPTCHAを出し、試行の間隔を延ばす
4. 送信元IPの遮断は、上の3つを入れたうえでの補助にとどめる

NIST SP 800-63B-4の3.2.2は、同じアカウントへの連続失敗を100回以下に制限することを求めています。ただしパスワードリスト攻撃は1つのIDを1回しか試さないので、アカウント単位の上限だけでは止まりません。送信元単位の集計と組み合わせる必要があります。ログインAPIでのレート制限とトークン失効の組み方は[API不正アクセス対策の実装手順](https://www.issoh.co.jp/tech/details/18346/)で解説しています。

## 座席予約システム全般を止めずに守る、会員ログインと購入経路を分ける設計

### 駅窓口・券売機で発売を続けた京成の対応と、ネット予約側の止め方の選択肢

京成電鉄はネット側の4サービスを止めた一方、駅窓口と券売機での発売は続けました。利用者は列車に乗れ、ネットで購入済みの人は払い戻しで救済されています。業務を全部止めない判断としては筋の通った形です。

ただしネット側は、チケットレスサービス、e-ticket、顔認証の仕組み、旅行代理店向けの販売がまとめて止まりました。侵入経路が分からない段階では、まとめて止めるのが安全側の判断です。ここで設計の差が出ます。どの機能がどのデータに触れるかを分けて作っていれば、止める範囲を狭められます。外から見て分かるのは止めた範囲までで、京成の内部構成は公表されていません。以下は自社の設計を見直すための一般論です。

### 会員ログインだけを止めて空席照会と購入を残す、機能単位の停止スイッチ

止める単位は、触れるデータで分けます。

| 機能           | 触れるデータ        | 事故時の扱い        |
| ------------ | ------------- | ------------- |
| 空席照会         | 座席在庫のみ        | 止めない          |
| 会員ログイン・マイページ | ID・パスワード・個人情報 | 最初に止める        |
| 非会員での購入      | 決済代行のトークン・連絡先 | 経路が無関係と確かめて残す |
| 外部販売（代理店API） | 在庫と発券         | APIキーを再発行して再開 |

この分け方が効くのは、機能ごとに別々のスイッチで止められる場合だけです。設定値やフィーチャーフラグで「会員ログイン」「マイページ」「代理店API」を個別にオフにでき、オフのときは保守中の画面を返すようにしておきます。会員DBへの接続権限も機能ごとに分けておけば、空席照会のサーバーが侵入されても会員テーブルは読めません。

見送る場面もはっきりさせておきます。侵入経路が分からず、会員DBと在庫DBが同じ権限で読めるつくりなら、部分停止は選びません。全部止めるのが正解です。部分停止は、権限の分離が事前にできているシステムだけが取れる手段です。

## 判断：会員全員のパスワード再設定を強制する場面と、個人情報保護委員会への報告

### 鍵の所在を説明できないなら全会員の再設定を強制する、という線引き

自社で同じ事態になったら、判断基準となるのは次の条件です。パスワードを可逆の暗号化で保存していて、鍵が侵入された範囲の外にあったと説明できない場合は、全会員のパスワード再設定を強制し、発行済みのセッションも無効にします。NIST SP 800-63B-4の3.1.1.2は、侵害の証拠がある場合はパスワードの変更を強制すること（SHALL）と定めています。

再設定を対象者に絞ってよいのは、Argon2idやscryptを推奨値で使ったハッシュ化で保存し、かつ読まれたレコードをログで特定できた場合です。どちらか一方でも欠けるなら全員です。

再設定の案内は、偽メールが最も効く時期と重なります。案内メールに再設定のリンクを入れず、公式サイトやアプリから開くよう書くほうが安全です。

### 報告4類型に当てはめたときの速報3〜5日と、確報60日の期限

[個人情報保護委員会](https://www.ppc.go.jp/personalinfo/legal/leakAction/)は、報告が必要な事態として4つの類型を示しています。不正の目的によるおそれがある漏えい、財産的被害のおそれがある漏えい、本人の数が1,000人を超える漏えい、要配慮個人情報の漏えいです。外部からの不正アクセスで最大約337万件という今回の型は、不正の目的によるおそれと1,000人超の2つに当たります。

速報は発覚から3〜5日以内が目安です。確報は30日以内、不正の目的によるおそれがある場合は60日以内とされています。報告の流れは[個人情報保護委員会への報告義務の実務](https://www.issoh.co.jp/column/details/17840/)で解説しました。自社の予約・会員システムのパスワード保存方式や、ログイン周りの守りを第三者の目で確かめたい場合は、[脆弱性診断・セキュリティ診断](https://www.issoh.co.jp/service/system/vulnerability/)で相談できます。

## よくある質問

スカイライナーの不正アクセスについて、利用者と開発者から出やすい質問をまとめました。2026年11月14日時点の公表内容にもとづきます。

### スカイライナーの不正アクセスで取得されたのはどんな情報ですか？

京成電鉄の公表では、スカイライナーチケットレスサービスの利用者のユーザーID、パスワード、カナ氏名、電話番号、メールアドレスが取得された可能性があり、件数は最大約337万件です。パスワードは暗号化されているため第三者に復元されることはないと説明しています。クレジットカード情報については公表文に記載がありません。

### 自分が対象かどうかはどうやって確かめればよいですか？

同社は対象の利用者に、登録されているメールアドレス宛に後日個別に連絡するとしています。届いたメールのリンクから入力するのは避け、京成電鉄の公式サイトにある公表文と問い合わせ窓口で確かめてください。名前入りの偽メールやSMSが届く可能性があります。

### スカイライナーで使っていたパスワードは変更したほうがいいですか？

座席予約システム全般が止まっているため、先に同じパスワードを使っている他のサービスを変更します。メールアカウントと、決済や送金に関わるサービスが優先です。スカイライナー側のパスワードは、再開時の同社の案内に沿って変更します。

### スカイライナーのチケットレスサービスはいつ再開しますか？

11月14日時点で再開の時期は示されていません。その間も駅窓口と券売機ではスカイライナー券を発売しています。ネットで購入済みの人は駅で別途購入し、駅係員に申し出れば後日払い戻しを受けられます。

### パスワードの暗号化とハッシュ化はどう違うのですか？

暗号化は鍵があれば元の値に戻せる双方向の処理で、ハッシュ化は元に戻す手段がない一方向の処理です。OWASPはパスワードの保存にハッシュ化を使い、暗号化は平文が必要な例外に限るとしています。暗号化で保存している場合、鍵が一緒に取られると復元されるため、安全性は鍵の保管場所で決まります。

## 関連記事

- [パスワードリスト攻撃とは？検知の指標と漏えい照合・レート制限の実装](https://www.issoh.co.jp/tech/details/18730/)：漏えいしたID・パスワードの組を他サービスで試す攻撃の仕組みと検知
- [ハンゲの不正アクセス：ハッシュ化パスワード6.2万件の漏えいと、保存方式の点検手順](https://www.issoh.co.jp/tech/details/19731/)：ハッシュ間の移行とログイン時の再ハッシュ
- [skyticketの不正アクセスと対象確認・本物メールの見分け方](https://www.issoh.co.jp/tech/details/18322/)：旅行予約サービスでパスワードを含む情報が漏えいした事案
- [API不正アクセス対策の実装手順：JPCERT注意喚起に沿うレート制限・認可・トークン失効とアプリ解析・SSRF対策](https://www.issoh.co.jp/tech/details/18346/)：ログインAPIのレート制限とトークン失効
- [個人情報保護委員会への報告義務｜対象4類型・速報3〜5日と確報30日の実務](https://www.issoh.co.jp/column/details/17840/)：不正アクセスで漏えいしたときの報告の流れ

---

出典: [スカイライナーの不正アクセス：最大約337万件のID・パスワード取得と、暗号化保存の点検手順](<https://www.issoh.co.jp/tech/details/19751/>)（株式会社一創）
