Rails6のHostのホワイトリスト機能の使い方

こんにちは!kossyです!




さて、今回はRails6のHostのホワイトリスト機能の使い方について、ブログに残してみたいと思います。



環境

Ruby 2.6.6
Rails 6.0.3.5
Docker for Mac



Hostのホワイトリスト機能とは?

Hostのホワイトリスト機能が入ったPR

github.com

概要欄の英語を翻訳してみます。

・Introduce guard against DNS rebinding attacks

DNS再バインド攻撃に対するガードを導入します

The ActionDispatch::HostAuthorization is a new middleware that prevent
against DNS rebinding and other Host header attacks.

ActionDispatch :: HostAuthorizationは、これを防ぐ新しいミドルウェアです。 DNSの再バインドやその他のホストヘッダー攻撃に対して。

In other environments Rails.application.config.hosts is empty and no
Host header checks will be done. If you want to guard against header
attacks on production,

他の環境では、Rails.application.config.hostsは空であり、 ホストヘッダーのチェックが行われます。

The host of a request is checked against the hosts entries with the case operator (#===), which lets hosts support entries of type RegExp, Proc and IPAddr to name a few.

リクエストのホストは、ケース付きのホストエントリに対してチェックされます 演算子(#===)。これにより、ホストは正規表現タイプのエントリをサポートできます。 いくつか例を挙げると、ProcとIPAddrです。

A special case is supported that allows you to whitelist all sub-domains:

すべてのサブドメインホワイトリストに登録できる特別なケースがサポートされています。

開発環境の場合は、デフォルトで以下のような設定がされるみたいです。

 Rails.application.config.hosts = [
   IPAddr.new("0.0.0.0/0"), # All IPv4 addresses.
   IPAddr.new("::/0"),      # All IPv6 addresses.
   "localhost"              # The localhost reserved domain.
 ]

コンソールで設定の確認をしてみます。

$ Rails.env
=> "development"

$ Rails.application.config.hosts
=> [".localhost",
 #<IPAddr: IPv4:0.0.0.0/0.0.0.0>,
 #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0000/0000:0000:0000:0000:0000:0000:0000:0000>
]

きちんと設定されていることがわかります。

本番環境の場合はhostsは空になっているので、自身で設定する必要があります。

# config/environments/production.rb

config.hosts << "example.com"

DNS rebinding attackについて

wikipedia先生に頼ります。
en.wikipedia.org

DNS再バインドは、コンピュータ攻撃の一形態として一般的に使用されるドメイン名の解決を操作する方法です。この攻撃では、悪意のあるWebページにより、訪問者はネットワーク上の他の場所のマシンを攻撃するクライアント側のスクリプトを実行します。理論的には、同一生成元ポリシーはこれが発生しないようにします。クライアント側のスクリプトは、スクリプトを提供したのと同じホスト上のコンテンツにのみアクセスできます。ドメイン名の比較は、このポリシーを適用する上で不可欠な部分であるため、DNSの再バインドは、ドメインネームシステム(DNS)を悪用することにより、この保護を回避します。

この攻撃は、被害者のWebブラウザにプライベートIPアドレスのコンピュータにアクセスさせ、その結果を攻撃者に返すことにより、プライベートネットワークを侵害するために使用される可能性があります。また、スパム、分散型サービス拒否攻撃、またはその他の悪意のあるアクティビティに被害者のマシンを使用するために使用することもできます。

攻撃者はドメイン(attacker.comなど)を登録し、攻撃者の制御下にあるDNSサーバーに委任します。サーバーは、非常に短い存続時間(TTL)レコードで応答するように構成されているため、DNS応答がキャッシュされません。被害者が悪意のあるドメインを参照すると、攻撃者のDNSサーバーは、最初に悪意のあるクライアント側のコードをホストしているサーバーのIPアドレスで応答します。たとえば、被害者のブラウザを、被害者のコンピュータで実行することを目的とした悪意のあるJavaScriptまたはFlashスクリプトを含むWebサイトに向けることができます。

悪意のあるクライアント側のコードは、元のドメイン名(attacker.comなど)に追加のアクセスを行います。これらは同一生成元ポリシーで許可されています。ただし、被害者のブラウザがスクリプトを実行すると、ドメインに対して新しいDNS要求が行われ、攻撃者は新しいIPアドレスで応答します。たとえば、インターネット上の別の場所にあるターゲットの内部IPアドレスまたはIPアドレスで応答できます。

シスコのドキュメントによると、DNSのフィルタリングを行うことで、攻撃から保護できるとのことでした。
umbrella.cisco.com

Rails6のHostのホワイトリスト機能の場合、偽造されたHostが指定された場合でも、許可されたホストの通信のみ許可する設定にしているため、
DNS rebinding Attackを防げるということかと思います。

Hostを偽装してアクセスしてみる

curlを使ってHostを偽装して開発環境に対してリクエストをしてみます。

$ curl -v -H 'Host:yhaoo.cp.jp' http://localhost:3000/

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host:yahoo.co.jp
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< Content-Type: text/html; charset=UTF-8
< Content-Length: 3108
<

# 省略

* Connection #0 to host localhost left intact
* Closing connection 0

403が返ってきました。省略した部分はHTMLで、添付画像のような内容でした。
f:id:kossy-web-engineer:20210227134550p:plain

表示されている通り、Hostの指定をして再度リクエストを試みてみます。

$ curl -v -H 'Host:yahoo.co.jp' http://localhost:3000/
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host:yahoo.co.jp
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Download-Options: noopen
< X-Permitted-Cross-Domain-Policies: none
< Referrer-Policy: strict-origin-when-cross-origin
< Content-Type: text/html; charset=utf-8
< ETag: W/"8a65edeb7c0d064821d16acc080a28c2"
< Cache-Control: max-age=0, private, must-revalidate
< X-Request-Id: 62f6b96e-407b-4f65-bbd6-9158f8c0fc33
< X-Runtime: 0.478029
< Transfer-Encoding: chunked

HTTPステータス200が返ってきました。

Hostの偽装が可能なこととHostの指定によって異なるホストへの通信が許可されることがわかりました。




勉強になりました。



大いに参考にさせていただいたサイト

この場を借りて御礼を申し上げます。

Guard against DNS rebinding attacks by permitting hosts by gsamokovarov · Pull Request #33145 · rails/rails · GitHub
DNS rebinding - Wikipedia