#セキュリティ


このサイトの作り方 (8) セキュリティ対策

tokeisan4

個人で運営するサイトでも、公開する以上はセキュリティ対策が欠かせません。今回は、このサイトを守るために導入した仕組みについて書きます。 まず入れたのが「fail2ban」というソフトです。ログインを何度も間違えたり、明らかに不審なアクセスを繰り返すIPアドレスを、自動的に一定時間ブロックしてくれます。自分自身の作業用アドレスまで誤ってブロックされないよう、自宅のネットワークだけは対象外にする設定もあわせて行いました。 次に「ModSecurity」というWebアプリケーションファイアウォールを導入しました。SQLインジェクションやXSSといった、Webサイトへのよくある攻撃パターンを検知する仕組みです。まずは検知だけを行うモードで様子を見て、誤検知が少ないことを確認してから、本格的な防御に切り替えていく方針にしています。実際、長文投稿への対応中に、正規のリクエストまで誤検知されてしまう場面もあり、ルールを個別に調整しました。 管理画面についても、自宅のネットワークからしかアクセスできないよう制限をかけるなど、外部に露出させる範囲を最小限にする工夫も加えています。サーバー自体も、外部から直接ポートを開放せず、特殊なトンネル技術を使って必要な通信だけを中継する構成にしました。 セキュリティ対策は「これで完璧」という終わりがない分野です。段階的に対策を積み重ねていくことが、結局は一番の近道だと感じています。 もう一つ意識したのは、対策を入れすぎて正常な利用者まで不便にしてしまわないことです。実際、ファイアウォールの検知ルールが厳しすぎて、正規の投稿フォームが誤って弾かれてしまう場面がありました。安全性と使いやすさは、常に天秤にかけながら調整していく必要があるのだと学びました。 次回は、サイトの状態を見張る監視の仕組みについて書きます。

Apache設定まるわかり!よく使うディレクティブ解説

tokeisan4

