ユーニックス総合研究所

  • home
  • archives
  • nginx-ddos

NginxでDDoS攻撃に対処する: ある鯖管のメモ

  • 作成日: 2021-12-03
  • 更新日: 2023-12-24
  • カテゴリ: Nginx

DDoS攻撃の兆候

筆者は複数のWebサービスを運営している個人開発者だ。
最近はもっぱらブログを書くことを本業としているが、アイデアが湧いてくるとサービスを作ってリリースしている。

Webサービスのアクセス数の解析にはGoogleアナリティクスを使っている。
アナリティクスは複数のサービスを同じ画面で管理できるので中々便利だ。
最近はリリースしたサービスのアクセス数をこのアナリティクスで眺めるのが日課になっている。

ある日、とあるサービスのアクセス履歴を見ていた。
するとある特定の日だけ抜きんでてアクセス数が多いのが見て取れた。
どこかのメディアに紹介でもされたのだろうか?
私はニヤニヤと笑みを浮かべた。

だが、その考えは脳裏にはあったが、私の頭の中の大部分を占める考えはそれとは違っていた。

*> これは攻撃だ

私はそう直感した。

このような周辺のPVから抜きんでてPVが伸びているケースでまず考えられるのは、高負荷攻撃の可能性だ。
つまり、サービスに短期間で大量にアクセスしてサーバーに負荷をかけて、サーバーをダウンさせるという目的を持った攻撃のことである。
これはDoS攻撃DDoS攻撃と呼ばれる。

私はさっそく使い古したお気に入りの端末を立ち上げ、問題のサーバーにログインした。
ログを調べるためである。

ログの調査

問題のアクセスが攻撃だったかどうかは、ログを見ればわかる。
今回のサーバーではNginxというWebサーバーを使っていた。
Nginxのサーバーのログは/var/log/nginx/以下に保存される。
ここに保存されているaccess.logerror.logがNginxが記録しているログファイルだ。

私はlessコマンドでaccess.logを開いてみた。
しかし目視による確認では限界がある。私は2秒でlessを閉じた。

私はログインユーザーのホームディレクトリに↓のスクリプトを作った。

#!/usr/local/bin/python3  
import sys  

if len(sys.argv) < 2:  
    sys.exit(0)  

path = sys.argv[1]  

with open(path, 'r') as fin:  
    lines = fin.readlines()  

s = {}  

for line in lines:  
    toks = line.split(' ')  
    ip = toks[0]  
    if ip in s.keys():  
        s[ip] += 1  
    else:  
        s[ip] = 0  

items = sorted(s.items(), key=lambda el: el[1])  

for k, v in items:  
    print(f'{v}: {k}')  

このスクリプトは、access.logを解析してIPアドレスごとのアクセス数の統計を取るスクリプトだ。
↓のようにして使う。

$ python3 countip.py /var/log/nginx/access.log  
100: 123.223.323.423  
200: 223.223.323.423  
400: 323.223.323.423  

このスクリプトを使えば、どのIPアドレスから大量のアクセスが短期間で来ているかがわかる。
測ってみると特定のIPから短期間に600以上のアクセスがあることがわかった。
私は「ビンゴ」と舌なめずりをした。
両腕を頭上に上げて手を組み、ぽきぽきと首の骨を鳴らす。
私はそれが終わると次にその大量アクセスをしているIPの情報を調べることにした。

IPの登録情報はwhoisコマンドで調べればわかる。
whoisで問題のIPを調べるとそれぞれのIPは香港とドイツのものだった。
whoisで公開されているドメインにアクセスすると、ドイツはVPSを貸し出しているレンタルサーバーであることがわかった。
これはDDoS攻撃だろう。私の予想は当たっていた。
何を目的にしているのか知らないが、誰かがドイツや香港のレンタルサーバーを使って大量のアクセスを私のサービスに仕掛けたわけだ。

目的はなんだろう?
ホビーか?クラッキングの練習か?
どちらにせよ迷惑なユーザーであることは間違いない。

対処

さて、どう対処しようか。
Webサーバーレベルで対処するか、あるいはアプリケーションレベルで対処するか私は迷った。
実は依然もDDoSの対処はしたことがあって、その時はアプリケーションレベルの対処をしていた。
今回はWebサーバーレベルの対処をしてみることにした。

今回のサービスのWebサーバーはNginxを使っている。
私は↓の記事を参考にIPアドレス制限を行うことにした。
いまさらNginxでDoS攻撃対策してみる(3つの対応してみた) - Qiita

Nginxの設定ファイルに以下を書く。

geo $access_filter {  
    default OK;  
    include /etc/nginx/conf.d/ip_deny_list.txt;  
}  

server {  

    ...  

    # NGだったら500を返す  
    if ($access_filter = "NG") {  
        return 500;  
    }  
}  

この設定は/etc/nginx/conf.d/ip_deny_list.txtに列挙されているIPアドレスに対して、500エラーを返す設定だ。
ip_deny_list.txtには↓のようにブロックするIPを列挙する。

123.223.323.456 NG;  
223.223.323.456 NG;  

テスト用にこのリストに自分のIPも入れておく。
service nginx restartでNginxを再起動。
ブラウザでサービスにアクセスすると無事に500エラーになった。これでフィルタリングは成功。
私は自分のIPをリストから除いて再度Nginxを再起動した。

これで高負荷攻撃をしてくるIPアドレスをNginxレベルで遮断できた。

このブラックリストによるアクセス制御の欠点は、自動化できない点だ。
つまり、高負荷のアクセスを検知して、自動でIPアドレスを弾くという芸当はできない。
そういう芸当をやりたい場合は、Webサーバーレベルではなくてアプリケーションレベルでやる必要があるだろう。
フレームワークとデータベースを使えばそういった自動化は大して難しくないはずだ。

おわりに

しかし、Webサーバーのレイヤーで遮断しているわけだから、Webサーバーのリソースは結局使ってることになるんだよなぁ。
「そう考えるとかなしいな……」
私はお茶を一口飲み、はぁと溜息をついた。
季節は秋だ。DDoS攻撃の対処も慣れれば風流なものだ。

私は使い古した端末を閉じると、ネットサーフィンを再開した。
ブログのネタはいくらあっても困るものではない。ポチポチ……。