EC2上に「AIDE」を用いてファイル改竄検知を行い、機能を詳しく調べてみた
はじめに
みなさんこんにちは、クラウド事業本部コンサルティング部の浅野です。
HIDS(host-based intrusion detection system)という概念をご存知ですか?
個々のサーバー内部でファイルの内容や設定変更を監視するセキュリティシステムです。
ネットワーク全体を監視するNIDS(network-based intrusion detection system)とは異なり、特定のホスト内の変化に焦点を当てたものです。
今回はLinuxベースのサーバ内で使用できるHIDSサービスである「AIDE」を用いて実際に動作検証を行い勉強しながらイメージを掴みたいと思います。
AIDEとは
AIDE(Advanced Intrusion Detection Environment)は、Linuxで使用可能なオープンソースなファイル整合性監視ツールです。システムファイルの改ざんや不正な変更を検出するため、ファイルのハッシュ値、権限、タイムスタンプなどを監視してくれます。Amazon Linux 2023だとdnfパッケージとして登録されており簡単にインストールが可能です。
動作環境
今回はAmazon Linux 2023のEC2一台を立て、その中に「AIDE」をインストールし、ファイル改竄検知のログをCloudWatch Log Groupに流して確認してみました。
- OS: Amazon Linux 2023(x86_64)
- EC2インスタンス: t3.micro
- パッケージ管理: dnf
検証
ネットワーク周りやEC2の用意は割愛します。コマンド入力の箇所で補足説明を入れていきます。
インストール
sudo dnf update -y
sudo dnf install -y aide
バージョン確認
バージョンと設定ファイルの場所、どの設定が有効化されているのか詳しく出力してくれます!
aide --version
AIDE 0.18.6
Compile-time options:
use pcre2: mandatory
use pthread: yes
use zlib compression: yes
use POSIX ACLs: yes
use SELinux: yes
use xattr: yes
use POSIX 1003.1e capabilities: no
use e2fsattrs: yes
use cURL: yes
use Mhash: no
use GNU crypto library: yes
use Linux Auditing Framework: yes
use locale: no
syslog ident: aide
syslog logopt: LOG_CONS
syslog priority: LOG_NOTICE
default syslog facility: LOG_LOCAL0
Default config values:
config file: /etc/aide.conf
database_in: file:/etc/aide.db
database_out: file:/etc/aide.db.new
Available compiled-in attributes:
acl: yes
.
.
.
// 長いので省略
注目すべきはここですね。
Default config values:
config file: /etc/aide.conf
database_in: file:/etc/aide.db
database_out: file:/etc/aide.db.new
設定ファイルは /etc/aide.conf
にあるみたいです。
確認してみましょう。
設定ファイルについて
初期状態の設定ファイル一覧を出力してみます。
sudo cat /etc/aide.conf
# Example configuration file for AIDE.
@@define DBDIR /var/lib/aide
@@define LOGDIR /var/log/aide
# The location of the database to be read.
database_in=file:@@{DBDIR}/aide.db.gz
# The location of the database to be written.
#database_out=sql:host:port:database:login_name:passwd:table
#database_out=file:aide.db.new
database_out=file:@@{DBDIR}/aide.db.new.gz
# Whether to gzip the output to database
gzip_dbout=yes
# Default.
log_level=warning
report_level=changed_attributes
report_url=file:@@{LOGDIR}/aide.log
report_url=stdout
#report_url=stderr
#NOT IMPLEMENTED report_url=mailto:root@foo.com
#NOT IMPLEMENTED report_url=syslog:LOG_AUTH
# These are the default rules.
#
#p: permissions
#i: inode:
#n: number of links
#u: user
#g: group
#s: size
#b: block count
#m: mtime
#a: atime
#c: ctime
#S: check for growing size
#acl: Access Control Lists
#selinux SELinux security context
#xattrs: Extended file attributes
#md5: md5 checksum
#sha1: sha1 checksum
#sha256: sha256 checksum
#sha512: sha512 checksum
#rmd160: rmd160 checksum
#tiger: tiger checksum
#haval: haval checksum (MHASH only)
#gost: gost checksum (MHASH only)
#crc32: crc32 checksum (MHASH only)
#whirlpool: whirlpool checksum (MHASH only)
FIPSR = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256
#R: p+i+n+u+g+s+m+c+acl+selinux+xattrs+md5
#L: p+i+n+u+g+acl+selinux+xattrs
#E: Empty group
#>: Growing logfile p+u+g+i+n+S+acl+selinux+xattrs
# You can create custom rules like this.
# With MHASH...
# ALLXTRAHASHES = sha1+rmd160+sha256+sha512+whirlpool+tiger+haval+gost+crc32
ALLXTRAHASHES = sha1+rmd160+sha256+sha512+tiger
# Everything but access time (Ie. all changes)
EVERYTHING = R+ALLXTRAHASHES
# Sane, with multiple hashes
# NORMAL = R+rmd160+sha256+whirlpool
NORMAL = FIPSR+sha512
# For directories, don't bother doing hashes
DIR = p+i+n+u+g+acl+selinux+xattrs
# Access control only
PERMS = p+i+u+g+acl+selinux
# Logfile are special, in that they often change
LOG = >
# Just do sha256 and sha512 hashes
LSPP = FIPSR+sha512
# Some files get updated automatically, so the inode/ctime/mtime change
# but we want to know when the data inside them changes
DATAONLY = p+n+u+g+s+acl+selinux+xattrs+sha256
# Next decide what directories/files you want in the database.
/boot NORMAL
/bin NORMAL
/sbin NORMAL
/lib NORMAL
/lib64 NORMAL
/opt NORMAL
/usr NORMAL
/root NORMAL
# These are too volatile
!/usr/src
!/usr/tmp
# Check only permissions, inode, user and group for /etc, but
# cover some important files closely.
/etc PERMS
!/etc/mtab
# Ignore backup files
!/etc/.*~
/etc/exports NORMAL
/etc/fstab NORMAL
/etc/passwd NORMAL
/etc/group NORMAL
/etc/gshadow NORMAL
/etc/shadow NORMAL
/etc/security/opasswd NORMAL
/etc/hosts.allow NORMAL
/etc/hosts.deny NORMAL
/etc/sudoers NORMAL
/etc/skel NORMAL
/etc/logrotate.d NORMAL
/etc/resolv.conf DATAONLY
/etc/nscd.conf NORMAL
/etc/securetty NORMAL
# Shell/X starting files
/etc/profile NORMAL
/etc/bashrc NORMAL
/etc/bash_completion.d/ NORMAL
/etc/login.defs NORMAL
/etc/zprofile NORMAL
/etc/zshrc NORMAL
/etc/zlogin NORMAL
/etc/zlogout NORMAL
/etc/profile.d/ NORMAL
/etc/X11/ NORMAL
# Pkg manager
/etc/yum.conf NORMAL
/etc/yumex.conf NORMAL
/etc/yumex.profiles.conf NORMAL
/etc/yum/ NORMAL
/etc/yum.repos.d/ NORMAL
/var/log LOG
/var/run/utmp LOG
# This gets new/removes-old filenames daily
!/var/log/sa
# As we are checking it, we've truncated yesterdays size to zero.
!/var/log/aide.log
# LSPP rules...
# AIDE produces an audit record, so this becomes perpetual motion.
# /var/log/audit/ LSPP
/etc/audit/ LSPP
/etc/libaudit.conf LSPP
/usr/sbin/stunnel LSPP
/var/spool/at LSPP
/etc/at.allow LSPP
/etc/at.deny LSPP
/etc/cron.allow LSPP
/etc/cron.deny LSPP
/etc/cron.d/ LSPP
/etc/cron.daily/ LSPP
/etc/cron.hourly/ LSPP
/etc/cron.monthly/ LSPP
/etc/cron.weekly/ LSPP
/etc/crontab LSPP
/var/spool/cron/root LSPP
/etc/login.defs LSPP
/etc/securetty LSPP
/var/log/faillog LSPP
/var/log/lastlog LSPP
/etc/hosts LSPP
/etc/sysconfig LSPP
/etc/inittab LSPP
/etc/grub/ LSPP
/etc/rc.d LSPP
/etc/ld.so.conf LSPP
/etc/localtime LSPP
/etc/sysctl.conf LSPP
/etc/modprobe.conf LSPP
/etc/pam.d LSPP
/etc/security LSPP
/etc/aliases LSPP
/etc/postfix LSPP
/etc/ssh/sshd_config LSPP
/etc/ssh/ssh_config LSPP
/etc/stunnel LSPP
/etc/vsftpd.ftpusers LSPP
/etc/vsftpd LSPP
/etc/issue LSPP
/etc/issue.net LSPP
/etc/cups LSPP
# With AIDE's default verbosity level of 5, these would give lots of
# warnings upon tree traversal. It might change with future version.
#
#=/lost\+found DIR
#=/home DIR
# Ditto /var/log/sa reason...
!/var/log/and-httpd
# Admins dot files constantly change, just check perms
/root/\..* PERMS
複雑そうに見えますが、記載していることは意外とシンプルです。
改竄チェック結果を出力するログファイルの設定や「/etc/hosts LSPP」など各ファイルに対してどのような変更を検知するかを定義しています。
例えば「LSPP」の意味として設定ファイルを確認すると「FIPSR+sha512」であり「FIPSR」は「p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256」と定義されているので、実態は
「p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256」に「sha512」のチェックサムを加えた設定ということになります。
各属性の意味は以下です。
設定するとこれらの属性の変更を検知してくれます。
- `p`: 権限 (permissions)
- `i`: inode番号
- `n`: シンボリックリンクなどのリンク数
- `u`: ユーザーID
- `g`: グループID
- `s`: ファイルサイズ
- `b`: ブロック数
- `m`: 変更時刻 (mtime)
- `c`: 作成時刻 (ctime)
- `md5`: MD5ハッシュ
- `sha256`: SHA256ハッシュ
- `acl`: アクセスコントロールリスト
- `selinux`: SELinuxセキュリティコンテキスト
- `xattrs` : 拡張ファイル属性
このように、設定ファイルに「ファイルのパス」と「変更内容の属性パターン」を追加していくことで各ファイルの改竄検知レベルを自由に設定可能です。
今回は特に追加や変更はしないので初期設定のまま進みます。
データベースファイル初期化とファイルチェック
AIDEは独自のデータベースにファイル改竄の履歴(ステート)を保持しそのファイルと照らし合わせることで改竄&設定変更を検知する仕組みになっています。ちなみにデータベースと記載していますがいわゆるRDBMSみたいなものではなく、監視対象ファイルのメタデータ(ハッシュ値、権限、タイムスタンプなど)を保存する独自フォーマットのバイナリファイルです。
通常「gz形式」として保存され、SQLクエリでテーブル操作などはできません。
以下コマンドでデータベースファイルを初期化し、現状の各ファイル情報を書き込みます。
# データベースファイルの初期化
sudo aide --init
Start timestamp: 2025-08-23 11:54:11 +0000 (AIDE 0.18.6)
AIDE successfully initialized database.
New AIDE database written to /var/lib/aide/aide.db.new.gz
Number of entries: 47787
---------------------------------------------------
The attributes of the (uncompressed) database(s):
---------------------------------------------------
/var/lib/aide/aide.db.new.gz
MD5 : X9fL*************************
SHA1 : iP/h*************************
SHA256 : PW87*************************
SHA512 : wPT3*************************
RMD160 : LCLr*************************
TIGER : a73**************************
CRC32 : E*****
WHIRLPOOL : QtTD*************************
GOST : Pf3F*************************
.
.
.
End timestamp: 2025-08-23 11:54:52 +0000 (run time: 0m 41s)
ファイルが書き込まれましたが、このままではファイルの変更内容を検知してくれません。
設定ファイルの上部の方に以下の記述があります。
※ /etc/aide.conf
から抜粋
database_in=file:@@{DBDIR}/aide.db.gz
.
.
database_out=file:@@{DBDIR}/aide.db.new.gz
「database_in」は、AIDEがチェック時に参照する既存のデータベースファイルです。
これは「現在の基準となるデータベース」として機能し、aide --check
コマンドを実行した際に、このファイルに保存されている情報と現在のシステム状態を比較します。
一方、「database_out」は、aide --init
やaide --update
を実行した際に新しく作成されるデータベースファイルです。これは「作成中・更新中のデータベース」として位置づけられ、作成が完了した後に管理者が手動で「database_in」にコピーすることでアクティブ化されます。
安全性と整合性の確保のためにこの仕組みが採用されています。
既存のデータベース(database_in)を保護しながら新しいデータベースを作成し、新しいデータベース作成中でも既存のチェック機能を維持できます。
また、管理者による手動コピーを必要とすることで、データベース更新を明示的に承認する仕組みとなっているようです。
なので作成されたデータベースをコピーしていきましょう。
sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
ここまでできたら一度checkコマンドを通してみます。
# 現状のファイルとデータベースファイル情報を比較する
sudo aide --check
Start timestamp: 2025-08-23 12:00:17 +0000 (AIDE 0.18.6)
AIDE found NO differences between database and filesystem. Looks okay!!
Number of entries: 47787
---------------------------------------------------
The attributes of the (uncompressed) database(s):
---------------------------------------------------
/var/lib/aide/aide.db.gz
MD5 : X9fL7thFKW6LIp0BgRD4Kw==
.
.
.
End timestamp: 2025-08-23 12:01:28 +0000 (run time: 1m 11s)
初期化してからファイルを何も変更していないので「NO differences」となっておりチェックが成功しました。上記の内容はレポートとして「/var/log/aide/aide.log」に保存されます。
ここから実際にファイルを変更して検知内容をチェックしてみましょう。
テスト1: ファイル内容変更の検知
試しに/etc/passwd
の中身に追記してみます。
パスワードファイルの検知タイプはデフォルトで「NORMAL」に設定されており、「NORMAL」の実態は
「p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256+sha512」なので
以下の設定が含まれておりファイルの中身が変わると検知してくれるはずです。
- s: ファイルサイズの変更
- sha256: SHA256ハッシュ値の変更(ファイル内容変更を検知)
- sha512: SHA512ハッシュ値の変更(ファイル内容変更を検知)
# /etc/passwdに1行追記
echo "example" | sudo tee -a /etc/passwd
# 検出確認
sudo aide --check
. #省略
.
.
File: /etc/passwd
Size : 1566 | 1637
Mtime : 2025-08-23 11:48:04 +0000 | 2025-08-23 12:38:50 +0000
Ctime : 2025-08-23 11:48:04 +0000 | 2025-08-23 12:38:50 +0000
Inode : 70516 | 1005318
SHA256 : MJcBiGJLMiLBIxmRNQMn/BHCrZJXwNl0 | 5AJeXPvnTYngFOiSNTznzg8hX/bPofKC
lAYcaaPIO5s= | ksXpmv37634=
SHA512 : VrcRmXvylYjTpM2Hx9U1f6fKNpIw9d+G | gvfIx+7rO5wrPbuSBhH4XPkz5EtzIVyH
LbZJGI449ajN6dz6qpUpTtWPSnAoGhUr | WBHhl7C5aGey9PGB/8KMOR3FXu9aBMjK
+0lGuvexU5Tt4OAFQIXXXA== | iOhIkweN7r3r1IJLHIIdAQ==
.
.
. #省略
実際に「ファイルサイズ」が大きくなっており「SHA256」や「SHA512」のチェックサムが変わっておりファイルの中身が改竄されていることが検知されました。どの部分が変更されたのかまでは確認できません。あくまでIDSの目的としては「改竄されたことに気づく」のがメインの目的だからですね。
テスト2: ファイル権限変更の検知
続いて/etc/hosts
の権限をchmod
コマンドで変更してみます。
設定ファイルでは属性が先ほど説明した「LSPP」に設定されているので、ファイル権限の変更も検知してくれるはずです。
まず既存の権限を確認します。
ls -ll /etc/hosts
-rw-r--r--. 1 root root 126 Aug 13 21:08 /etc/hosts
実際に変更してみましょう
# /etc/hostsの権限変更
sudo chmod 777 /etc/hosts
# 検出確認
sudo aide --check
. #省略
.
.
File: /etc/hosts
Perm : -rw-r--r-- | -rwxrwxrwx
Ctime : 2025-08-13 21:08:52 +0000 | 2025-08-23 12:52:14 +0000
ACL : A: user::rw- | A: user::rwx
A: group::r-- | A: group::rwx
A: other::r-- | A: other::rwx
.
.
.
うおおおお!権限が変更されたことがわかる上にどのように読み・書き・実行の権限が変わったかまで詳細に差分を出力してくれました。めちゃくちゃわかりやすいですね。
テスト3: 監視対象外ファイルの変更
当たり前ですが/etc/aide.conf
に記載がないファイルに関してはデータベースとして監視対象外です。
/tmp/testfile
としてファイルを新規作成しチェックしてみましたが、もちろん検知内容は出てきませんでした。
# /tmpディレクトリ(監視対象外)にファイル作成
echo "test" | sudo tee /tmp/testfile
# 検出確認
sudo aide --check
(/tmp に関して何も表示されない)
データベースファイルの更新
チェックした後は必ずaide --update
を行いデータベースファイルに変更内容の取り込みをしましょう。
sudo aide --update
Start timestamp: 2025-08-23 12:56:40 +0000 (AIDE 0.18.6)
AIDE found differences between database and filesystem!!
New AIDE database written to /var/lib/aide/aide.db.new.gz
さらにデータベースファイルを/var/lib/aide/aide.db.gz
に反映するためのコピーコマンドの実行もお忘れなく!
sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
これでもう一度チェックコマンドを実行すると変更内容が反映されたことが確認できました。
sudo aide --check
Start timestamp: 2025-08-23 12:59:46 +0000 (AIDE 0.18.6)
AIDE found differences between database and filesystem!!
Summary:
Total number of entries: 47829
Added entries: 0
Removed entries: 0
Changed entries: 2
---------------------------------------------------
Changed entries:
---------------------------------------------------
f > ... mc..H... : /etc/passwd
.
.
. #省略
End timestamp: 2025-08-23 13:01:06 +0000 (run time: 1m 20s)
cronなどで定期的にチェックする際は、必ず最後にaide --update
とデータベースファイルのコピー作業を忘れないように注意しましょう。
(おまけ) CloudWatch Logs にAIDEログ内容を配信する
レポートファイル/var/log/aide/aide.log
の中身をCloudWatch Logsに送信するように設定してみましょう。
CloudWatch Logs にAIDEログ内容を配信する
CloudWatch Logsエージェントのインストール
sudo dnf install -y amazon-cloudwatch-agent
設定ファイルに以下の記述を行えば/var/log/aide/aide.log
が変更されるとCloudWatch ロググループに配信してくれます。
# 設定ファイル作成
sudo tee /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json << 'EOF'
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/aide/aide.log",
"log_group_name": "/aws/ec2/aide/checks",
"log_stream_name": "{instance_id}-aide-checks",
"timezone": "UTC"
}
]
}
}
}
}
EOF
# エージェント有効化・開始
sudo systemctl enable amazon-cloudwatch-agent
sudo systemctl start amazon-cloudwatch-agent
状態を確認し以下のような記述が見えたらOK
# 状態確認
sudo systemctl status amazon-cloudwatch-agent
● amazon-cloudwatch-agent.service - Amazon CloudWatch Agent
Loaded: loaded (/etc/systemd/system/amazon-cloudwatch-agent.service; enabled; preset: disabled)
Active: active (running) since Sat 2025-08-23 12:18:11 UTC; 2h 2min ago
.
.
.
数分後、ロググループに送信されていることが確認できました!
最後に
今回はAIDEをEC2上に導入しインスタンス内のファイル改竄検知を実際に行ってみました。シンプルかつ直感的な比較的使いやすいツールだと思いました。
しかし、実際に本番環境で運用する際には各種ファイルの取り扱い、どの属性を監視するか、チェック頻度などを細かく要件化した上できちんと設定しないと過剰検知や監視不足に繋がりますので注意が必要だと感じました。
またAIDEはLinuxベースのインスタンスでしか動作せずWindowsでは別のHIDSサービスの検討が必要となります。今回は以上です。