このサイトを自宅サーバー(RHEL + Apache httpd)で運用する中で、実際に使ってきたApacheの設定項目(ディレクティブ)を、体系立ててまとめてみます。公式ドキュメントは網羅的すぎて最初はとっつきにくいので、「実際に何のために使うのか」という視点で整理していきます。 ■ そもそもディレクティブとは Apacheの設定ファイル(httpd.confや、conf.d以下の個別ファイル)に書く、1行1行の設定項目のことをディレクティブと呼びます。「このURLパスにはこの権限が必要」「このドメインが来たらこのアプリを表示する」といった、Apacheの振る舞いを決める命令文だと考えると分かりやすいです。 ■ VirtualHost:1台のサーバーで複数サイトを扱う 同じサーバー上で、異なるドメインや異なるポートごとに別々のサイトを配信するための仕組みです。 <VirtualHost *:443> ServerName example.com ServerAlias www.example.com SSLEngine on SSLCertificateFile /etc/httpd/ssl/server.crt SSLCertificateKeyFile /etc/httpd/ssl/server.key </VirtualHost> ServerNameで、このVirtualHostがどのドメインへのリクエストに反応するかを指定します。ServerAliasは、追加で反応させたい別名(wwwありなし等)を指定する際に使います。 ■ WSGIDaemonProcess・WSGIScriptAlias:Pythonアプリとの連携 DjangoのようなPython製Webアプリケーションを動かすには、mod_wsgiというモジュールを使います。 WSGIDaemonProcess myapp python-home=/path/to/venv python-path=/path/to/project WSGIProcessGroup myapp WSGIScriptAlias / /path/to/project/project/wsgi.py WSGIDaemonProcessは、Apacheの本体プロセスとは別に、Pythonアプリ専用の子プロセスを立ち上げる設定です。python-homeには仮想環境(venv)のパス、python-pathにはDjangoプロジェクトのパスを指定します。WSGIScriptAliasは、どのURLパスにアクセスされたら、どのwsgi.pyファイルを呼び出すかを紐付けます。 なお、WSGIApplicationGroup %{GLOBAL} という設定を追加しないと、「populate() isn't reentrant」というエラーが出ることがあります。これは、複数のPythonアプリケーションが同じインタプリタ内で衝突しないようにする設定で、Django単体で運用する場合は基本的に付けておくとトラブルを避けられます。 ■ Alias:静的ファイルの配信場所を指定する 画像やCSSなど、Pythonを介さずそのまま配信したいファイル群のために使います。 Alias /static/ /data/static/ <Directory /data/static/> Require all granted </Directory> Aliasで「このURLパスへのアクセスは、実際にはこのディレクトリの中身を見せる」という対応関係を作ります。続くDirectiveブロックで、そのディレクトリへのアクセス許可を明示的に与える必要があります。 ■ Require:アクセス制御 誰がそのURLパス・ディレクトリにアクセスできるかを制御します。 <Location /admin/> Require ip 192.168.11.0/24 </Location> この例では、管理画面へのアクセスを、自宅LAN内のIPアドレス帯だけに制限しています。全世界に公開したくない管理系のパスには、このような制限をかけておくと安心です。全員に許可したい場合はRequire all grantedを使います。 ■ SSLEngine・SSLCertificateFile:HTTPS化 <VirtualHost *:443> SSLEngine on SSLCertificateFile /etc/httpd/ssl/server.crt SSLCertificateKeyFile /etc/httpd/ssl/server.key </VirtualHost> SSLEngine onで、そのVirtualHostにSSL/TLSによる暗号化通信を有効にします。証明書ファイルと秘密鍵ファイルのパスを、それぞれ指定します。自己署名証明書を使う場合、ブラウザに警告が出ますが、リバースプロキシ(Cloudflareなど)を前段に置いている場合は、オリジンサーバー同士の通信としては許容されることもあります。 ■ RewriteEngine・RewriteRule:URLの書き換え RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] httpでのアクセスを、httpsへ自動的にリダイレクトする、よくあるパターンです。RewriteCondで条件(この場合「HTTPSが使われていない場合」)を指定し、RewriteRuleで実際の書き換えルールを定義します。ただし、特定のパス(Zabbixの管理画面など)だけリダイレクトの対象から除外したい場合は、RewriteCondに除外条件を追加する必要があります。 ■ ModSecurity関連ディレクティブ:WAF(Webアプリケーションファイアウォール) SecRuleEngine On SecAuditLog /data/log/httpd/modsec_audit.log SecDebugLog /data/log/httpd/modsec_debug.log SecRuleEngineは、ModSecurityの動作モードを指定します。Onにすると実際に攻撃をブロックし、DetectionOnlyにすると検知だけ行いブロックはしません。運用開始直後は、誤検知が多くないか確認するためDetectionOnlyで様子を見て、問題なければOnに切り替えるという段階的な進め方がおすすめです。個別のルールを除外したい場合は、以下のように書きます。 SecRuleRemoveById 931120 SecRuleRemoveById 942360 長文投稿など、特定の入力パターンで実行時エラー(PCRE limits exceeded)が起きるルールがある場合、こうしてIDを指定して個別に無効化できます。 ■ ログ関連ディレクティブ ErrorLog /data/log/httpd/error_log CustomLog /data/log/httpd/access_log combined ErrorLogはApache自体のエラーや、アプリケーション側の例外(Djangoのトレースバックなど)が記録される場所です。CustomLogは、誰がいつどのURLにアクセスしたかというアクセスログを記録します。combinedというのは、記録するログの書式(フォーマット)の名前です。 ■ MPM(Multi-Processing Module)関連ディレクティブ <IfModule mpm_event_module> StartServers 3 MinSpareThreads 75 MaxSpareThreads 250 ThreadsPerChild 25 MaxRequestWorkers 400 MaxConnectionsPerChild 0 </IfModule> Apacheが同時にどれだけのリクエストをさばけるかを制御する設定です。StartServersは起動時に立ち上げる子プロセスの数、ThreadsPerChildは1つの子プロセスが持つスレッド数、MaxRequestWorkersは全体で同時に処理できる最大リクエスト数です。サーバーのメモリ・CPUに余裕がある場合は、これらの数値を大きめに設定することで、アクセス集中時の余裕を持たせられます。逆に、小規模なサーバーで数値を大きくしすぎると、メモリ不足でサーバー全体が不安定になることもあるため、搭載メモリとの兼ね合いで調整する必要があります。 ■ セキュリティ関連ヘッダーのディレクティブ(Django側での設定が主だが参考として) Apache単体でも、以下のようなヘッダー付与用ディレクティブがあります。 Header always set X-Frame-Options "DENY" Header always set X-Content-Type-Options "nosniff" X-Frame-Optionsは、自分のサイトが他サイトの中にiframeとして埋め込まれることを防ぐ、クリックジャッキング対策の定番設定です。今回はアプリケーション側(Django)で同等の設定をしていますが、Apache側でも二重に設定しておくと、より確実な防御になります。 ■ まとめ Apacheの設定は、最初は項目数の多さに圧倒されますが、「誰に」「何を」「どう見せるか」という3つの軸で整理すると理解しやすくなります。VirtualHostとServerNameで「誰宛のリクエストか」を決め、AliasやWSGIScriptAliasで「何を見せるか」を決め、RequireやModSecurityで「どう制御するか」を決める、という構造を意識すると、設定ファイル全体の見通しが良くなるはずです。 今回紹介したディレクティブは、実際にこのサイトの運用の中で使ってきたものばかりです。設定ファイルをいじる際は、必ずapachectl configtestで文法チェックをしてから再起動する習慣をつけておくと、設定ミスによるサービス停止を防げます。