OSのパッケージバージョン管理とパッチ当て:Ubuntu版
INDEX
サーバーをセキュアに保つには、まず「何が動いていて、何がインストールされているか」を知る必要があります。把握していないものは、更新も削除もできないからです。
この記事では、Ubuntu の状態を確認する手順と、確認したあとにパッチを当てる・バージョンを上げる手順をまとめます。1〜5 章のコマンドはすべて読み取り専用です。システムを変更するのは 6 章と 7 章です。7 章では、2026 年 10 月に公開された PHP の脆弱性「PHP2Shell」を例に、特定のソフトウェアだけを緊急で更新する流れを追います。
各コードブロックの上に Command(入力するもの)と Output(表示される結果)の見出しを付けています。 出力例は Ubuntu 26.04 LTS(EC2)での表示形式に合わせていますが、ホスト名・ユーザー名・パッケージの一覧やバージョンなどは説明用の例です。
確認する順番#
次の順に見ていくと整理しやすくなります。
- OS とカーネル
- インストール済みパッケージ
- apt の管理外のもの
- パッケージのバージョンと修正状況
- 更新状況・サービス・ポート・ユーザー
- パッチ適用とバージョンアップ
- 事例: PHP2Shell に対応する
1. OS とカーネルの情報#
OS のバージョンを確認します。
Command
cat /etc/os-release
Output(抜粋)
PRETTY_NAME="Ubuntu 26.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="26.04"
VERSION="26.04.1 LTS (Resolute Raccoon)"
VERSION_CODENAME=resolute
ID=ubuntu
VERSION_CODENAME(この例では resolute)は、あとで出てくる apt のリポジトリ名に使われます。
カーネルのバージョンを確認します。
Command
uname -a
Output
Linux web01 7.0.0-1014-aws #14-Ubuntu SMP PREEMPT Fri Sep 18 00:50:36 UTC 2026 aarch64 GNU/Linux
7.0.0-1014-aws がカーネルのバージョンです。-aws は AWS 向けに調整されたカーネルであることを示します。
ホスト名や仮想化の種類をまとめて確認します。
Command
hostnamectl
Output(抜粋)
Static hostname: web01
Icon name: computer-vm
Chassis: vm
Virtualization: amazon
Operating System: Ubuntu 26.04.1 LTS
Kernel: Linux 7.0.0-1014-aws
Architecture: arm64
Hardware Vendor: Amazon EC2
Hardware Model: t4g.small
2. インストール済みパッケージ#
apt list の使い方#
apt list は、パッケージの一覧を表示するコマンドです。基本の形は次のとおりです。
apt list [オプション] [パッケージ名のパターン]
よく使うオプションは次のとおりです。
| オプション | 内容 |
|---|---|
| (なし) | リポジトリにあるパッケージをすべて表示 |
--installed | インストール済みのパッケージだけを表示 |
--upgradable | アップグレードできるパッケージだけを表示 |
--manual-installed | 名前を指定して入れたパッケージだけを表示 |
--all-versions / -a | リポジトリにあるすべてのバージョンを表示 |
パッケージ名には * などのパターンが使えます。シェルに展開されないように、クォートで囲みます。
Command
apt list 'openssh*'
Output(抜粋)
Listing...
openssh-client/resolute-updates,resolute-security,now 1:10.2p1-2ubuntu3.8 arm64 [installed,automatic]
openssh-known-hosts/resolute 0.6.3-1build1 all
openssh-server/resolute-updates,resolute-security,now 1:10.2p1-2ubuntu3.8 arm64 [installed]
openssh-sftp-server/resolute-updates,resolute-security,now 1:10.2p1-2ubuntu3.8 arm64 [installed]
openssh-tests/resolute-updates,resolute-security 1:10.2p1-2ubuntu3.8 arm64
1 行は次のように読みます。
openssh-server / resolute-updates,resolute-security,now 1:10.2p1-2ubuntu3.8 arm64 [installed]
パッケージ名 / 取得元のリポジトリ バージョン CPU 状態
- 取得元:
resolute-securityはセキュリティ修正、resolute-updatesは通常の修正です。nowは、このマシンに入っていることを表します。 - 状態: 角かっこの中が、このマシンでの状態です。何もなければ未インストールです。
| 状態の表示 | 意味 |
|---|---|
[installed] | 名前を指定してインストールしたもの |
[installed,automatic] | 依存関係として自動で入ったもの |
[installed,local] | 現在のリポジトリにないもの(.deb を直接入れた、リポジトリを外した など) |
[upgradable from: X] | インストール済みで、新しいバージョンがある(X が今のバージョン) |
[residual-config] | 削除済みだが設定ファイルが残っている |
[installed,local] は、更新が届かなくなっている可能性があるので要注意です。
インストール済みのパッケージ数を数えます。
Command
apt list --installed 2>/dev/null | wc -l
Output
1422
先頭の Listing... の 1 行も数えているので、実際の数は 1 つ少なくなります。2>/dev/null は、次の警告を消すためのものです。
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
この警告のとおり、apt の出力形式は将来変わる可能性があります。スクリプトで処理するときは、あとで出てくる dpkg-query を使う方が安全です。
インストール済みの中から、特定のものだけを探すこともできます。
Command
apt list --installed 'python3-*' 2>/dev/null | head -5
Output
Listing...
python3-apt/resolute,now 3.1.0ubuntu1 arm64 [installed,automatic]
python3-attr/resolute,now 25.3.0-1 all [installed,automatic]
python3-babel/resolute,now 2.17.0-1 all [installed,automatic]
python3-bcrypt/resolute,now 4.2.0-2.1build2 arm64 [installed,automatic]
同じパッケージの、リポジトリにあるバージョンをすべて表示します。
Command
apt list -a openssl
Output
Listing...
openssl/resolute-updates,resolute-security,now 3.5.5-1ubuntu3.7 arm64 [installed,automatic]
openssl/resolute 3.5.5-1ubuntu3 arm64
下の resolute はリリース時のバージョン、上の resolute-security がその後に配信された修正版です。今は修正版が入っていることが分かります。
dpkg-query と apt list --installed の違い#
どちらもインストール済みのパッケージを一覧できます。この環境では、どちらも 1421 件で同じ件数でした。違うのは、情報の出どころと、表示できる内容です。
Command
apt list --installed 2>/dev/null | grep -m1 '^openssl/'
dpkg-query -W -f='${db:Status-Abbrev} ${Package}:${Architecture} ${Version}\n' openssl
Output
openssl/resolute-updates,resolute-security,now 3.5.5-1ubuntu3.7 arm64 [installed,automatic]
ii openssl:arm64 3.5.5-1ubuntu3.7
dpkg-query -W | apt list --installed | |
|---|---|---|
| 情報の出どころ | dpkg のデータベース(/var/lib/dpkg/status)だけ | dpkg のデータベースに加えて、apt のキャッシュ(apt update で取得したリポジトリ情報) |
| 出力形式 | -f で自由に指定できる | 固定(名前/リポジトリ,now バージョン アーキ [状態]) |
| 出力の安定性 | 安定していて、スクリプト向き | 警告が出るとおり、将来変わる可能性がある |
| どのリポジトリ由来か | 分からない | 分かる(resolute-security など) |
| 手動/自動の区別 | 分からない | 分かる([installed,automatic]) |
ソースパッケージ名、Installed-Size | 取れる | 取れない |
削除済みで設定ファイルだけ残るもの(rc) | 表示される場合がある | 表示されない(ii のみ) |
「インストール済みのパッケージを確認する」という目的では、どちらも同じ結果になります。この環境でも、どちらも 1421 件で一致しました。更新後にバージョンを確認するだけなら、どちらを使っても確認できます。
ただし、完全に同じではありません。apt list --installed は最初からインストール済み(ii)だけを返しますが、dpkg-query -W は状態を問わず表示します。見られる項目も、上の表のように違います。
ポイントは次の 2 つです。
- 「手動/自動」と「リポジトリ名」は、dpkg にはない apt 側の情報です。
dpkg-queryだけで手動インストールかどうかを調べることはできません。その場合はapt-mark showmanualを使います。 dpkg-queryは状態を問わず表示するので、ii(インストール済み)だけにしたいときはawk '$1 == "ii"'などで自分で絞ります。
使い分けの目安は、人が目で見るなら apt list --installed、スクリプトで処理するなら dpkg-query -W -f=... です。
サイズの大きいパッケージを見る#
容量の大きいものから並べると、そのマシンが何に使われているかがだいたい分かります。単位は KB です。
Command
dpkg-query -W -f='${Installed-Size}\t${Package}\n' | sort -rn | head -8
Output
216141 ansible
146758 linux-modules-7.0.0-1014-aws
139622 mesa-vulkan-drivers
130672 libllvm21
127960 docker.io
117624 snapd
108192 python3-botocore
96528 containerd
一般に、カーネルモジュール、コンテナ基盤、コンパイラ一式、グラフィックス関連のライブラリ、構成管理ツールなどが上位に来やすいです。
セキュリティの観点では、次のようなものに目を向けます。
- コンテナ基盤(Docker など):
dockerグループに入っているユーザーは、実質 root と同じ権限を持ちます。 - コンパイラ: 侵入された場合に、攻撃者がツールをビルドする手段になります。使わないなら削除を検討します。
- GUI 向けのライブラリ: 上の例の
mesa-vulkan-driversのように、GUI を使わないサーバーでは不要なことが多いです。 - snapd: snap で入れたアプリは
apt list --upgradableに表示されません。snap listで別に確認します。
あるパッケージが何に必要とされているかは、次のコマンドで確認できます。
Command
apt-cache rdepends --installed openssl
Output
openssl
Reverse Depends:
ssl-cert
ca-certificates
ec2-instance-connect
Reverse Depends: の下に何も表示されなければ、ほかのパッケージからは依存されていません。
3. 「手動インストール」の意味に注意#
自分で入れたパッケージは次のコマンドで一覧にできます。
Command
apt-mark showmanual
Output(抜粋)
acl
ansible
bash
chrony
cloud-init
curl
ただし、ここでの「手動」は apt が管理しているパッケージのうち、名前を指定してインストールしたもの という意味です。「apt 以外の方法で入れたもの」という意味ではありません。依存関係として自動で入ったパッケージは表示されません。OS のインストール時に入ったもの(bash など)も含まれます。
| 入れ方 | showmanual に出るか |
|---|---|
apt install <名前> で指定した | 出る(依存で入ったものは出ない) |
PPA など追加リポジトリから apt install した | 出る |
.deb を dpkg -i で入れた | 出る |
ソースから make install した | 出ない |
pip / npm / cargo などで入れた | 出ない |
| バイナリを置いただけ | 出ない |
| snap / flatpak | 出ない |
注意が必要なのは apt の管理外 のものです。apt list --upgradable に出てこないため、更新を自分で追いかけないと、古く脆弱なまま残ってしまいます。
apt の管理外のものを探す#
ソースからビルドしたものや、配布元のバイナリを置いたものは /usr/local や /opt に入っていることが多いです。
Command
ls /usr/local/bin /opt
Output
/opt:
amazon containerd terraform
/usr/local/bin:
aws aws_completer terraform
この例では、AWS CLI と Terraform が apt の外で入っています。これらは自分で更新する必要があります。
言語ごとのパッケージ管理ツールも確認します。
Command
pip list --user
Output
Package Version
---------- -------
requests 2.32.3
Command
npm list -g --depth=0
Output
/usr/local/lib
└── npm@11.6.0
Command
cargo install --list
Output
ripgrep v14.1.1:
rg
ツールが入っていなければ command not found と表示されます。それ自体は問題ありません。
あるコマンドがどのパッケージに含まれるかを調べます。
Command
dpkg -S /usr/bin/ssh
Output
openssh-client: /usr/bin/ssh
dpkg の管理外のファイルを指定すると、次のように表示されます。
Command
dpkg -S /usr/local/bin/terraform
Output
dpkg-query: no path found matching pattern /usr/local/bin/terraform
登録されているリポジトリの一覧も確認しておきます。
Command
grep -rhE '^(deb|URIs)' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null
Output
URIs: http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu/
URIs: http://security.ubuntu.com/ubuntu
deb [arch=arm64 signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main
上の 2 行が Ubuntu 公式、3 行目が追加した外部リポジトリ(この例では GitHub CLI)です。外部リポジトリは、そこを運営している組織を信頼することになります。使っていないものは外します。
4. Ubuntu のパッチ込みのバージョンを確認する#
dpkg-query の ${Version} を使うと、Ubuntu のリビジョンまで含めたバージョンが表示されます。
Command
dpkg-query -W -f='${Package}\t${Version}\n' openssh-server openssl
Output
openssh-server 1:10.2p1-2ubuntu3.8
openssl 3.5.5-1ubuntu3.7
手動インストールしたパッケージのバージョンを、まとめて表示することもできます。
Command
apt-mark showmanual | xargs dpkg-query -W -f='${Package}\t${Version}\n'
Output(パッケージ名とバージョンは説明用の架空のものです)
example-tool 1.2.3-0ubuntu2.1
libfoo 4.5+dfsg-1ubuntu1
bar-daemon 2.0.1+ds1-0ubuntu1
baz-cli 3.4.0
バージョン表記の読み方#
Debian 系のバージョンは次の形をしています。
[epoch:]上流バージョン[-リビジョン]
| 部分 | 例 | 意味 |
|---|---|---|
| epoch | 1: | 先頭の数字とコロン。付いていないことも多い |
| 上流バージョン | 1.2.3 | 開発元がリリースしたバージョン |
| Debian リビジョン | -1 / -0 | Debian でのパッケージの版。0 は Debian ではまだパッケージ化されていないことを示す |
| Ubuntu リビジョン | ubuntu2 | Ubuntu が独自に加えたパッチの版 |
| リリース後の更新 | .1 | security や updates で配信された修正 |
上の出力例を読み解くと、次のようになります。
1.2.3-0ubuntu2.1: 上流1.2.3を Ubuntu が2回目にパッケージ化し、リリース後に修正が1回加わっています。末尾の.1は、セキュリティ修正の可能性があります。4.5+dfsg-1ubuntu1:+dfsgは、Debian Free Software Guidelines に合わせて非フリーなファイルを取り除いたことを示します。Debian の版1に、Ubuntu のパッチが加わっています。2.0.1+ds1-0ubuntu1:+ds1は、ソースを再パッケージしたことを示します。3.4.0: リビジョンがありません。Ubuntu のリポジトリではなく、配布元の公式リポジトリから入れたものによく見られる形です。
このほか、+ubuntuXX.YY.N のように、特定の Ubuntu リリース向けのビルドであることを示す表記もあります。
取得元は次のコマンドで確認できます。
Command
apt policy openssh-server
Output
openssh-server:
Installed: 1:10.2p1-2ubuntu3.8
Candidate: 1:10.2p1-2ubuntu3.8
Version table:
*** 1:10.2p1-2ubuntu3.8 500
500 http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu resolute-updates/main arm64 Packages
500 http://security.ubuntu.com/ubuntu resolute-security/main arm64 Packages
100 /var/lib/dpkg/status
1:10.2p1-2ubuntu3 500
500 http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu resolute/main arm64 Packages
Installed: 今入っているバージョンCandidate:apt upgradeで入る予定のバージョン。Installedと違えば、更新があります***: 今入っているバージョンの行。その下に取得元の URL が並びます
外部リポジトリから入れたものは、ここに外部の URL が表示されます。Ubuntu のセキュリティ更新とは別の経路で更新されます。
バージョン番号だけで脆弱性は判断できない#
上流のバージョンが古く見えても、Ubuntu が修正をバックポートしていることがあります。脆弱性が修正されているかどうかは、changelog に書かれた CVE 番号で確認します。
Command
apt changelog openssh-server | grep -i 'CVE-' | head -5
Output(CVE 番号は説明用の例です)
- debian/patches/CVE-2026-0001.patch: upstream: sftp: be stricter in
- CVE-2026-0001
- debian/patches/CVE-2026-0002.patch: upstream: Reset GSSAPI client
- CVE-2026-0002
調べたい CVE 番号が出てくれば、そのバージョンで修正されています。
Ubuntu の CVE トラッカー(https://ubuntu.com/security/cves)でパッケージ名を検索して、修正済みのバージョンを確認する方法もあります。
5. 更新状況・サービス・ポート・ユーザー#
アップグレードできるパッケージを確認します。
Command
apt list --upgradable 2>/dev/null
Output(抜粋)
Listing...
cloud-init/resolute-updates 26.2-0ubuntu1~26.04.1 all [upgradable from: 26.1-0ubuntu3~26.04.1]
containerd/resolute-updates 2.2.2-0ubuntu1.2 arm64 [upgradable from: 2.2.2-0ubuntu1.1]
libsystemd0/resolute-updates 259.9-0ubuntu0.1 arm64 [upgradable from: 259.8-0ubuntu0.1]
取得元が -security のものは、セキュリティ修正です。優先して当てます。この一覧は、最後に apt update した時点の情報です。最新の状態を見るには、先に sudo apt update を実行します(6 章)。
動作中のサービスを確認します。
Command
systemctl list-units --type=service --state=running
Output(抜粋)
UNIT LOAD ACTIVE SUB DESCRIPTION
apache2.service loaded active running The Apache HTTP Server
chrony.service loaded active running chrony, an NTP client/server
containerd.service loaded active running containerd container runtime
cron.service loaded active running Regular background program processing daemon
ssh.service loaded active running OpenBSD Secure Shell server
知らないサービスが動いていたら、何のためのものかを調べます。不要なら止めて、自動起動を無効にします。
待ち受けポートを確認します。
Command
sudo ss -tulpn
Output(抜粋)
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=3393,fd=17))
udp UNCONN 0 0 127.0.0.1:323 0.0.0.0:* users:(("chronyd",pid=821,fd=4))
tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1012,fd=3))
tcp LISTEN 0 511 *:80 *:* users:(("apache2",pid=1388,fd=4))
tcp LISTEN 0 4096 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=1120,fd=6))
Local Address の見方がポイントです。
| 待ち受けアドレス | 意味 |
|---|---|
127.0.0.1、127.0.0.53、[::1] | そのマシンの中からしか接続できない |
0.0.0.0、[::]、* | すべてのネットワークから接続できる可能性がある |
この例では、SSH(22)と Apache(80)が外に開いています。PostgreSQL(5432)は 127.0.0.1 なので、外からは接続できません。
ログインできるユーザーを確認します。
Command
grep -v -E 'nologin|false' /etc/passwd
Output
root:x:0:0:root:/root:/bin/bash
sync:x:4:65534:sync:/bin:/bin/sync
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
sato:x:1001:1001::/home/sato:/bin/bash
sync は特殊なアカウントで、ログインには使われません。それ以外に、知らないユーザーがいないかを見ます。
sudo グループと docker グループのメンバーを確認します。
Command
getent group sudo docker
Output
sudo:x:27:ubuntu,sato
docker:x:109:sato
行の最後がメンバーです。どちらも root 相当の権限になるので、必要な人だけにします。
ファイアウォールの状態を確認します。
Command
sudo ufw status verbose
Output
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp (OpenSSH) ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)
Default: deny (incoming) で、許可したものだけを通す設定になっていることを確認します。Status: inactive なら、ufw は使われていません。クラウドでは、セキュリティグループなどほかの仕組みで制御していることもあります。
6. パッチ適用とバージョンアップ#
ここからはシステムを変更する操作です。本番環境では、次の準備をしてから行います。
- 戻せるようにしておく: EC2 なら AMI やスナップショットを取得する
- 先に検証環境で試す: 同じ構成の環境で、更新後にアプリが動くことを確かめる
- 時間を決めて行う: サービスの再起動や OS の再起動が発生するので、影響の少ない時間に行う
流れ#
sudo apt update パッケージの一覧を最新にする
→ apt list --upgradable 何が更新されるかを見る
→ apt -s upgrade シミュレーションで確認する
→ sudo apt upgrade 適用する
→ 再起動が必要かを確認する
パッケージの一覧を最新にする: apt update#
apt update はパッケージ自体を更新するのではなく、リポジトリから最新の一覧を取ってくるだけ です。これをしないと、新しい修正があっても見えません。
Command
sudo apt update
Output(抜粋)
Hit:1 http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu resolute InRelease
Get:2 http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu resolute-updates InRelease [126 kB]
Get:3 http://security.ubuntu.com/ubuntu resolute-security InRelease [126 kB]
Fetched 1,284 kB in 2s (642 kB/s)
16 packages can be upgraded. Run 'apt list --upgradable' to see them.
Hit: 前回から変化なしGet: 新しい情報を取得した
最後の行で、更新できるパッケージの数が分かります。
適用する前にシミュレーションする#
-s(simulate)を付けると、実際には何も変更せずに、何が起きるかだけを表示します。root 権限も不要です。
Command
apt -s upgrade
Output(抜粋)
NOTE: This is only a simulation!
...
Upgrading:
cloud-init libpam-systemd libudev1 systemd-resolved
cloud-init-base libsystemd-shared systemd systemd-sysv
libnss-systemd libsystemd0 systemd-cryptsetup udev
Not upgrading yet due to phasing:
containerd tmux ubuntu-pro-client ubuntu-pro-client-l10n
Summary:
Upgrading: 12, Installing: 0, Removing: 0, Not Upgrading: 4
Upgrading: 更新されるパッケージNot upgrading yet due to phasing: Ubuntu が更新を少しずつ配信している途中のもの(段階的リリース)。数日待てば自動で対象になります。不具合があったときの影響を抑える仕組みなので、通常は待って問題ありませんSummary:Removingが 0 であることを必ず確認します
systemd やカーネルなど、重要なパッケージが含まれていないかもここで見ます。
適用する: apt upgrade と apt full-upgrade#
Command
sudo apt upgrade
Output(抜粋)
Summary:
Upgrading: 12, Installing: 0, Removing: 0, Not Upgrading: 4
Download size: 18.4 MB
Space needed: 1,024 kB / 6,512 MB available
Continue? [Y/n] y
...
Setting up systemd (259.9-0ubuntu0.1) ...
確認が出るので、Summary を見てから y を入力します。
upgrade と full-upgrade の違いは次のとおりです。
| コマンド | 新しいパッケージの追加 | 既存パッケージの削除 | 使いどころ |
|---|---|---|---|
apt upgrade | する | しない | 日常のパッチ適用 |
apt full-upgrade | する | する(依存関係の解決に必要なら) | 依存関係が変わる更新。upgrade で更新しきれなかったとき |
full-upgrade はパッケージを削除することがあるので、必ず apt -s full-upgrade で Removing の中身を確認してから実行します。
緊急でない定期対応: 一括で最新に保つ#
緊急の脆弱性対応でなければ、特定のパッケージだけでなく、更新できるものを 一括で最新に保つ 選択肢があります。月 1 回などのメンテナンス時間を決めて、次の流れで行います。
1. sudo apt update 更新候補を取得する
2. apt list --upgradable 一括で更新されるものを確認する
3. apt -s upgrade シミュレーションで、Installing / Removing を確認する
4. 必要なら apt-mark hold 止めたいものを除外する
5. sudo apt upgrade 一括で適用する
6. 再起動が必要かを確認する needrestart / /var/run/reboot-required
7. 動作を確認する サービスの状態、ログ、アプリの疎通
一括で更新するときに見るのは、次の点です。
- 何が上がるか:
apt list --upgradableの一覧に、DB やミドルウェア、PHP のようにアプリの動作に影響するものが含まれていないかを見ます。 - 削除や新規インストールがないか:
apt -s upgradeのRemovingとInstallingを見ます。apt upgradeはパッケージを削除しませんが、カーネルのように新規に入るものはあります。削除が必要な更新はapt full-upgradeになるので、中身を確認してから実行します。 - 止めたいものがないか: バージョンを固定したいものは、
apt-mark holdで除外します。 - 再起動が必要か: カーネルや共有ライブラリが更新されると、再起動が必要になります。
定期的に行うメリットは、更新の差分が小さく保たれ、1 回あたりの影響が読みやすくなることです。長く放置すると、更新が大きくなり、問題が起きたときに原因の切り分けも難しくなります。
運用の組み合わせとしては、次の分け方が一般的です。
| 種類 | 方法 |
|---|---|
| セキュリティ修正 | unattended-upgrades で毎日自動適用する(後述) |
| それ以外の更新 | 定期のメンテナンス時間に、上の流れで一括適用する |
| 緊急の脆弱性 | 定期を待たず、次の節のように対象だけを先に更新する |
本番環境では、いきなり一括適用せず、検証用の環境で同じ更新を先に試してから適用します。
セキュリティ修正の自動適用は、本番でも使われる#
unattended-upgrades による自動適用は、本番サーバーでも一般的です。標準では、自動適用されるのはセキュリティ修正(-security のリポジトリ)だけで、機能追加を含む更新は対象外です。毎日少しずつ適用するので、1 回あたりの変更も小さくなります。
本番で使うときは、次の点に注意します。
- サービスの再起動: パッケージが更新されると、そのサービスが自動で再起動される場合があります。PHP-FPM やデータベースが、意図しない時間に再起動する可能性があります。
- カーネルは自動で再起動されない: 標準では自動再起動しないため、再起動するまで古いカーネルで動き続けます。自動再起動を有効にするなら、時間帯を指定します。
- 止めたいものを除外する: バージョンを固定したいものは、
apt-mark holdかPackage-Blacklistで除外します。 - 失敗に気づく仕組み: メール通知や
/var/log/unattended-upgrades/の監視で、適用の失敗に気づけるようにします。
自動適用を使わない運用もあります。重要なシステムで検証環境を先に通す場合や、イメージを作り直して入れ替える場合です。
どの運用でも、パッチが出たことに気づく仕組みが必要#
「定期的に当てる」「自動で当てる」「作り直す」のどれでも、パッチが出たことに気づく必要があります。方針で変わるのは、気づいた後の適用の方法だけです。
| 運用方針 | 適用の方法 | 気づく必要がある理由 |
|---|---|---|
| 定期的に当てる | 一括で apt upgrade | 緊急のものは、定期を待たず先に対応するかを判断するため |
| 自動で当てる | unattended-upgrades | 自動適用の対象外のものや、失敗したものに気づくため |
| 作り直す | イメージを更新して入れ替える | 気づかないと作り直すきっかけがなく、古いパッケージのまま動き続けるため |
気づく手段の例は、次のとおりです。
- ディストリビューションの情報: Ubuntu Security Notices(USN)を購読する。CVE トラッカー(
ubuntu.com/security/cves)を見る。 - サーバー側の検知:
apt list --upgradableやpro security-statusで更新候補を定期的に確認する。脆弱性スキャナで、入っているパッケージを調べる。 - 作り直す運用: イメージのビルド時にパッケージの更新を取り込む。定期ビルドか、脆弱性の通知をきっかけにした再ビルドにする。
- ミドルウェアの情報: PHP のようにアプリの動作に直結するものは、公式のリリース情報を別に追う。
気づいた後の判断も、あらかじめ決めておくと対応が速くなります。
- 緊急度の基準: 外部から攻撃できるか、すでに悪用されているか(7 章の PHP2Shell のような事例)を基準にします。
- 対応の期限: 緊急度ごとに、いつまでに適用するかを決めます。
- 適用の方法: 緊急なら対象のパッケージだけを先に更新し、緊急でなければ定期に一括で適用します。
更新の前後で何が変わったかを記録しておく#
更新の作業は、あとから「何がどう変わったか」を説明できるようにしておくと、障害の調査や監査のときに役立ちます。更新の前にスナップショットを取り、実行ログを残し、更新後に差分を取る運用です。
1. 更新前にスナップショットを取る
mkdir -p ~/upgrade-log/$(date +%F) && cd ~/upgrade-log/$(date +%F)
dpkg-query -W -f='${Package} ${Version}\n' | sort > before.txt # 全パッケージの版
apt list --upgradable 2>/dev/null > upgradable.txt # 更新候補
apt -s upgrade > simulate.txt # 今回のシミュレーション
uname -r > kernel_before.txt # 動いているカーネル
systemctl --failed > failed_before.txt # 失敗しているサービス
日付ごとのディレクトリに保存するので、過去の更新と比べられます。failed_before.txt を取っておくと、更新前から失敗していたサービスと、更新で新しく壊れたサービスを区別できます。
2. 更新の実行ログも残す
sudo apt upgrade -y 2>&1 | tee upgrade.log
端末全体を録画する script コマンドもありますが、更新のログを残すだけなら tee で足ります。-y は確認を省略するので、simulate.txt で Removing の中身を確認してから実行します。
3. 更新後に差分を取る
dpkg-query -W -f='${Package} ${Version}\n' | sort > after.txt
diff before.txt after.txt > diff.txt # 版が変わったものだけが出る
cat diff.txt
ls /var/run/reboot-required 2>/dev/null; cat /var/run/reboot-packages.txt 2>/dev/null
sudo reboot
# 再起動後
uname -r > kernel_after.txt
systemctl --failed > failed_after.txt
diff.txt には、バージョンが変わったパッケージだけが、更新前(<)と更新後(>)の組で出ます。/var/run/reboot-required があれば再起動が必要で、reboot-packages.txt にその原因のパッケージが書かれています。
再起動のあとは、kernel_before.txt と kernel_after.txt を比べて、カーネルが新しくなったことを確認します。failed_before.txt と failed_after.txt を比べれば、更新で壊れたサービスにも気づけます。
| ファイル | 内容 | 使いどころ |
|---|---|---|
before.txt / after.txt / diff.txt | 全パッケージのバージョンと差分 | 何が上がったかの記録 |
upgradable.txt / simulate.txt | 更新候補とシミュレーション | 想定どおりの更新だったかの確認 |
upgrade.log | 実行時の出力 | エラーや警告の調査 |
kernel_before.txt / kernel_after.txt | カーネルのバージョン | 再起動で新しいカーネルになったかの確認 |
failed_before.txt / failed_after.txt | 失敗しているサービス | 更新で壊れたサービスの検知 |
apt 自身も、/var/log/apt/history.log に更新の履歴を残しています(「自動更新を確認する」の節を参照)。ここで紹介した方法は、それに加えて「更新前の状態」と「更新後の差分」を、手元で見やすい形でまとめて残すものです。
特定のパッケージだけを更新する#
緊急の脆弱性対応などで、1 つだけ更新したいときは --only-upgrade を使います。入っていないパッケージを新しくインストールすることはありません。
Command
sudo apt install --only-upgrade openssh-server
Output(抜粋)
Upgrading:
openssh-client openssh-server openssh-sftp-server
Summary:
Upgrading: 3, Installing: 0, Removing: 0, Not Upgrading: 13
依存関係にあるパッケージも一緒に更新されることがあります。
--only-upgrade が「新しくインストールしない」のは、コマンドラインで名前を指定したパッケージだけです。更新に必要な依存パッケージは、新しく入ることがあります。実行前に apt -s install --only-upgrade <名前> でシミュレーションして、Installing と Removing の中身を確認します。
ソフトウェア単位で更新する具体的な流れは、7 章の PHP の例で詳しく見ます。
カーネルだけを更新する#
カーネルは、ほかのパッケージと更新のされ方が違います。Ubuntu のカーネルは バージョンがパッケージ名に入っています(例: linux-image-7.0.0-1015-aws)。新しいカーネルは「既存パッケージの更新」ではなく「別のパッケージの新規インストール」として入ります。
それを束ねているのが、バージョンを含まない メタパッケージ です。メタパッケージは「最新のカーネルに依存する」だけの空のパッケージで、これを更新すると、新しいカーネルが依存関係として入ります。
| メタパッケージ | 役割 |
|---|---|
linux-aws | 下の 2 つをまとめたもの |
linux-image-aws | 最新のカーネル本体(linux-image-<バージョン>-aws)に依存する |
linux-headers-aws | 最新のカーネルヘッダーに依存する。DKMS でドライバをビルドするときに必要 |
-aws の部分は、使っているカーネルの種類で変わります。EC2 なら -aws、通常のサーバーなら -generic です。入っているものを確認します。
Command
apt list --installed 'linux-*aws*' 2>/dev/null
Output
Listing...
linux-aws/resolute-updates,resolute-security,now 7.0.0.1014.14 arm64 [installed,automatic]
linux-headers-7.0.0-1014-aws/resolute-updates,resolute-security,now 7.0.0-1014.14 arm64 [installed,automatic]
linux-headers-aws/resolute-updates,resolute-security,now 7.0.0.1014.14 arm64 [installed,automatic]
linux-image-7.0.0-1014-aws/resolute-updates,resolute-security,now 7.0.0-1014.14 arm64 [installed,automatic]
linux-image-aws/resolute-updates,resolute-security,now 7.0.0.1014.14 arm64 [installed,automatic]
linux-modules-7.0.0-1014-aws/resolute-updates,resolute-security,now 7.0.0-1014.14 arm64 [installed,automatic]
sudo apt update のあとで、カーネルの更新が来ているかを確認します。
Command
apt list --upgradable 'linux-*' 2>/dev/null
Output
Listing...
linux-aws/resolute-updates,resolute-security 7.0.0.1015.15 arm64 [upgradable from: 7.0.0.1014.14]
linux-headers-aws/resolute-updates,resolute-security 7.0.0.1015.15 arm64 [upgradable from: 7.0.0.1014.14]
linux-image-aws/resolute-updates,resolute-security 7.0.0.1015.15 arm64 [upgradable from: 7.0.0.1014.14]
ここに出るのはメタパッケージだけです。linux-image-7.0.0-1015-aws のような新しいカーネル本体は、まだ入っていないので upgradable には出ません。
メタパッケージを指定してシミュレーションします。
Command
apt -s install --only-upgrade linux-aws linux-image-aws linux-headers-aws
Output(抜粋)
NOTE: This is only a simulation!
...
Installing dependencies:
linux-aws-headers-7.0.0-1015 linux-image-7.0.0-1015-aws
linux-headers-7.0.0-1015-aws linux-modules-7.0.0-1015-aws
Upgrading:
linux-aws linux-headers-aws linux-image-aws
Summary:
Upgrading: 3, Installing: 4, Removing: 0, Not Upgrading: 13
Installing dependencies に新しいカーネルが並んでいれば、想定どおりです。カーネルの更新では Installing が 0 にならないのが普通です。Not Upgrading: 13 は、カーネル以外の更新が手つかずで残っていることを示します。
問題なければ適用します。
Command
sudo apt install --only-upgrade linux-aws linux-image-aws linux-headers-aws
Output(抜粋)
Setting up linux-image-7.0.0-1015-aws (7.0.0-1015.15) ...
...
Processing triggers for linux-image-7.0.0-1015-aws (7.0.0-1015.15) ...
/etc/kernel/postinst.d/initramfs-tools:
update-initramfs: Generating /boot/initrd.img-7.0.0-1015-aws
/etc/kernel/postinst.d/zz-update-grub:
Generating grub configuration file ...
done
インストールしただけでは、まだ古いカーネルで動いています。新しいカーネルは再起動して初めて使われます。 再起動して、uname -r で切り替わったことを確認します。
Command
sudo reboot
# 再接続してから
uname -r
Output
7.0.0-1015-aws
古いカーネルは、新しいカーネルで起動できなかったときの予備として残っています。
Command
dpkg-query -W -f='${db:Status-Abbrev} ${Package}\n' 'linux-image-[0-9]*'
Output
ii linux-image-7.0.0-1014-aws
ii linux-image-7.0.0-1015-aws
新しいカーネルで問題なく動くことを確かめたら、sudo apt autoremove --purge で古いカーネルを整理できます。apt は、動作中のカーネルと 1 つ前のカーネルは自動削除の対象から外すので、すべてが消えることはありません。ただし /boot の容量が少ない環境では、カーネルがたまるとインストールに失敗するので、定期的に整理します。
逆に、カーネル以外だけを更新したい ときは、メタパッケージを一時的に hold します(hold については後述)。
Command
sudo apt-mark hold linux-aws linux-image-aws linux-headers-aws
sudo apt upgrade
sudo apt-mark unhold linux-aws linux-image-aws linux-headers-aws
再起動の時間が取れないときに、カーネル以外のパッチを先に当てる、といった使い方ができます。終わったら unhold を忘れないようにします。
なお、Ubuntu Pro の Livepatch を有効にすると、重大なカーネルの脆弱性の一部は再起動なしで修正されます。それでも、すべての修正が Livepatch で配信されるわけではないので、定期的な再起動とカーネルの更新は必要です。
再起動が必要かを確認する#
カーネルや一部のライブラリを更新したときは、再起動しないと新しいものが使われません。再起動が必要なときは、次のファイルが作られます。
Command
cat /var/run/reboot-required /var/run/reboot-required.pkgs
Output
*** System restart required ***
linux-image-7.0.0-1015-aws
linux-base
No such file or directory と表示されれば、再起動は不要です。
再起動までは不要でも、古いライブラリを使ったまま動いているサービス が残ることがあります。needrestart で確認できます。-r l は「一覧を出すだけで、再起動はしない」という指定です。
Command
sudo needrestart -r l
Output(抜粋)
Services to be restarted:
systemctl restart apache2.service
systemctl restart cron.service
ここに出たサービスは、再起動するまで古いコードのままです。脆弱性の修正を確実に効かせるには、サービスを再起動します。
更新したくないパッケージを止める: apt-mark hold#
アプリの互換性の都合で、特定のパッケージのバージョンを固定したいときは hold します。
Command
sudo apt-mark hold containerd
apt-mark showhold
Output
containerd set on hold.
containerd
hold したものは apt upgrade で更新されなくなります。セキュリティ修正も届かなくなる ので、理由と期限を記録しておき、不要になったら sudo apt-mark unhold containerd で戻します。
自動更新(unattended-upgrades)を確認する#
Ubuntu では、セキュリティ修正を自動で適用する unattended-upgrades が標準で有効になっています。
Command
cat /etc/apt/apt.conf.d/20auto-upgrades
Output
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
どちらも "1" なら、毎日、一覧の更新とセキュリティ修正の適用が自動で行われます。
自動・手動を問わず、いつ何が更新されたかは apt の履歴に残っています。
Command
grep -E '^(Start-Date|Commandline|Upgrade)' /var/log/apt/history.log | tail -6
Output(抜粋)
Start-Date: 2026-10-24 06:38:33
Commandline: /usr/bin/unattended-upgrade
Upgrade: zlib1g:arm64 (1:1.3.dfsg+really1.3.1-1ubuntu3.1, 1:1.3.dfsg+really1.3.1-1ubuntu3.2)
Start-Date: 2026-10-24 13:40:30
Commandline: apt install --only-upgrade openssh-server
Upgrade: openssh-server:arm64 (1:10.2p1-2ubuntu3.7, 1:10.2p1-2ubuntu3.8)
Upgrade: のかっこの中は「(更新前, 更新後)」です。障害が起きたときに「直前に何が更新されたか」を調べる手がかりになります。
apt の管理外のものも更新する#
3 章で見つけた apt の管理外のものは、apt upgrade では更新されません。それぞれの方法で確認します。
Command
snap refresh --list
Output
Name Version Rev Size Publisher Notes
core22 20260909 2988 72.4MB canonical** base
snap は通常、自動で更新されます。ここに出ているのは、次の自動更新を待っているものです。
Command
pip list --user --outdated
Output
Package Version Latest Type
-------- ------- ------ -----
requests 2.32.3 2.32.5 wheel
/usr/local/bin や /opt に置いたバイナリ(この記事の例では AWS CLI や Terraform)は、配布元のリリース情報を見て、自分で入れ直します。
元に戻す#
更新後に不具合が出たときは、リポジトリに古いバージョンが残っていれば、バージョンを指定して入れ直せます。使えるバージョンは apt list -a で確認します(2 章)。
Command
sudo apt install openssh-server=1:10.2p1-2ubuntu3
ただし、古いバージョンには修正前の脆弱性が残っています。また、リポジトリから消えたバージョンには戻せません。確実に戻す手段は、作業前に取ったスナップショット です。
Ubuntu のリリースを上げる: do-release-upgrade#
24.04 LTS から 26.04 LTS のように、Ubuntu 自体のリリースを上げるときは do-release-upgrade を使います。パッケージの更新とは影響の大きさがまったく違うので、別の作業として計画します。
新しいリリースが出ているかを確認します。
Command
do-release-upgrade -c
Output(24.04 LTS で実行した場合の例)
Checking for a new Ubuntu release
New release '26.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
最新の LTS を使っている場合は、次のように表示されます。
Checking for a new Ubuntu release
There is no development version of an LTS available.
リリースを上げるときのポイントです。
- LTS から LTS へは、
.1のリリース(例: 26.04.1)が出てから案内される のが標準です。それまでは-cを実行しても表示されません - 先に今のリリースのパッケージを最新にする:
sudo apt update && sudo apt full-upgradeをして、再起動してから行います - SSH 越しに行うときは注意: 途中で切断されると、中途半端な状態で止まります。
do-release-upgradeは SSH を検知すると予備の SSH ポートを開くので、ファイアウォールやセキュリティグループで許可しておくか、tmuxの中で実行します - 外部リポジトリは無効になる: アップグレード中に外部リポジトリは無効にされます。終わったら、新しいリリースに対応しているかを確認してから戻します
- クラウドでは作り直しも選択肢: EC2 などでは、新しいリリースの AMI からサーバーを作り直して移行する方が、安全で手順も単純なことが多いです
7. 事例: PHP2Shell に対応する#
ここまでの手順を、実際の脆弱性対応に当てはめてみます。題材は、2026 年 10 月に公開された PHP の脆弱性「PHP2Shell」です。
PHP2Shell の概要#
| 項目 | 内容 |
|---|---|
| CVE | CVE-2026-109618、CVE-2026-109619 |
| 内容 | PHP 本体のメモリ安全性の不具合。ログインなしで、リモートからコードを実行される |
| 深刻度 | CVSS v4.0 で 9.2(Critical)。Ubuntu の優先度も Critical |
| 経緯 | 10 月 20 日に、修正版が出る前に詳細がネットに流出。10 月 21 日に PHP が修正版を公開し、Ubuntu も同日に USN-8948-1 を公開 |
| 上流の修正版 | PHP 8.5.12、8.4.27、8.3.36、8.2.35 |
CVE-2026-109619 はアップロードの進捗表示機能での解放後使用(use-after-free)、CVE-2026-109618 はメモリ不足が起きたときのメモリ破壊です。PHP は「設定変更だけでは完全には防げない」としていて、修正版への更新が唯一の確実な対策です。
インターネットに公開している PHP のサイトがあれば、すぐに対応が必要な種類の脆弱性です。
流れ#
1. PHP が入っているか、どこから入れたかを確認する
2. Ubuntu の CVE トラッカーで、修正済みのバージョンを確認する
3. apt update して、修正版が届いているかを見る
4. PHP のパッケージだけをシミュレーション → 更新する
5. changelog で CVE が修正されていることを確認する
6. PHP を読み込んでいるサービスを再起動する
7. apt の管理外の PHP がないかを確認する
1. PHP が入っているかを確認する#
Command
apt list --installed '*php*' 2>/dev/null
Output
Listing...
libapache2-mod-php8.5/resolute-updates,resolute-security,now 8.5.4-0ubuntu1.4 arm64 [installed,automatic]
php-common/resolute,now 2:96ubuntu1 all [installed,automatic]
php8.5-cli/resolute-updates,resolute-security,now 8.5.4-0ubuntu1.4 arm64 [installed,automatic]
php8.5-common/resolute-updates,resolute-security,now 8.5.4-0ubuntu1.4 arm64 [installed,automatic]
php8.5-mysql/resolute-updates,resolute-security,now 8.5.4-0ubuntu1.4 arm64 [installed,automatic]
php8.5-opcache/resolute-updates,resolute-security,now 8.5.4-0ubuntu1.4 arm64 [installed,automatic]
php8.5-readline/resolute-updates,resolute-security,now 8.5.4-0ubuntu1.4 arm64 [installed,automatic]
php/resolute,now 2:8.5+96ubuntu1 all [installed]
Ubuntu 26.04 LTS の PHP は 8.5 系です。libapache2-mod-php8.5 があるので、5 章で見た Apache が PHP を読み込んでいることが分かります。
CVE トラッカーは、ソースパッケージ の単位で修正状況を載せています。1 つのソースパッケージから、複数のパッケージ(バイナリパッケージ)が作られます。どのソースパッケージから来たものかを確認します。
Command
dpkg-query -W -f='${Package}\t${source:Package}\t${Version}\n' '*php*'
Output
libapache2-mod-php8.5 php8.5 8.5.4-0ubuntu1.4
php php-defaults 2:8.5+96ubuntu1
php-common php-defaults 2:96ubuntu1
php8.5-cli php8.5 8.5.4-0ubuntu1.4
php8.5-common php8.5 8.5.4-0ubuntu1.4
php8.5-mysql php8.5 8.5.4-0ubuntu1.4
php8.5-opcache php8.5 8.5.4-0ubuntu1.4
php8.5-readline php8.5 8.5.4-0ubuntu1.4
2 列目がソースパッケージです。更新が必要なのは、ソースパッケージが php8.5 のものです。php と php-common は「PHP の既定のバージョンを選ぶ」ためのパッケージ(php-defaults)で、PHP 本体のコードは含みません。
取得元も確認します。
Command
apt policy php8.5-common
Output
php8.5-common:
Installed: 8.5.4-0ubuntu1.4
Candidate: 8.5.4-0ubuntu1.4
Version table:
*** 8.5.4-0ubuntu1.4 500
500 http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu resolute-updates/main arm64 Packages
500 http://security.ubuntu.com/ubuntu resolute-security/main arm64 Packages
100 /var/lib/dpkg/status
8.5.4-0ubuntu1 500
500 http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu resolute/main arm64 Packages
Ubuntu 公式から入っていることが分かります。
PHP は、Ubuntu 公式ではなく 外部の PPA(ppa:ondrej/php など)から入れていることがよくあります。その場合、ここに PPA の URL が表示され、バージョンも 8.5.11-1+ubuntu26.04.1+deb.sury.org+1 のような形になります。PPA の PHP は Ubuntu のセキュリティ更新の対象外です。その場合は Ubuntu の CVE トラッカーではなく、PPA 側で修正版が公開されたかを確認します。
2. 修正済みのバージョンを確認する#
Ubuntu の CVE トラッカー(https://ubuntu.com/security/CVE-2026-109618)を見ると、リリースごとの修正版が載っています(抜粋)。
| Ubuntu | ソースパッケージ | 修正済みのバージョン |
|---|---|---|
| 26.04 LTS (resolute) | php8.5 | 8.5.4-0ubuntu1.5 |
| 24.04 LTS (noble) | php8.3 | 8.3.6-0ubuntu0.24.04.13 |
| 22.04 LTS (jammy) | php8.1 | 8.1.2-1ubuntu2.28 |
| 20.04 LTS (focal) | php7.4 | 7.4.3-4ubuntu2.29+esm5(Ubuntu Pro が必要) |
ここで注目したいのは、上流の修正版が 8.5.12 なのに、Ubuntu の修正版は 8.5.4 のままだということです。Ubuntu は上流のバージョンを上げずに、修正だけをバックポートしています(4 章)。php -v の表示は更新後も PHP 8.5.4 のままなので、php -v を見て「8.5.12 より古いから脆弱」と判断するのは間違い です。比べるのは、末尾の Ubuntu のリビジョンまで含めたバージョンです。
今入っているバージョンが修正版より古いかを、dpkg --compare-versions で比べます。Debian 系のバージョンの大小は、文字列の比較とは規則が違うので、このコマンドを使うのが確実です。
Command
dpkg --compare-versions 8.5.4-0ubuntu1.4 lt 8.5.4-0ubuntu1.5 && echo "修正前のバージョンです"
Output
修正前のバージョンです
lt は「より小さい(less than)」です。この例では、まだ修正が入っていません。
3. 修正版が届いているかを見る#
まず、unattended-upgrades がすでに当てていないかを確認します。セキュリティ修正なので、自動更新が有効なら、1 日以内に適用されていることもあります。
Command
grep -A2 'Start-Date: 2026-10-2' /var/log/apt/history.log | grep -i php
何も表示されなければ、まだ当たっていません。一覧を最新にして、修正版が来ているかを確認します。
Command
sudo apt update
apt list --upgradable '*php*' 2>/dev/null
Output(apt list の部分)
Listing...
libapache2-mod-php8.5/resolute-updates,resolute-security 8.5.4-0ubuntu1.5 arm64 [upgradable from: 8.5.4-0ubuntu1.4]
php8.5-cli/resolute-updates,resolute-security 8.5.4-0ubuntu1.5 arm64 [upgradable from: 8.5.4-0ubuntu1.4]
php8.5-common/resolute-updates,resolute-security 8.5.4-0ubuntu1.5 arm64 [upgradable from: 8.5.4-0ubuntu1.4]
php8.5-mysql/resolute-updates,resolute-security 8.5.4-0ubuntu1.5 arm64 [upgradable from: 8.5.4-0ubuntu1.4]
php8.5-opcache/resolute-updates,resolute-security 8.5.4-0ubuntu1.5 arm64 [upgradable from: 8.5.4-0ubuntu1.4]
php8.5-readline/resolute-updates,resolute-security 8.5.4-0ubuntu1.5 arm64 [upgradable from: 8.5.4-0ubuntu1.4]
取得元に resolute-security があり、バージョンがトラッカーの修正版(8.5.4-0ubuntu1.5)と一致しています。
トラッカーで「修正済み」になっていても、ミラーへの反映には時間差があります。ここに出てこないときは、少し時間をおいて apt update からやり直します。
4. PHP のパッケージだけを更新する#
更新するパッケージを、ソースパッケージが php8.5 のものから自動で選びます。手で並べると、拡張モジュール(php8.5-mysql など)の指定漏れが起きやすいからです。
Command
PKGS=$(dpkg-query -W -f='${db:Status-Abbrev} ${Package} ${source:Package}\n' \
| awk '$1 == "ii" && $3 == "php8.5" { print $2 }')
echo $PKGS
Output
libapache2-mod-php8.5 php8.5-cli php8.5-common php8.5-mysql php8.5-opcache php8.5-readline
$1 == "ii" で、インストール済み(設定ファイルだけ残っているものを除く)に絞っています。まずシミュレーションします。
Command
apt -s install --only-upgrade $PKGS
Output(抜粋)
NOTE: This is only a simulation!
...
Upgrading:
libapache2-mod-php8.5 php8.5-common php8.5-opcache
php8.5-cli php8.5-mysql php8.5-readline
Summary:
Upgrading: 6, Installing: 0, Removing: 0, Not Upgrading: 10
Upgrading が PHP のパッケージだけで、Removing が 0 であることを確認して適用します。
Command
sudo apt install --only-upgrade $PKGS
Output(抜粋)
Setting up php8.5-common (8.5.4-0ubuntu1.5) ...
Setting up php8.5-cli (8.5.4-0ubuntu1.5) ...
Setting up libapache2-mod-php8.5 (8.5.4-0ubuntu1.5) ...
...
緊急対応では、このように対象を絞って更新すると、ほかのパッケージの更新による影響を切り分けられます。ほかの更新は、あとで通常のメンテナンスとして当てます。
5. 修正されたことを確認する#
バージョンを確認します。
Command
dpkg-query -W -f='${Package}\t${Version}\n' $PKGS
Output
libapache2-mod-php8.5 8.5.4-0ubuntu1.5
php8.5-cli 8.5.4-0ubuntu1.5
php8.5-common 8.5.4-0ubuntu1.5
php8.5-mysql 8.5.4-0ubuntu1.5
php8.5-opcache 8.5.4-0ubuntu1.5
php8.5-readline 8.5.4-0ubuntu1.5
changelog に、対象の CVE 番号が載っていることも確認します。
Command
apt changelog php8.5-common | grep -E 'CVE-2026-10961[89]'
Output(パッチのファイル名は説明用の例です)
- debian/patches/CVE-2026-109618.patch: fix request cleanup after
- CVE-2026-109618
- debian/patches/CVE-2026-109619.patch: fix use-after-free in
- CVE-2026-109619
6. PHP を読み込んでいるサービスを再起動する#
パッケージを更新しただけでは、動作中の PHP は古いコードのままです。 mod_php なら Apache のプロセスの中、PHP-FPM なら FPM のプロセスの中に、修正前の PHP が読み込まれています。
Command
sudo needrestart -r l
Output(抜粋)
Services to be restarted:
systemctl restart apache2.service
使い方に応じて、次のサービスを再起動します。
| PHP の使い方 | 再起動するもの |
|---|---|
Apache + mod_php(libapache2-mod-php8.5) | apache2 |
PHP-FPM(php8.5-fpm) | php8.5-fpm |
| コマンドラインや cron で実行するスクリプト | 不要(実行のたびに新しい PHP が読み込まれる) |
| 常駐するワーカー(キューの処理など) | そのワーカー(supervisor や systemd で管理しているもの) |
Command
sudo systemctl restart apache2
systemctl status apache2 --no-pager | head -3
Output
● apache2.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/apache2.service; enabled; preset: enabled)
Active: active (running) since Sun 2026-10-25 10:12:41 JST; 4s ago
since の時刻が今になっていれば、再起動されています。もう一度 sudo needrestart -r l を実行して、何も出ないことを確認します。unattended-upgrades で自動適用された場合も、サービスが再起動されているとは限らないので、同じように確認します。
最後に、サイトが正常に表示されることを確認します。
7. apt の管理外の PHP がないかを確認する#
3 章で見たとおり、apt の管理外のものは apt upgrade では更新されません。PHP の場合、次のような入り方がよくあります。
Command
which -a php
Output
/usr/bin/php
ここに /usr/local/bin/php など別の場所のものが出てきたら、ソースからビルドしたものなどが残っています。上流の修正版(8.5.12 など)でビルドし直します。
コンテナで PHP を動かしている場合は、コンテナの中の PHP がホストとは別にあります。
Command
sudo docker ps --format '{{.Names}}\t{{.Image}}' | grep -i php
Output(説明用の例です)
legacy-app php:8.5.11-apache
ホストの PHP を更新しても、コンテナの中の PHP は古いままです。修正版のイメージ(この例なら php:8.5.12-apache)に変えて、作り直します。
更新前後の確認観点のまとめ#
ここまでの確認を、更新の前と後に分けて整理します。
| 時点 | 観点 | 確認すること | コマンド |
|---|---|---|---|
| 更新前 | 入っているもの | 対象のパッケージと、更新前のバージョンを控える | dpkg-query -W -f='${Package}\t${Version}\n' $PKGS > before.txt |
| 更新前 | 状態の異常 | 途中で止まった設定や、保留がない | dpkg --audit、apt-mark showhold |
| 更新前 | 動き方 | CLI、PHP-FPM、Apache のどれで動いているか | systemctl list-units 'php*' apache2.service |
| 更新後 | 全部上がった | 更新前の控えと比べて、取りこぼしがない | dpkg-query -W -f='${Package}\t${Version}\n' $PKGS | diff before.txt - |
| 更新後 | 修正が入っている | 対象の CVE が changelog にある | apt changelog <パッケージ> | grep CVE- |
| 更新後 | 設定が読める | 再起動の前に、文法エラーがない | php-fpm8.5 -t、apachectl configtest |
| 更新後 | プロセスが新しい | 動いているプロセスが再起動されている | sudo needrestart -r l |
| 更新後 | 機能が保たれている | 必要な拡張モジュールが読み込まれている | php -m を更新前と比べる |
| 更新後 | アプリが動く | 実際のリクエストが成功し、ログにエラーが増えていない | ヘルスチェック用の URL への curl -I、journalctl -u php8.5-fpm --since "10 min ago" |
パッケージのバージョンが上がっても、サービスが再起動されていなければ、メモリ上では古いコードが動き続けます。バージョンの確認だけで終わらせず、再起動とアプリの動作確認までを対応の完了とします。
すぐに更新できないとき#
検証が間に合わないなどの理由ですぐに更新できない場合でも、「何もしない」は選択肢になりません。PHP は設定変更だけでは完全に防げないとしています。英国の NCSC は、すぐに修正できないシステムはオフラインにすることも検討するよう呼びかけています。外部に公開しているサイトなら、一時的に公開を止める、アクセス元を絞る、といった手当てをしてから、検証と更新を進めます。
参考#
- Ubuntu CVE トラッカー: CVE-2026-109618
- Help Net Security: PHP2Shell: Critical PHP vulnerabilities patched, update now!
- Qualys: PHP2Shell: Find, Mitigate, and Patch Now
- PHP のセキュリティアドバイザリ: GHSA-w8hw-w9fx-j79w、GHSA-6wmf-4rv5-p3rp
まとめ#
apt listは 取得元・バージョン・状態 が 1 行で分かる。--installed、--upgradable、-aを使い分けるapt-mark showmanualの「手動」は、apt の管理内での話。セキュリティ上の盲点になりやすいのは、apt の管理外で入れたもの(ソースビルド、言語別のパッケージ管理、snap、外部リポジトリ)- バージョン番号だけで判断せず、changelog と CVE トラッカーで修正状況を確認する
- パッチは
apt update→ シミュレーション →apt upgrade→ 再起動の要否確認 の順で当てる。Removingが出たら立ち止まる - 特定のパッケージだけを更新するときは
apt install --only-upgrade。カーネルはメタパッケージ(linux-awsなど)を指定し、再起動してuname -rで切り替わりを確認する - 脆弱性対応は ソースパッケージで対象を特定 → トラッカーの修正版と比較 → 対象だけ更新 → changelog で確認 → サービスを再起動 の順で行う。
php -vのような上流のバージョン表示では判断しない - hold したパッケージと apt の管理外のものは、自動では更新されない。自分で追いかける
- リリースのアップグレードは別の作業として計画し、スナップショットを取ってから行う
全体を一通り把握できたら、lynis のような診断ツールで自動チェックするのも有効です。
Command
sudo apt install lynis
sudo lynis audit system
Output(抜粋)
Lynis security scan details:
Hardening index : 64 [############ ]
Tests performed : 268
Hardening index が対策の進み具合の目安です。結果の下に、改善の提案(Suggestions)が並びます。