概要
本記事は、
に改訂版を書いたため削除しました。
DoS/DDoS対策ができるモジュールをApacheに導入したときのメモです。
まずはサーバにターミナルログインするところから始めます。
sudo aptitude install libapache2-mod-evasive
このとき、postfixが依存関係でインストールされる場合があります。メール機能が使えない(AWS等で送信が制限されているなど)は、途中の設定で「何もしない」を選択します。
これは、www-dataがufwを実行する場合の処理です。権限昇格の危険性を承知した上で、慎重に作業を行ってください。
sudo cp -pi /etc/sudoers /path/to/backup/directory/sudoers.$(date +%Y%m%d)
任意のバックアップディレクトリを指定します。
sudo diff -u /path/to/backup/directory/sudoers.$(date +%Y%m%d) /etc/sudoers
差分がないことを確認します。
echo 'www-data ALL=(ALL) NOPASSWD: /usr/sbin/ufw' | sudo tee -a /etc/sudoers
sudo diff -u /path/to/backup/directory/sudoers.$(date +%Y%m%d) /etc/sudoers
以下の差分を確認します。
+www-data ALL=(ALL) NOPASSWD: /usr/sbin/ufw
sudo cp -pi /etc/apache2/mods-available/evasive.conf /path/to/backup/directory/evasive.conf.$(date +%Y%m%d)
sudo diff -u /path/to/backup/directory/evasive.conf.$(date +%Y%m%d) /etc/apache2/mods-available/evasive.conf
差分がないことを確認します。
/etc/apache2/mods-available/evasive.conf DOSHashTableSize 3097
DOSPageCount 100
DOSSiteCount 100
#かなり緩く設定して、後で狭めていった方が偽陽性を防げます。
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 10
#DOSEmailNotify you@yourdomain.com
#メール通知を行わないため、ここを省いています
DOSSystemCommand "sudo ufw deny proto tcp from %s to any port 80,443"
# 検証時に自分のサイトがブロックされるのを防ぐため、ポートは80/443に絞っています
DOSLogDir "/var/log/mod_evasive"
DOSWhitelist 127.0.0.1
DOSWhitelist xx.xx.xx.xx
# 対象外としたいIPアドレス(自分の環境など)
参考:Apache の DoS攻撃対策モジュール mod_evasive
sudo diff -u /path/to/backup/directory/evasive.conf.$(date +%Y%m%d) /etc/apache2/mods-available/evasive.conf
- #DOSHashTableSize 3097
- #DOSPageCount 2
- #DOSSiteCount 50
- #DOSPageInterval 1
- #DOSSiteInterval 1
- #DOSBlockingPeriod 10
+ DOSHashTableSize 3097
+ DOSPageCount 100
+ DOSSiteCount 100
+ DOSPageInterval 1
+ DOSSiteInterval 1
+ DOSBlockingPeriod 10
#DOSEmailNotify you@yourdomain.com
- #DOSSystemCommand "su - someuser -c '/sbin/... %s ...'"
- #DOSLogDir "/var/log/mod_evasive"
+ DOSSystemCommand "sudo ufw deny proto tcp from %s to any port 80,443"
+ DOSLogDir "/var/log/mod_evasive"
+ DOSWhitelist 127.0.0.1
+ DOSWhitelist xx.xx.xx.xx
</IfModule>
sudo apache2ctl configtest
Syntax OKを確認します。
sudo systemctl restart apache2.service
これで、不審なアクセスが大量にあったときにufwで弾く体制が整いました。
先のエントリーで述べたようにWikiとしては致命的な弱点だったため、やむなくv6.3.5に戻したという経緯があります。
ですが、回避策が見つかりましたのでメモとして残します。
How to reproduce? (再現手順)
2台のHostPCでそれぞれGrowiを立ち上げています。A環境・B環境と呼称します。
「データ移行」機能を用いて、A環境からB環境にデータをインポートする
B環境のGrowiに他のPCからアクセスする
記事の編集画面を表示するWhat happens? (症状)
記事の編集画面が白紙になっており、そのまま保存しても記事の内容が失われる(添付画面参照)
と、事象が一致。
上記issueのツリーに
私の環境でも編集画面が空白になる現象が観測されました。
私は https-portal を使ってデプロイしているのですが、 growi-docker-compose/examples/https-portal にある WEBSOCKET: 'true' の環境変数を設定し損ねていたのが原因でした。
私の環境では、これを設定すると通常通り編集を行えることを確認しております。逆に、コメントアウトすると空白に戻ります。
とあります。
これを原因と断定し、対処に臨みます。
参考にしたURL
How to Reverse Proxy Websockets with Apache 2.4
cat /etc/apache2/sites-available/growi.conf
自分の環境に合わせます。
# socket.io の path を rewrite する
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/socket.io [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule /(.*) ws://localhost:3000/ [P,L]
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
設定そのものはgithubのgrowi公式ドキュメントに沿ったものでしたが、これが引っかかっていたようです。
sudo cp -pi /etc/apache2/sites-available/growi.conf /path/to/backup/directory/growi.conf.$(date +%Y%m%d)
ファイル名は自分の環境に合わせます。適宜、任意のバックアップディレクトリを指定します。
diff -u /path/to/backup/directory/growi.conf.$(date +%Y%m%d) /etc/apache2/sites-available/growi.conf
差分が無いこと(エラーがないこと)でバックアップを確認します。
上記、バックアップを取ったファイルを教義・信仰に沿ったエディタで編集します。(要管理者権限)
# socket.io の path を rewrite する
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/socket.io [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule /(.*) ws://localhost:3000/ [P,L]
# WebSocketのための設定
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule /(.*) ws://localhost:3000/$1 [P,L]
編集後、差分を取ります。
diff -u /path/to/backup/directory/growi.conf.$(date +%Y%m%d) /etc/apache2/sites-available/growi.conf
- # socket.io の path を rewrite する
- RewriteEngine On
- RewriteCond %{REQUEST_URI} ^/socket.io [NC]
- RewriteCond %{QUERY_STRING} transport=websocket [NC]
- RewriteRule /(.*) ws://localhost:3000/ [P,L]
+ # WebSocketのための設定
+ RewriteEngine On
+ RewriteCond %{HTTP:Upgrade} =websocket [NC]
+ RewriteCond %{HTTP:Connection} upgrade [NC]
+ RewriteRule /(.*) ws://localhost:3000/$1 [P,L]
sudo apache2ctl configtest
Syntax OKを確認します。
systemctl status apache2.service
active (running)を確認します。
sudo systemctl restart apache2.service
systemctl status apache2.service
active (running)を確認します。
上記、設定を行ったGrowiサイトにアクセスします。
編集後、左ペイン(エディタ部分)がそのまま残っていれば対処完了です。
自分のサーバのアクセスログを見たら
"GET /picture.php?/6797/category/73 HTTP/1.1" 200 14394 "-" "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)"
と、クローラーが大量にアクセスしてきました。robots.txtも意に介さない悪名高いbotのようなので、このアクセスを、サーバで拒否します。
/etc/apache2配下にあります。また、バーチャルサイトによる複数のサイトを運用しているので、そのうちの1つだけを弾きます。
cd /etc/apache2/sites-available && pwd
sudo cp -pi hoge.conf /path/to/backup/directory/hoge.conf.$(date +%Y%m%d)
設定を行いたい自分の設定ファイルを、任意のバックアップディレクトリにバックアップします。
diff -u /path/to/backup/directory/hoge.conf.$(date +%Y%m%d) hoge.conf
差分が無ければ(エラーがなければ)バックアップは成功です。
上述した設定ファイルを教義・進行に則ったエディタで編集します。(要管理者権限)
DocumentRoot /var/www/html/hoge
<Directory /var/www/html/hoge>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
## GoogleBOTを拒否(正常bot不正bot両方拒否)
SetEnvIfNoCase User-Agent "Googlebot" bot
## Facebookのクローラーを拒否
SetEnvIfNoCase User-Agent "facebookexternalhit/1.1" fb_bot
<RequireAll>
Require all granted
Require not env bot
Require not env fb_bot
</RequireAll>
/var/www/html/hogeは自分の環境に合わせます。
※ついでにGoogleBOTも拒否します。
diff -u /path/to/backup/directory/hoge.conf.$(date +%Y%m%d) hoge.conf
- Require all granted
+ ## GoogleBOTを拒否(正常bot不正bot両方拒否)
+ SetEnvIfNoCase User-Agent "Googlebot" bot
+ ## Facebookのクローラーを拒否
+ SetEnvIfNoCase User-Agent "facebookexternalhit/1.1" fb_bot
+ <RequireAll>
+ Require all granted
+ Require not env bot
+ Require not env fb_bot
+ </RequireAll>
sudo apache2ctl configtest
Syntax OKを確認します。
sudo systemctl restart apache2.service
systemctl status apache2.service
active(running)を確認します。
設定を行ったアクセスログを開きます。
403 3772 "-" "facebookexternalhit/1.1
のように、ステータスコードが「403」になっていれば、アクセス拒否されています。
2024年4月に発表された脆弱性への対処を行います。
https://jvn.jp/vu/JVNVU99032532
sudo add-apt-repository ppa:ondrej/apache2
sudo aptitude update
sudo aptitude upgrade
このリストの中にapache2とapache2関連パッケージが更新される(2024/04/10現在)ため、それぞれアップグレードを行います。
apache2ctl -v
Apache/2.4.59以降であることを確認します。2.4.58には、http/2プロトコルへの脆弱性があるので、左記のバージョンであることを確認します。
2024/04/10
WordPressなどの特定のディレクトリに対して攻撃を仕掛けてくるIPアドレスやNWをブロックする方法についてメモします。
以下で動作を確認しました。
/etc/apache2/site-available/example.confなど、バーチャルサイトを利用/etc/apache2/sites-available && pwd
sudo cp -pi example.conf /path/to/backup/directory/example.conf.$(date +%Y%m%d)
バックアップするファイルやディレクトリは自分の環境に合わせます。
diff -u example.conf /path/to/backup/directory/example.conf.$(date +%Y%m%d)
バックアップがなければ(エラーがなければ)バックアップはできています。
/etc/apache2/site-available/example.conf以下のように追記します。
<Directory "/var/www/html/example">
<RequireAll>
Require all granted
Require not ip 192.168.1.1
</RequireAll>
</Directory>
拒否対象のディレクトリや、IPアドレスは対象に合わせて修正してください。
動作確認に万全を期すなら、自分が用意できるアクセス元のIPアドレスを指定します。(その後、設定を削除します)
diff -u /path/to/backup/directory/example.conf.$(date +%Y%m%d) /etc/apache2/site-available/example.conf
上記の追記内容が出ていることを確認します。
sudo apache2ctrl configtest
SyntaxOKを確認します。
sudo systemctl restart apache2.service
など、改良していきます。
ちょっとした小技が役立ったのでメモに残しておきます。
で、バーチャルサイトでサイトを検証していました。
検証で動かしているWebアプリAがaaa.hoge.comで動いていました。
そこに、同じ環境でWebアプリBを動かす需要がありました。
本来なら、DNSで
とするところ、
という背景がありました。そこで、「一度WebアプリAを無効にしつつ、WebアプリBをaaa.hoge.com」として動かすようなすり抜けを使いました。
sudo a2dissite app_a.conf
sudo systemctl restart apache2.service
WebアプリB用の設定ファイル作成
に以下のように作っていきます。
略
<VirtualHost *:443>
# ドメイン名を指定します
ServerName aaa.hoge.com
# アプリB用のログディレクトリを指定します。
CustomLog /var/log/nextcloud/nextcloud_access.log combined
ErrorLog /var/log/nextcloud/nextcloud_error.log
# アプリB用のドキュメントルートディレクトリを指定します。
# アプリAno参照ドキュメントとは違うディレクトリにします
DocumentRoot /home/www-data/nextcloud
<Directory /home/www-data/nextcloud>
Options -MultiViews
AllowOverride All
Require all granted
</Directory>
略
sudo a2ensite app_b.conf
sudo apache2ctl configtest
sudo systemctl restart apache2.service
動作を確認します。
aaa.hoge.com(など、今までアプリAが動いていたサイトのドメインで)アプリBのサイトが動くようになれば成功。
一時的な手段ではありますが、効果はありました。
ちょっとした事情で、既に稼働しているRedmineのドメインを変更しました。以下、作業メモです。
ここでは、aaa.hoge.comをbbb.hoge.comに変える手順を行います。
cd /etc/apache2/sites-available && pwd
# 自分の環境に合わせます。
sudo cp -pi aaa.hoge.com.conf bbb.hoge.com.conf
# 設定ファイルは自身の環境に合わせます。
以下の差分の通り修正します。信仰・教義に沿ったエディタで編集してください。
<VirtualHost _default_:80>
-servername aaa.hoge.com
+servername bbb.hoge.com
<VirtualHost _default_:443>
- servername aaa.hoge.com
+ servername bbb.hoge.com
-SSLCertificateFile /etc/certs/aaa.hoge.com.crt
-SSLCertificateKeyFile /etc/private/aaa.hoge.com.key
+SSLCertificateFile /etc/certs/bbb.hoge.com.crt
+SSLCertificateKeyFile /etc/private/bbb.hoge.com.key
# 証明書と秘密鍵の適切な格納場所を指定します。
# ホスト名のみ変更し、ワイルドカード証明書を利用するのであれば証明書の部分は修正する必要はありません。
sudo a2dissite aaa.hoge.com.conf
# サービス再起動を求められますが、最後にまとめて行います。
sudo a2ensite bbb.hoge.com.conf
sudo apache2ctl configtest
# Syntax OKを確認します。
sudo systemctl restart apache2.service
AWS Lightsailを用いて外部公開しているWebサイトのセキュリティを高めるため、セキュリティヘッダを更に付与しました。
また、/etc/apache2/sites-availavle配下にバーチャルサイトファイルで管理しています。
cd /etc/apache2/sites-available &&pwd
# 自環境のバーチャルサイトの格納場所に移動します
sudo cp -pi hoge.conf /path/to/directory/hoge.conf.$(date +%Y%m%d)
# バックアップ元とバックアップ先は自分の環境に合わせます。
diff -u hoge.conf /path/to/directory/hoge.conf.$(date +%Y%m%d)
# 差分がないことでバックアップが取れていることを確認します。
教義・進行に沿ったエディタを用いて、以下の差分になるようにセキュリティヘッダをコンフィグに付与します。
+ Header set X-Content-Type-Options "nosniff"
+ Header always append X-Frame-Options "SAMEORIGIN"
+ Header set X-XSS-Protection "1; mode=block"
以下はChantGPTによる解説です。
このヘッダーは、ブラウザがレスポンスのContent-Typeヘッダーと実際のコンテンツの種類が一致しない場合に、ブラウザが自動的にコンテンツのタイプを推測するのを防止します。これにより、悪意のあるコンテンツが実行されるリスクを低減することができます。
このヘッダーは、クリックジャッキング攻撃を防止するために使用されます。"DENY" を指定すると、ページがフレーム内で表示されることが完全に禁止されます。"SAMEORIGIN" を指定すると、同じオリジン(ドメインとプロトコルが一致)のフレーム内でのみページが表示されます。
このヘッダーは、クロスサイトスクリプティング(XSS)攻撃からの保護を目的としています。ブラウザによって検出されたXSS攻撃が検出された場合、ブラウザはページをブロックするように指示されます。
sudo apache2ctl configtest
#Syntax OKを確認します
sudo systemctl restart apache2.service
systemctl status apache2.service
#Active(running)を確認します
ヘッダーが正しく設定されていれば、それぞれのヘッダーの値が表示されます。
以下のスクリプトを修正しました。
そうして、「一度でもMod_Securityが疑わしいと検知すれば、次回以降のアクセスを許さない」という、言わば“ONE OUTS”システムを採用しています。
この可読性を高めました。
# Mod Security
SecRuleEngine On
## ModSecurity有効化
SecRequestBodyInMemoryLimit 524288000
SecRequestBodyLimit 524288000
## ファイルのアップロードをできるようにします。
# ネガティブリスト
SecRule REMOTE_ADDR "@pmFromFile negativelist.txt" "phase:1,id:2,deny,msg:'Negativelisted IP address'"
※ 教義・信仰に沿ったエディタで記載します。
#!/bin/bash
# このシェルスクリプトは、変数で定義したエラーログからIPアドレスを抽出し、
# suspicious_ipディレクトリに保存し、その後、特定のIPアドレスを削除して
# /etc/apache2/sites-available/negativelist.txtに書き込むものです。
# 読み込むログのディレクトリとファイル名を変数指定
log_dir="/var/lib/redmine/log"
log_file="error.log"
# 除外するIPアドレスをファイルで指定
exclude_ips_file="/path/to/exclude_ips.txt"
# IPアドレスを抽出して重複を排除し、ファイルに保存
cd "$log_dir"
awk 'match($0,/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/) { print substr($0, RSTART, RLENGTH) }' "$log_file" | sort | uniq > "$log_dir/suspicious_ip/suspicious_ip.$(date +%Y%m%d)"
chown www-data:www-data "$log_dir/suspicious_ip/suspicious_ip.$(date +%Y%m%d)"
# 過去のIPアドレスを読み込んで重複を排除し、ファイルに保存
cat "$log_dir/suspicious_ip/suspicious_ip."2* | sort | uniq > "$log_dir/suspicious_ip_all.txt"
chown www-data:www-data "$log_dir/suspicious_ip_all.txt"
# 新たにリストに書き起こす
cat "$log_dir/suspicious_ip_all.txt" > /etc/apache2/sites-available/negativelist.txt
# 除外するIPアドレスをファイルから削除
while IFS= read -r exclude_ip; do
sed -i "/$exclude_ip/d" /etc/apache2/sites-available/negativelist.txt
done < "$exclude_ips_file"
# Apacheを再起動
systemctl restart apache2.service
※ 教義・信仰に沿ったエディタで作成します。
192.168.0.1
172.28.0.1
# 一行ずつ記載
sudo chown root:root negativelist.sh
sudo chmod 744 negativelist.sh
sudo crontab -e -u root
0 6 * * * /home/manualmaton/bin/negativelist.sh
これによって、スクリプトを変数化。他のサーバへの転用を行いやすくしました。
Powered by WordPress & Theme by Anders Norén