VPSがハッキングされたときの対処法
VPSが侵害されたらクリーンアップを試みず、プロバイダー側のファイアウォールで隔離します。証拠としてディスクをスナップショットし、保持していた全キーをローテーションして、クリーンなイメージから再構築します。
ハッキングされた VPS をクリーンアップしない
VPS がハッキングされた場合、最も重要な判断は、コマンドを実行する前に行います。マシンをクリーンアップしようとしてはいけません。プロバイダー側で隔離し、証拠としてディスクのスナップショットを取得し、その VPS が保持していたすべての認証情報をローテーションしたうえで、信頼できるソースから新しいサーバーを構築し直します。
rootkit が除去されたことは証明できません。証明に使えるツール自体を攻撃者が制御しているためです。
これが要点です。その仕組みを説明します。root に到達した攻撃者は ps を置き換え、特定のプロセス ID が出力に一度も現れないようにできます。/etc/ld.so.preload の 1 行で、システム上の動的リンクされたすべてのプログラムに攻撃者のコードを読み込ませることができます。そのため、ls、ss、find はすべて同じ方法で一貫して偽の情報を返します。ロード可能なカーネルモジュールはシステムコールより下位でファイルを隠せるため、新たにダウンロードしたバイナリでさえ、ディスクがクリーンであるかのように認識します。マイナーを削除すると CPU 使用率のグラフは下がり、サーバーは静かになります。しかし、静かな状態は、バックドアが正常に動作している場合にも見られます。
再構築のコストは、感じるほど大きくありません。一般的な VPS は、いくつかのパッケージ、1 つの設定ディレクトリ、1 つのデータセットで構成されるため、再構築は終わりのある有限の作業です。攻撃者が行った変更をすべて探し出す作業には終わりがなく、完全な証明に到達することもありません。
本当に侵害されているか確認する
ハッキングされたと報告されたサーバーの多くは、実際には侵害されていません。1 日に数千件の SSH ログイン失敗が発生しても、インターネット上の通常のバックグラウンドノイズである可能性があります。すべてのパブリック IPv4 アドレスは継続的にスキャンされているためです。lastb の出力に root や admin の試行が大量に含まれていても、スキャナーがポートを見つけたことを示すだけです。誰かが侵入したことを意味するわけではありません。
次の兆候は、実際の問題を示します。
Accepted password for root from 203.0.113.7など、自分では説明できないログイン成功。- 自分で追加していない鍵が
authorized_keysにある。 - サーバーから外部へ送信されるネットワークトラフィックについて、ホストから不正利用の通知を受けた。
- カーネルスレッドからコピーした名前を持つプロセスが CPU を 100% 使用している。公開された Redis や Docker のソケット経由で侵入したマイナーは、
kdevtmpfsiやkinsingなどの名前で報告されることがよくあります。 - どのサービスも使用していないアドレスへの外向き接続。
カーネルスレッドを偽装しているかどうかは、簡単に確認できます。本物のカーネルスレッドは角括弧内に表示され、対応する実行ファイルを持ちません。そのため、sudo ls -l /proc/<pid>/exe は本物のカーネルスレッドに対して No such file or directory で失敗します。[kworker/0:2] として表示されたプロセスに、/tmp 配下を指す exe リンクがある場合、それはカーネル名を装った通常のユーザープログラムです。
システムが偽の情報を返す可能性を考慮して、これらの確認を実行してください。これらの確認で、何か問題があるかどうかは判断できます。問題がないとは判断できません。
プロバイダー側でネットワークを遮断し、サーバー内部からは行わない
最初に隔離します。攻撃者がまだシェルを操作できる状態では、その後の作業はすべて無駄になるためです。ログの確認、鍵のローテーション、データの復元は、攻撃者に監視されている状態では意味がありません。
プロバイダーのコントロールパネルで、オペレーティングシステムの外部で動作するネットワークファイアウォールを使用して実施します。受信と送信の両方を拒否し、Web コンソールを接続手段として残します。そこで適用されたルールは、ディスク上で何が起きても維持されます。
サーバー内部から実施してはいけない理由は2つあります。侵害されたカーネル内で設定するファイアウォールは、そのカーネルによって適用されます。root は、あなたが設定したのと同じように簡単に nftables のルールをフラッシュできます。また、sudo ip link set enp1s0 down を SSH 経由で実行すると、最初に自分のセッションが切断されます。その結果、調査中のマシンから締め出されます。
送信も受信と同様にブロックします。リバースシェルはサーバーから攻撃者へ発信するため、受信だけをブロックしても、確立済みの接続は正常に動作し続けます。プロバイダーが受信ルールしか提供していない場合、残る選択肢はネットワークインターフェースを切断するか、インスタンスの電源を切ることです。
まだ再起動しないでください。まず /var/log/journal が存在するか確認します。そのディレクトリがない場合、journald はメモリ上にある /run/log/journal に書き込んでいます。この場合、再起動すると侵入の記録が消失します。実行中のプロセスも再起動時に消えます。プロセスのコマンドラインは、多くの場合、取得できる最も明確な証拠です。
操作を始める前にディスクのスナップショットを取得する
ここでは、スナップショットとバックアップは異なる役割を持ちます。今取得するスナップショットは、侵害されたディスクのコピーです。これは証拠であり、誤って何かを上書きした後に元へ戻すための唯一の手段です。以前のバックアップは、復旧に使用します。プロバイダーのパネルでこの2つの用語が厳密に区別されていない場合は、先に VPS のスナップショットと実際のバックアップの違い を確認してください。保持ルールと復元時の動作は同じではありません。
再度ログインする前に、プロバイダーのパネルからスナップショットを取得します。稼働中に取得するスナップショットはクラッシュ整合性のある状態になります。これは、その瞬間のディスクの状態を取得するもので、電源を突然切断した場合と同じです。証拠としては問題ありません。誤って復元されないよう、名前を付けてください。COMPROMISED-do-not-restore-2026-08-12 のように、明確で分かりやすい名前が適しています。調査が完了し、ホストとの不正利用に関するチケットが終了するまで保持してください。
SSH が使えなくなった場合のログイン方法
方法は 2 つあります。どちらもプロバイダーパネルから利用します。Web コンソール(VNC またはシリアル)は、キーボードを接続した場合と同じようにマシンへ接続します。sshd が停止している場合、ファイアウォールの設定を誤った場合、攻撃者が SSH ポートを変更した場合でも使用できます。ローカルパスワードで認証するため、鍵のみで認証するサーバーでは、コンソールを使用する前に root パスワードをリセットする必要がある場合があります。
より適切なのはレスキューモードです。ディスクを接続した状態で、ディスク上のシステムを実行せずに小さなライブシステムを起動します。そのため、実行するコマンドを信頼できます。侵害されたカーネルやバイナリは実行されません。ディスクは読み取り専用でマウントします。
lsblk -f
sudo mkdir -p /mnt/victim
sudo mount -o ro /dev/vda1 /mnt/victimlsblk に通常のパーティションではなく LVM(論理ボリュームマネージャー)のボリュームが表示される場合は、まず sudo vgchange -ay でボリュームを有効化します。その後、/dev/mapper/ の下に表示されるデバイスをマウントします。
マウントしたディスク内を確認するために chroot しないでください。chroot を実行すると、攻撃者のバイナリが自分の権限で実行されます。これでは、レスキューモードで起動した目的が失われます。
信頼できる証拠を収集する
レスキューモードで、ディスクを /mnt/victim に読み取り専用でマウントした状態で、次のコマンドを実行します。侵入時刻を特定できるため、まずログインを確認します。時間範囲が分かれば、ほかの調査も進めやすくなります。
sudo grep -aE 'Accepted (password|publickey)' /mnt/victim/var/log/auth.log
sudo last -f /mnt/victim/var/log/wtmp
sudo lastb -f /mnt/victim/var/log/btmp
sudo journalctl -D /mnt/victim/var/log/journal -u ssh --since "2026-07-01"/var/log/auth.log がないこと自体は不審ではありません。現在の Ubuntu イメージには rsyslog が含まれないものがあり、sshd が journal にのみログを出力するためです。journalctl -D の行はその journal を読み取ります。確認すべきなのは、連続しているはずのログに空白期間があることや、ログファイルが 0 バイトに切り詰められていることです。ログの消去はよく行われますが、通常は手際がよくありません。
次に、アカウントと鍵を確認します。
sudo find /mnt/victim/root /mnt/victim/home -name 'authorized_keys*' -exec ls -l {} +
sudo awk -F: '$3 == 0 {print $1}' /mnt/victim/etc/passwd
sudo lsattr /mnt/victim/root/.ssh/authorized_keysawk の行は、ユーザー ID が 0 のすべてのアカウントを出力します。その出力で root 以外のものがあれば、それは 2 つ目の root アカウントです。find のパターンは意図的に authorized_keys2 も一致させています。OpenSSH はデフォルトで両方のファイル名を読み取り、後者は見落としやすいためです。lsattr が属性一覧に i を出力する場合、そのファイルは immutable です。攻撃者はこのフラグを設定し、鍵を削除しようとすると Operation not permitted で失敗するようにします。その結果、疲弊した管理者が編集に成功したと思い込むことがあります。
永続化の設定が隠れる場所は限られているため、すべて確認します。
sudo ls -lt /mnt/victim/etc/systemd/system
sudo ls -l /mnt/victim/etc/cron.d /mnt/victim/var/spool/cron/crontabs
sudo cat /mnt/victim/etc/ld.so.preload
sudo grep -rnE 'curl|wget|base64|/dev/tcp' /mnt/victim/etc/update-motd.d /mnt/victim/etc/rc.local /mnt/victim/root/.bashrc /mnt/victim/root/.profile通常の Ubuntu または Debian システムには /etc/ld.so.preload が存在しないため、No such file or directory が正常な結果です。内容が少しでもあれば注意が必要です。ログインファイルが base64 -d の出力をシェルへパイプしている場合も同様です。正当な設定であれば、自身のテキストを隠す必要はありません。
タイムラインは変更時刻ではなく、inode の変更時刻を基に作成します。
sudo find /mnt/victim -xdev -newerct '2026-08-01' -type f -printf '%TF %TT %p\n' | sorttouch は攻撃者が望む任意の値に変更時刻を設定できるため、mtime は簡単に偽装できます。変更時刻(ctime)は inode に変更があるたびに更新され、touch で過去の時刻へ戻すことはできません。そのため、-newerct を使うと、最近書き込まれたものをより正確に一覧できます。ただし、これも証明にはなりません。root はシステム時刻を変更したり、ブロックデバイスへ直接書き込んだりできるためです。
パッケージの整合性確認には、1 つのコマンドと 1 つの注意点があります。稼働中のシステムでは、sudo dpkg --verify はチェックサムが一致しないパッケージファイルごとに 1 行を出力し、チェックサム列に 5 を表示します。sudo debsums -ac は、debsums パッケージがインストールされている場合、設定ファイルも含めて同じ確認を行います。結果は一方向にだけ解釈してください。変更された /usr/sbin/sshd は実際の証拠です。しかし、クリーンな結果からは何も証明できません。バイナリを置き換えた同じ root アカウントが、/var/lib/dpkg/info/ にあるチェックサム一覧も書き換えられるためです。rkhunter や chkrootkit などの rootkit スキャナーにも同じ原則が当てはまります。検出結果は情報になりますが、何も検出されなくても安全が確認されたわけではありません。
破壊的な操作を行う前に、収集したものをマシンの外部へコピーします。
sudo tar --ignore-failed-read -C /mnt/victim -czf /root/evidence-2026-08-12.tgz \
var/log etc root/.ssh root/.bash_history home
sha256sum /root/evidence-2026-08-12.tgzそのハッシュをサーバー外の場所に記録します。これが保険請求や警察への被害届に発展した場合、収集後にアーカイブが変更されていないことを示せるかどうかが、証拠と単なるファイルの入ったフォルダーの違いになります。調査中に誤って何かを削除することは珍しくありません。スナップショットとこのアーカイブがあれば、その事態から復旧できます。後から不適切な rm を元に戻すのは、想像以上に困難です。rm -rf で削除したファイルを復元するで説明しています。
侵入経路を特定します
侵入経路を閉じずに再構築すると、再び侵害されます。多くの場合、数日以内です。最初に侵入を見つけたスキャンが止まらないためです。1 台のサーバーへの侵害は、主に4つの経路から発生します。
SSH のパスワードログイン。 身に覚えのないアドレスからの Accepted password for root 行があれば、それだけで原因を特定できます。/etc/ssh/sshd_config 内の PasswordAuthentication と、/etc/ssh/sshd_config.d/ 配下のすべてのファイルを確認します。sshd はキーワードについて最初に取得した値を使用します。Ubuntu では Include 行がメインファイルの先頭にあるため、後方で編集した設定よりも、追加された設定ファイルの内容が優先されます。
認証なしで公開されたサービス。 6379 番ポートの Redis、2375 番ポートの Docker API、0.0.0.0 ではなく 127.0.0.1 にバインドされたデータベースなどです。Docker は特に見落とされやすい例です。コンテナのポートを公開すると、ufw のチェーンより先に評価される DNAT(宛先ネットワークアドレス変換)ルールが挿入されます。そのため、ufw status ではポートがブロックされているように見えても、その背後のコンテナはインターネット全体から応答できます。再構築前にこの仕組みを理解してください。Docker の公開ポートが ufw を迂回する理由では、ルールの評価順序と修正方法を説明しています。
パッチが適用されていない Web アプリケーション。 Web サーバーのアクセスログで、最初に不審な時刻が記録された前後を調べます。アップロード用パスまたは管理用パスへの POST を探し、Web ルート配下で変更時刻が一致するファイルを確認します。uploads ディレクトリに不審な PHP ファイルがあるのは典型的な結果です。
漏えいした認証情報。 リポジトリにコミットされた鍵、チャットに貼り付けられたトークン、設定ミスのある Web サーバーによって静的ファイルとして提供された .env ファイルなどです。自動化ではこのようなミスが簡単に起こるため、Secret を AI エージェントとその設定ファイルから分離することが重要です。
すべて確認しても経路を特定できない場合は、認証情報が漏えいしたと仮定してください。そのマシンが保持していたすべての Secret を公開済みとして扱います。
マシンからアクセス可能だったすべての認証情報を更新する
ネットワークを切断した後に更新し、決してその前に行わないでください。攻撃者が接続中に更新すると、新しい秘密情報をそのまま渡すことになります。
- サーバーに保存されているすべての SSH 秘密鍵、および対応する公開鍵を信頼していた外部のすべてのアカウント。
ssh -Aでサーバーに転送した鍵。Agent forwarding により/tmpの下にソケットが作成されます。そのマシンの root は、セッションが開いている間、鍵が受け入れられる任意の場所で、その鍵を使ってあなたとして認証できます。.envファイル、systemd のEnvironment=行、CI の設定、プロバイダーの認証情報に含まれる API トークン。- データベースのパスワード、およびそれを使用するアプリケーションアカウント。
- サーバーが保持していた TLS (transport layer security) 秘密鍵。証明書を再発行し、古い証明書を失効させます。
- ホスティングアカウントのパスワード。2 要素認証も有効にします。その管理パネルから、所有するすべてのサーバーを再構築、スナップショット取得、コンソール接続できるため、実質的な境界となります。
- 侵害中にそのホストのシェルセッションへ入力したすべてのパスワード。root は入力中の端末セッションを記録できるためです。
そのマシンのパスワードを他の場所でも使用している場合は、そこでも変更してください。パスワードの再利用により、1 台の侵害された VPS からメールアカウントまで侵害されます。
再構築チェックリスト
- 新しいディストリビューションイメージから新規サーバーを作成します。侵害されたサーバーの snapshot や、root ファイルシステム全体の復元から作成してはいけません。
- パッケージはディストリビューションのリポジトリからインストールします。古いディスクからバイナリをコピーしてはいけません。
- データだけを復元します。タイムライン上で最初に侵害の証拠が確認された時点より前の日付のバックアップを使用します。対象はデータベースダンプ、アップロードファイル、アプリケーション状態です。
/etc、/usr、古い unit ファイルは復元しません。 - ローテーションした Secret は手動で入力します。古い
.envをコピーしてはいけません。 - 復元した Web コンテンツを再び提供する前に、侵入期間中に追加されたファイルがないか確認します。
- 外部公開前にセキュリティを強化します。SSH は鍵認証だけにし、root ではない作業用アカウントを使用し、インバウンド通信のデフォルトを拒否するファイアウォールを設定します。また、必要以上に広く公開するサービスがないようにします。新しい VPS の最初の10分間の手順を実施し、続いてSSH を適切に強化する手順を行い、さらにUbuntu 24.04 に fail2ban を追加することでログイン試行によるノイズを減らします。各サービスには最小権限の専用アカウントを割り当て、次の侵入経路が root の侵入経路にならないようにします。
- 古いサーバーを停止し、調査と abuse ticket の対応が完了するまで snapshot を保持します。
- バックアップを見直します。手順3が推測に頼るものだった場合、本当の教訓は、侵入前までさかのぼれるだけのバックアップ履歴がなかったことです。次回、クリーンな復元ポイントを確保するには、長期保持するバージョン管理付きのオフサーバーバックアップが必要です。VPS の restic バックアップなら、その両方を実現できます。
侵入時期を特定できない場合、安全なバックアップを選択できません。その場合は、目視で確認できるデータだけを復元します。たとえば、内容を読める SQL ダンプや、一覧表示できる画像ディレクトリです。実行可能なものはすべて疑わしいものとして扱い、リポジトリから再度インストールします。
ホストから届いた abuse 通知の意味
多くの場合、サーバーが侵害されたことを知るきっかけは、自分の監視ではなくプロバイダーからの通知です。ホスト側は、他のネットワークに対する SSH ブルートフォース攻撃、25 番ポートからの spam、リフレクション攻撃への加担など、外向きの network traffic を確認できます。通常、チケットには発生時刻、ポート、通信のサンプルが記載され、数時間単位の対応期限も示されます。
回答してください。サーバーを隔離して再構築中であることしか伝えられない場合でも、返信が必要です。チケットに回答がないと、プロバイダーはサーバーを null-route にしたり、停止したりします。その結果、インシデントが障害に発展します。続いて、報告の根拠となった raw log の行を依頼してください。その時刻はサーバーの外部で記録されたものです。そのため、攻撃者が編集できなかったタイムラインの情報となります。ディスク上の情報よりも、侵入時期を正確に特定できることもよくあります。
顧客サーバーの侵害対応はホストにとって通常の業務です。適切に対応したことが不利益に扱われることはありません。VPS ホスティングが安全かどうかという広い問題は、主に顧客がどのように設定するかで決まります。そして今、その部分を最初からやり直すことになります。
専門家に相談すべき場合
- サーバーに他者の個人データが保存されていた場合。GDPR(General Data Protection Regulation)では、個人データ侵害は不当な遅延なく監督機関へ報告する必要があります。実行可能な場合は、侵害を認識してから72時間以内に報告します。その期限の起算時点を判断するのは、システム管理ではなく法務の業務です。
- 決済カードデータが対象範囲に含まれていた場合。カードブランドは承認済みのフォレンジック調査員を要求します。自分で調査すると、証拠や調査手続きを損なう可能性があります。
- 恐喝要求を受けている場合、またはデータが暗号化されている場合。
- そのマシンから他のマシンへ接続できた場合。対象には、内部ネットワーク、ハイパーバイザー、本番環境の認証情報を保持する CI runner などがあります。グループ内の 1 台が侵害された場合、問題がないと証明されるまではグループ全体のインシデントとして扱います。
- 保険請求や法執行機関への提出に耐える証拠が必要な場合。スナップショットを取得した時点で作業を止め、ディスク全体のイメージを取得し、誰がいつ取り扱ったかを記録します。
自分のサービスだけを実行する単一の VPS で、他者のデータが保存されていない場合は、上記の手順ですべて完了します。プロバイダー側で隔離します。証拠としてスナップショットを取得します。まだ信頼できるデータを収集します。すべての認証情報をローテーションします。クリーンな環境に再構築します。
FAQ
ハッキングされた VPS を再構築せずにクリーンアップできますか?
確実にはできません。侵害されたシステム自身に、自分の状態を報告させることになるためです。置き換えられた ps はプロセスを隠し、/etc/ld.so.preload に挿入された 1 行のコードは、実行するすべての動的リンクツールにコードを注入できます。カーネルモジュールは、すべてのプログラムから一度にファイルを隠すこともできます。何かを見つけることはできるため、検出結果には意味があります。しかし、存在しないことは証明できないため、何も検出されなくても安全とはいえません。サーバーに重要なものが何もなく、再び侵害される可能性を受け入れられる場合に限り、クリーンアップは許容できます。
侵害されたサーバーは電源を切るべきですか、それとも稼働させておくべきですか?
まずプロバイダー側でネットワークを遮断し、その後、スナップショットを取得して実行中のプロセスを確認できる時間だけ稼働させておきます。電源を切るとプロセス一覧が失われます。また、/var/log/journal が存在しない場合は、journald が /run の下のメモリに書き込むため、ジャーナル全体も削除されます。ただし、他のネットワークを現在攻撃しており、外向きのトラフィックを遮断できない場合は、電源を切ってください。被害の停止を証拠の保持より優先します。
攻撃者が侵入した時期を調べるにはどうすればよいですか?
/var/log/auth.log またはジャーナルで、説明できない最も早い Accepted password または Accepted publickey の行を探します。次に、変更時刻の一覧である find / -xdev -newerct 'YYYY-MM-DD' -type f と照合します。ctime は mtime より偽装が難しいためです。その後、両方をプロバイダーの abuse ticket に記録されたタイムスタンプと比較します。これらの時刻はマシン外で記録されており、編集できないためです。3 つの日付のうち最も早い日付より前のバックアップを選びます。時系列が一致しない場合は、侵害がバックアップ履歴より前に発生したと判断し、検査できるデータだけを復元します。
侵害後にバックアップを安全に復元できますか?
検査を行えば、データは通常復元できます。システムファイルは安全とはいえません。侵入後に取得したバックアップにはバックドアが含まれているため、root ファイルシステム全体を復元すると攻撃者も復元されます。バックアップリポジトリ自体も確認してください。リポジトリの認証情報が侵害されたサーバーに保存されていた場合、履歴が削除または改ざんされている可能性があります。そのため、追記専用または pull ベースのバックアップ先が有効です。アプリケーションデータを復元し、その後、ディストリビューションのリポジトリからソフトウェアを再インストールします。
VPS が侵害されたことを誰かに知らせる必要がありますか?
ホストからの abuse notice には必ず返信してください。それ以外については、マシン上に誰のデータが保存されていたかによって異なります。他人に属する個人データがあった場合、GDPR が監督機関への 72-hour notification を求める場合があるなど、法的な報告義務が発生する可能性があります。ユーザーの認証情報がサーバーに保存されていた場合は、ユーザーに知らせ、他のサービスでもパスワードを変更できるようにします。サーバー上の鍵で、code host や cloud account など第三者のシステムへのアクセスを許可していた場合は、それらのプロバイダーに知らせ、不正利用がないか確認してもらいます。他人のデータを保存していない個人用サーバーであれば、abuse ticket 以外の義務はありません。