🚨

gem rack-attackで不正アクセス対策を実装する(初学者向け)

に公開

導入の背景

ドメインを取得した際、通常とは異なるアクセスパターン(大量リクエスト、ブルートフォース攻撃など)が発生しました。幸い、Railsの標準セキュリティ機能により情報漏洩は確認されませんでしたが、今後を考えて追加の対策としてgem rack-attackを導入しました。

gem rack-attackとは?

リクエストの内容や送信元に応じて、不正なアクセスを検知し、Railsアプリへのアクセスを許可・ブロック・制限できるgemです。

https://github.com/rack/rack-attack?tab=readme-ov-file

環境

Rails 7.2.1
ruby 3.3.6
Docker

今回の実装の目標

  • ブルートフォース攻撃(ログイン試行)の抑止
  • 特定エンドポイントへの過剰リクエスト制御

作業の流れ

  • gemの追加
  • Rack::Attackミドルウェアの有効化
  • initializersの設定

gemの追加

# Gemfile
gem "rack-attack"
# ターミナル
$ docker compose exec web bundle install

インストールが終了したら、サーバーを再起動します。

# ターミナル
$ docker compose restart

Rack::Attackミドルウェアの有効化

gem rack-attackをインストールすると、Rack::Attackという「アクセスをチェックするフィルター(ミドルウェア)」が使えるようになります。

# config/environments/production.rb
Rails.application.configure do
  config.middleware.use Rack::Attack
end

Rack::Attackを有効化したい環境ファイルにconfig.middleware.use Rack::Attackの記載をします。今回は本番環境のみで読み込んでいます。

※ 開発やテスト環境でも使いたい場合は、config/application.rbmodule内に記載すれば、全環境で読み込まれます。

initializersの設定

# ターミナル
$ touch config/initializers/rack_attack.rb

具体的な制限ルールを記載するためのファイルを作成します。

下記の公式推奨コードを、作成したrack_attack.rbファイルにコピペすることで、基本的な制限ルールを簡単に追加できます。
また、これをベースに自分のアプリに合わせたオリジナルの制限ルールを作成することも可能です。

https://github.com/rack/rack-attack/blob/main/docs/example_configuration.md

ここからは、公式の推奨コードをパートごとに分けて、
それぞれどんな攻撃への対策なのか、どのように動作するのかを解説していきます。

1. 特定IPアドレスからの過剰なリクエストの制限

# config/initializers/rack_attack.rb
class Rack::Attack
  throttle('req/ip', limit: 300, period: 5.minutes) do |req|
    req.ip # unless req.path.start_with?('/assets')
  end
end

🚨どんな攻撃への対策か?

特定のIPアドレスから短時間に大量のリクエストが送られるケースでは、不正アクセスや脆弱性スキャンを試みている可能性があります。
このようなアクセスを早い段階で検知・制限することで、サーバーへの負荷を抑え、攻撃の足がかりを未然に防ぐことができます。

👮‍♂️どのように対処しているか?

5分間で同じIPアドレスから300リクエストあった場合、制限対象とみなします。
301回目リクエストがあった時に、対象のユーザーへHTTP 429 Too Many Requestsを返します。

# DOSed the site. Rack::Attack returns 429 for throttling by default

※ レスポンスのカスタマイズも可能ですが、Customizing responsesにも記載されている通り、通常はHTTP 429 Too Many Requests を返します。

なお、カウントされるリクエスト例は下記となります。

# すべてのリクエストがカウント対象
GET /users
POST /login
GET /about
GET /api/data

なお、下記のコメントアウトを外した場合、CSSやJavaScriptのアセットパイプラインへのアクセスはカウントされません。

# unless req.path.start_with?('/assets')
GET /users              # カウントされる
GET /assets/app.css     # カウントされない
GET /assets/app.js      # カウントされない
GET /assets/logo.png    # カウントされない

2. ブルートフォース対策 -パスワード編-

# config/initializers/rack_attack.rb
class Rack::Attack
  throttle('logins/ip', limit: 5, period: 20.seconds) do |req|
    if req.path == '/login' && req.post?
      req.ip
    end
  end
end

🚨どんな攻撃への対策か?

ログイン画面に対する典型的なブルートフォース攻撃(総当たり攻撃)を防ぐ対策です。
特定のIPアドレスから短時間に何度もパスワードを試されると、認証突破のリスクが高まります。
このルールでは一定回数以上のログイン試行があった場合にアクセスを制限することで、攻撃の成功率を大幅に下げることができます。
また、分散攻撃(複数のIPを利用して総当たりを行う攻撃)への対策にも有効です。

👮‍♂️どのように対処しているか?

特定のIPアドレスで、20秒のうちに5回以上アクセスした場合、そのIPアドレスでのアクセスに対してHTTP 429 Too Many Requestsを返します。


3. ブルートフォース対策 -メールアドレス編-

# config/initializers/rack_attack.rb
class Rack::Attack
  throttle('logins/email', limit: 5, period: 20.seconds) do |req|
    if req.path == '/login' && req.post?
      # Normalize the email, using the same logic as your authentication process, to
      # protect against rate limit bypasses. Return the normalized email if present, nil otherwise.
      req.params['email'].to_s.downcase.gsub(/\s+/, "").presence
    end
  end
end

🚨どんな攻撃への対策か?

悪意のあるユーザーが特定のメールアドレスを使って、意図的になりすましや制限をかけるような攻撃をしてくる場合があります。
これは、特定のアカウントに負荷をかけ、ログイン制限を引き起こす「サービス妨害攻撃(DoS)」の一種です。

👮‍♂️どのように対処しているか?

特定のメールアドレスで、20秒のうちに5回以上ログインを試みた場合、そのメールアドレスでのログイン試行に対してHTTP 429 Too Many Requestsを返します。

https://e-words.jp/w/総当たり攻撃.html
参考:IT用語辞典


終わりに

Railsには標準で多くのセキュリティ対策が備わっていますが、gem rack-attackのような追加の対策を組み合わせることで、より堅牢なアプリケーションを構築できます。
初心者でも導入しやすいgemなので、セキュリティ対策の第一歩としておすすめです。

Discussion