■Apacheセキュリティとパフォーマンスのベストプラクティス
■Apacheセキュリティとパフォーマンスのベストプラクティス
はじめに
Webアプリケーションのセキュリティを考える際、HTTPセキュアヘッダーの設定は「ブラウザに挙動を決めるうえで、最初の一手」として非常に重要です。そこからもう一歩踏み込んで、より高度なWebサーバーのハードニングを行うためには、Apache自体の設定を適切に行う必要があります。
本記事では、Apache 2.4系における実践的なセキュリティ強化とパフォーマンス最適化の手法を解説します。単に設定を羅列するだけではなく、「なぜその設定が必要なのか」、「攻撃者の視点から見てどのような脆弱性を突かれる可能性があるのか」を理解していただくことが目的です。
なお、本記事の対象者は、「エンジニア経験3年目程度の方」を想定しております。
また、HTTPセキュアヘッダーについては、前回の記事で詳しく解説しています。
よろしければこちらも併せてご参照ください。
1. 基本的なセキュリティ強化
1.1 サーバー情報の隠蔽
なぜ重要なのか
攻撃者は、標的とするサーバを決定する際に、必ず情報収集(フットプリンティング)を行います。
例えば、サーバーのバージョン情報は、既知の脆弱性を特定するための重要な手がかりとなります。バナーとして「Apache/2.4.29」という情報が漏れていれば、攻撃者はCVEデータベースからそのバージョン特有の脆弱性を簡単に検索できます。
情報を隠蔽することは「Security through Obscurity(隠蔽によるセキュリティ)」と呼ばれています。
単体では完全な防御となりませんが、攻撃の難易度を上げる重要な要素とされています。
実装方法
# /etc/apache2/conf-enabled/security.conf (Debian/Ubuntu)
# /etc/httpd/conf.d/security.conf (RHEL/CentOS)
# サーバーのバージョン情報を最小限に
ServerTokens Prod
# レスポンスヘッダ例: Server: Apache (バージョン番号なし)
# エラーページのフッターにサーバー情報を表示しない
ServerSignature Off
# PHPのバージョン情報も隠蔽(PHPを使用している場合)
# php.iniで設定: expose_php = Off
補足: ServerTokensの設定値
- Full: Apache/2.4.29 (Unix) PHP/7.2.10 (最も詳細)
- OS: Apache/2.4.29 (Unix)
- Min: Apache/2.4.29
- Prod: Apache (推奨)
1.2 ディレクトリリスティングの無効化
なぜ重要なのか
ディレクトリリスティングが有効な場合に、インデックスファイル(例:index.htmlなど)が存在しないディレクトリへアクセスすると、ディレクトリ内ファイルの一覧が表示されます。これにより以下のリスクが発生します:
- 設定ファイルやバックアップファイルの露出: .bak、.old、.configなどの本来公開すべきでないファイルが公開される
- ディレクトリ構造の把握: アプリケーションの内部構造を推測されやすくなる
- 隠しファイルの発見: 開発時のメモやテストファイルなど
実装方法
# グローバル設定(全ディレクトリに適用)
<Directory />
Options -Indexes
# または、他のオプションと組み合わせる場合
# Options FollowSymLinks -Indexes
</Directory>
# 特定のディレクトリのみ許可する場合(例:ダウンロードディレクトリ)
<Directory /var/www/html/downloads>
Options +Indexes
# より安全にする場合は、表示をカスタマイズ
IndexOptions FancyIndexing HTMLTable SuppressDescription
IndexIgnore .* *~ *# HEADER* README* RCS CVS *,v *,t
</Directory>
1.3 .htaccessファイルの無効化
なぜ重要なのか
.htaccessは原則として無効化を強く推奨します
マルチテナント環境のような特殊な事情がない限り、以下の理由から使用を避けるのが望ましいです。
1. パフォーマンスの大幅な低下
- リクエストごとに全ディレクトリツリーを遡って`.htaccess`を探索
- 例:`/var/www/html/blog/images/photo.jpg`へのアクセス時、最大5箇所の`.htaccess`をチェック
2. 予期しないトラブルの温床
- 開発者が勝手に`.htaccess`を設置し、本番環境で問題発生
- 設定の優先順位が複雑になり、デバッグが困難になる
- バージョン管理から漏れやすい
3. セキュリティリスク
- Webアプリケーションの脆弱性により`.htaccess`が改ざんされ、バックドア設置される
- 意図しないリダイレクトやアクセス許可の変更
実装方法
# すべてのディレクトリで.htaccessを無効化
<Directory />
AllowOverride None
</Directory>
# 必要な場合は、特定のディレクトリのみ有効化
# ただし、できるだけ限定的に
<Directory /var/www/html/wordpress>
# 最小限の権限のみ付与
AllowOverride FileInfo Limit
# すべて許可するAllは避ける
# AllowOverride All
</Directory>
移行のヒント: 既存の.htaccess設定は、メインの設定ファイルに移動させましょう。
2. パフォーマンス最適化
2.1 Keep-Aliveの最適化
なぜ重要なのか
HTTP/1.1のKeep-Alive(持続的接続)は、一度確立したTCP接続を複数のHTTPリクエストで再利用する仕組みです。
これにより:
- TCPハンドシェイクの削減: 3ウェイハンドシェイクの回数が減少
- SSL/TLSネゴシエーションの削減: HTTPS通信でより効果的
- レイテンシの改善: 特に画像やCSS、JavaScriptなど多数のリソースを読み込むページで効果的
ただし、設定を誤ると接続が長時間占有され、DoS攻撃の標的になる可能性があります。
実装方法
# Keep-Aliveを有効化
KeepAlive On
# 一つの接続で処理する最大リクエスト数
# 大きすぎると接続が長時間占有される
MaxKeepAliveRequests 100
# 次のリクエストを待つ最大時間(秒)
# 短すぎると効果が薄れ、長すぎるとリソースを浪費
KeepAliveTimeout 5
# 高トラフィックサイトの場合はより短く
# KeepAliveTimeout 2
2.2 圧縮の有効化(mod_deflate)
なぜ重要なのか
テキストベースのコンテンツ(HTML、CSS、JavaScript、JSON等)は圧縮により60-80%のサイズ削減が可能です。
これにより:
- 帯域幅の削減: サーバーの転送量とコストを削減
- ページ読み込み速度の向上: 特にモバイル環境で効果的
- SEOの改善: Googleはページ速度を評価指標に含めている
実装方法
# mod_deflateの有効化
# Debian/Ubuntu
sudo a2enmod deflate
sudo systemctl restart apache2
# RHEL/CentOS(通常はデフォルトで有効)
# LoadModule deflate_module modules/mod_deflate.so
<IfModule mod_deflate.c>
# 圧縮レベル(1-9、デフォルトは6)
DeflateCompressionLevel 6
# 圧縮するMIMEタイプを指定
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/json
# 古いブラウザのバグ対策
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
# プロキシ経由のリクエストも圧縮
Header append Vary User-Agent
</IfModule>
注意: 画像(JPEG、PNG等)やPDF、動画ファイルなど、既に圧縮されているファイルは圧縮しないこと。CPU使用率が上がるだけで効果はありません。
2.3 ブラウザキャッシュの設定(mod_expires)
なぜ重要なのか
適切なキャッシュ設定により:
- サーバー負荷の軽減: 静的ファイルへのリクエストが減少
- ユーザー体験の向上: 2回目以降のアクセスが高速化
- 帯域幅の節約: 転送量の削減
ただし、設定を誤ると更新したファイルが反映されない問題が発生します。
実装方法
# mod_expiresの有効化
# Debian/Ubuntu
sudo a2enmod expires
sudo systemctl restart apache2
# RHEL/CentOS
# LoadModule expires_module modules/mod_expires.so
<IfModule mod_expires.c>
ExpiresActive On
# デフォルトの有効期限(アクセスから1時間)
ExpiresDefault "access plus 1 hour"
# ファイルタイプ別の設定
# 画像ファイル(めったに変更されない)
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/x-icon "access plus 1 year"
# Webフォント
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType application/font-woff "access plus 1 year"
# CSS/JavaScript(定期的に更新される可能性)
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
# HTML(頻繁に更新される)
ExpiresByType text/html "access plus 0 seconds"
# データファイル
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
</IfModule>
# Cache-Controlヘッダーも併用
<FilesMatch "\.(jpg|jpeg|png|gif|svg|css|js)$">
Header set Cache-Control "public, max-age=31536000"
</FilesMatch>
# 動的コンテンツはキャッシュさせない
<FilesMatch "\.(php|cgi|pl)$">
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
</FilesMatch>
バージョニングのベストプラクティス: CSSやJavaScriptファイルは、ファイル名にバージョン番号を含める(例:style.v1.2.3.css)か、クエリパラメータを付ける(例:style.css?v=1.2.3)ことで、キャッシュ問題を回避できます。
3. SSL/TLS設定の強化
3.1 脆弱な暗号スイートの排除
なぜ重要なのか
SSL/TLSの暗号スイート選択は、通信の安全性を左右する最も重要な要素の一つです。残してはいけない暗号スイートを理解することが、セキュリティコンサルタントとして最も重要です。
排除すべき暗号スイートとその理由:
- SSLv2, SSLv3: POODLE攻撃などの脆弱性
- TLS 1.0, 1.1: 暗号強度が不十分、2020年に主要ブラウザがサポート終了
- RC4: ストリーム暗号の弱点が発見されている
- 3DES: 64ビットブロック暗号、Sweet32攻撃に脆弱
- MD5: ハッシュ衝突攻撃が現実的
- Export暗号: 意図的に弱体化された暗号(FREAK攻撃、Logjam攻撃)
- NULL暗号: 暗号化なし(デバッグ用)
- Anonymous暗号: 認証なし、中間者攻撃に脆弱
実装方法
# SSL/TLSモジュールの設定
# /etc/apache2/mods-available/ssl.conf (Debian/Ubuntu)
# /etc/httpd/conf.d/ssl.conf (RHEL/CentOS)
# プロトコルバージョンの制限(TLS 1.2以上のみ許可)
SSLProtocol -all +TLSv1.2 +TLSv1.3
# サーバー側の暗号スイート優先順位を使用
SSLHonorCipherOrder On
# 安全な暗号スイートのみを許可
# 以下は高セキュリティ設定の例
SSLCipherSuite "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
# より詳細な設定(否定形式で脆弱な暗号を明示的に除外)
SSLCipherSuite "ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:!aNULL:!MD5:!DSS:!RC4:!3DES:!CAMELLIA:!SEED:!IDEA:!PSK:!SRP:!EXP:!CBC"
# TLS 1.3用の暗号スイート(Apache 2.4.36以降)
SSLCipherSuite TLSv1.3 "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
# セッションキャッシュの設定
SSLSessionCache shmcb:/var/cache/apache2/ssl_scache(512000)
SSLSessionCacheTimeout 300
# OCSP Staplingの有効化(証明書の有効性確認の高速化)
SSLUseStapling On
SSLStaplingCache shmcb:/var/cache/apache2/ssl_stapling(32768)
3.2 DHパラメータの強化
なぜ重要なのか
- Diffie-Hellman(DH)鍵交換で使用するパラメータが弱い場合、Logjam攻撃などに脆弱になります。デフォルトの1024ビットでは不十分です。
実装方法
# 2048ビット以上のDHパラメータを生成
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
# Apache設定に追加
SSLOpenSSLConfCmd DHParameters "/etc/ssl/certs/dhparam.pem"
4. エラーハンドリングとロギング
4.1 カスタムエラーページ
なぜ重要なのか
デフォルトのエラーページは以下の問題があります:
- 情報漏洩: サーバーバージョンやモジュール情報の露出
- ユーザー体験の低下: 技術的なメッセージで一般ユーザーには理解困難
- フィッシングの標的: 攻撃者が偽のエラーページを表示させる可能性
実装方法
# カスタムエラーページの設定
ErrorDocument 400 /error/400.html
ErrorDocument 401 /error/401.html
ErrorDocument 403 /error/403.html
ErrorDocument 404 /error/404.html
ErrorDocument 500 /error/500.html
ErrorDocument 502 /error/502.html
ErrorDocument 503 /error/503.html
# エラーページ用ディレクトリの設定
<Directory /var/www/html/error>
# エラーページ自体へのアクセスは許可
Require all granted
# ただし、ディレクトリリスティングは禁止
Options -Indexes
</Directory>
# エラーページのサンプル(404.html)
# ユーザーフレンドリーかつセキュアな内容に
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ページが見つかりません</title>
<style>
body { font-family: sans-serif; text-align: center; padding: 50px; }
h1 { color: #333; }
</style>
</head>
<body>
<h1>404 - ページが見つかりません</h1>
<p>お探しのページは存在しないか、移動した可能性があります。</p>
<p><a href="/">トップページに戻る</a></p>
</body>
</html>
4.2 ロギングの最適化
なぜ重要なのか
適切なロギングは:
- セキュリティインシデントの検知: 不正アクセスの痕跡を発見
- パフォーマンス分析: ボトルネックの特定
- コンプライアンス対応: 監査要件への対応
ただし、過度なロギングはパフォーマンスを低下させ、ディスク容量を圧迫します。
実装方法
# ログレベルの設定(本番環境)
LogLevel warn
# 開発環境ではより詳細に
# LogLevel info
# カスタムログフォーマット
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %T/%D" combined_plus
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" proxy
# アクセスログ
CustomLog ${APACHE_LOG_DIR}/access.log combined_plus
# ロードバランサー経由の場合
SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded
CustomLog ${APACHE_LOG_DIR}/access.log proxy env=forwarded
# 静的ファイルへのアクセスはログから除外(パフォーマンス向上)
SetEnvIf Request_URI "^/images/" dontlog
SetEnvIf Request_URI "^/css/" dontlog
SetEnvIf Request_URI "^/js/" dontlog
SetEnvIf Request_URI "\.jpg$" dontlog
SetEnvIf Request_URI "\.png$" dontlog
SetEnvIf Request_URI "\.gif$" dontlog
CustomLog ${APACHE_LOG_DIR}/access.log combined env=!dontlog
# セキュリティ関連のログを別ファイルに
<IfModule mod_security2.c>
SecAuditLog ${APACHE_LOG_DIR}/modsec_audit.log
</IfModule>
4.3 ログローテーション
# /etc/logrotate.d/apache2
/var/log/apache2/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
/usr/sbin/apache2ctl graceful > /dev/null
endscript
}
5. リソース制限とDoS対策
5.1 タイムアウト設定
なぜ重要なのか
適切なタイムアウト設定は、Slowloris攻撃などのDoS攻撃を防ぎます。攻撃者は意図的に遅い接続を維持することで、サーバーリソースを枯渇させようとします。
実装方法
# 全体的なタイムアウト(秒)
Timeout 60
# リクエストボディの受信タイムアウト
<IfModule mod_reqtimeout.c>
# ヘッダーの受信: 最初の20秒 + 500バイト/秒で最大30秒
RequestReadTimeout header=20-30,MinRate=500
# ボディの受信: 最初の20秒 + 500バイト/秒
RequestReadTimeout body=20,MinRate=500
# 大きなファイルアップロードを許可する場合
<Location /upload>
RequestReadTimeout body=20-120,MinRate=500
</Location>
</IfModule>
5.2 同時接続数の制限
なぜ重要なのか
MPM(Multi-Processing Module)の設定により、サーバーが処理できる同時接続数を制御します。設定が不適切だと:
- 少なすぎる場合: 正当なユーザーがアクセスできない
- 多すぎる場合: メモリ不足やCPU過負荷でサーバーダウン
上記の不具合が発生するため、適切な同時接続数のチューニングが必要となります。
実装方法
# MPMの確認
apachectl -V | grep MPM
# Prefork MPM(プロセスベース)の設定
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 150 # 最大同時接続数
ServerLimit 150 # MaxRequestWorkersと同じ値
MaxConnectionsPerChild 10000 # プロセスの再起動頻度
</IfModule>
# Worker MPM(スレッドベース)の設定 - より効率的
<IfModule mpm_worker_module>
StartServers 3
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400 # 最大同時接続数
ServerLimit 16 # MaxRequestWorkers / ThreadsPerChild
MaxConnectionsPerChild 0 # 0 = 無制限
</IfModule>
# Event MPM(イベント駆動)の設定 - 最も効率的
<IfModule mpm_event_module>
StartServers 3
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
ServerLimit 16
AsyncRequestWorkerFactor 2 # Keep-Alive接続の処理効率化
</IfModule>
5.3 mod_evasiveによるDoS対策
なぜ重要なのか
mod_evasiveは、同一IPアドレスからの大量リクエストを検知・ブロックします。
実装方法
# インストール
# Debian/Ubuntu
sudo apt-get install libapache2-mod-evasive
# RHEL/CentOS
sudo yum install mod_evasive
<IfModule mod_evasive20.c>
# 同一ページへの秒間リクエスト数の閾値
DOSPageCount 5
# サイト全体への秒間リクエスト数の閾値
DOSSiteCount 50
# ページカウントの間隔(秒)
DOSPageInterval 1
# サイトカウントの間隔(秒)
DOSSiteInterval 1
# ブロック時間(秒)
DOSBlockingPeriod 600
# 通知メール
DOSEmailNotify security@example.com
# ホワイトリスト
DOSWhitelist 127.0.0.1
DOSWhitelist 192.168.1.*
</IfModule>
6. 運用面での推奨事項
6.1 設定ファイルの構造化
なぜ重要なのか
設定ファイルの適切な構造化により:
- 保守性の向上: 変更箇所が明確
- ミスの削減: 設定の重複や競合を防ぐ
- 環境別管理: 開発・ステージング・本番の切り替えが容易
実装方法
# Debian/Ubuntu の構造
/etc/apache2/
├── apache2.conf # メイン設定ファイル
├── conf-available/ # 利用可能な設定
├── conf-enabled/ # 有効な設定(シンボリックリンク)
├── mods-available/ # 利用可能なモジュール
├── mods-enabled/ # 有効なモジュール
├── sites-available/ # 利用可能なサイト設定
├── sites-enabled/ # 有効なサイト設定
└── ports.conf # ポート設定
# サイト設定の例
# /etc/apache2/sites-available/example.com.conf
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/example.com
# HTTPSへリダイレクト
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/example.com
# SSL設定
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
SSLCertificateChainFile /etc/ssl/certs/intermediate.crt
# 共通設定をインクルード
Include /etc/apache2/conf-available/security-headers.conf
Include /etc/apache2/conf-available/performance.conf
</VirtualHost>
# 有効化
sudo a2ensite example.com
sudo systemctl reload apache2
6.2 設定のテストとデプロイ
なぜ重要なのか
設定ミスによるサービス停止を防ぐため、デプロイ前の検証は必須です。
実装方法
# 1. 設定ファイルの構文チェック
sudo apache2ctl configtest
# または
sudo apachectl -t
# 2. 詳細な設定情報の確認
sudo apache2ctl -S # Virtual Host設定の確認
sudo apache2ctl -M # ロードされているモジュールの確認
# 3. グレースフルリスタート(接続中のクライアントに影響なし)
sudo apache2ctl graceful
# または
sudo systemctl reload apache2
# 4. 設定変更のスクリプト化
#!/bin/bash
# deploy-apache-config.sh
# バックアップ
cp -r /etc/apache2 /etc/apache2.backup.$(date +%Y%m%d_%H%M%S)
# 構文チェック
if apache2ctl configtest; then
echo "設定ファイルのチェック: OK"
apache2ctl graceful
echo "Apache再起動: 完了"
else
echo "エラー: 設定ファイルに問題があります"
exit 1
fi
6.3 監視とメンテナンス
mod_statusによる監視
# mod_statusの設定(アクセス制限必須)
<Location /server-status>
SetHandler server-status
# IP制限
Require ip 192.168.1.0/24
# または認証
AuthType Basic
AuthName "Server Status"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
</Location>
# 拡張情報の表示
ExtendedStatus On
定期的なセキュリティアップデート
# セキュリティアップデートの確認
# Debian/Ubuntu
sudo apt update
sudo apt list --upgradable | grep -i apache
# RHEL/CentOS
sudo yum check-update httpd
# 自動アップデートの設定(セキュリティパッチのみ)
# Debian/Ubuntu - unattended-upgrades
# RHEL/CentOS - yum-cron
終わりに
本記事では、Apache 2.4系における実践的なセキュリティ強化とパフォーマンス最適化について解説しました。重要なポイントを振り返ると:
- セキュリティは多層防御: 単一の対策に頼らず、複数の防御層を構築する
- パフォーマンスとセキュリティのバランス: 過度なセキュリティ設定はパフォーマンスを損なう可能性がある
- 継続的な改善: 定期的な見直しとアップデートが不可欠
特にSSL/TLS設定では、「使ってはいけない暗号スイート」を確実に除外することが、ハードニングの要です。なお、これらの設定は、あくまで出発点です。実際の運用では、アプリケーションの特性、ユーザーの環境、コンプライアンス要件などを考慮して、適切にカスタマイズする必要があります。
また、Apacheの設定だけでなく、HTTPセキュアヘッダーの設定、WAF(Web Application Firewall)の導入、定期的な脆弱性診断など、総合的なセキュリティ対策を実施することが重要です。