PlayFramework でアクセスできるhostを制限する

概要

PlayFrameworkにはアクセスできるhostを制限できるFilterがあり、これはapplication.confファイル下記を設定することで、実現できる。

play.filters.hosts {
  allowed = [".example.com", "localhost:9000"]
}

このフィルターはホワイトリスト形式で、example.com と、そのサブドメイン、それとlocalhost:9000からのリクエストを許可している。※サブドメインを含む許可は本番環境では非推奨

サブドメインの許可を表現しているのは . (ドット)の部分で、これがワイルドカードのような役割を果たしてサブドメインを全て許可にしている。. をつけず、example.com のみにすれば、サブドメインは許可されない。

実際にやってみる

下記のfilterが設定されたPlayのアプリケーションを実行し、APIに問い合わせしてみる。

play.filters.hosts {
  allowed = ["localhost:9000"]
}

これは、localhost:9000 のみを許可している。Playのアプリケーションはlocalhost:9000で待ち受けているので、このリクエストは当然通る。

localhost:9000gが許可されているときのレスポンスkここrこ

これを下記のように少し書き換えて再度実行してみる。

play.filters.hosts {
  allowed = ["localhost:9999"]
}

port番号を9000番から9999に変えてみた。すると、BadRequestとなり、リクエストが弾かれていることが分かる。

localhost:9999 のみを許可したとき、localhost:9000は許可されていないので、、BadRequestになる

テスト

ドメインの許可設定が正常に稼働しているかを確認するテストを書いてみる。リクエストのテストになるので、とりあえずここではControlerのテストとして実装したサンプルを示す。

host として、example.com のみを許可しているという想定。

利用しているのは scalatestplus-play version 5.0.0。

package coc.interfaces.controllers

import org.scalatestplus.play.PlaySpec
import org.scalatestplus.play.guice.GuiceOneAppPerTest
import play.api.http.HeaderNames
import play.api.http.Status.{BAD_REQUEST, OK}
import play.api.mvc.AnyContentAsEmpty
import play.api.test.Helpers.{GET, defaultAwaitTimeout, route, status, writeableOf_AnyContentAsEmpty}
import play.api.test.{FakeHeaders, FakeRequest}

class CocControllerTest extends PlaySpec with GuiceOneAppPerTest {
  "CocControllerTest" should {
    "hostが example.com の時、Okを返す" in {
      val request = FakeRequest(
        method = GET,
        uri = "/coc/abilities",
        headers = FakeHeaders(Seq(HeaderNames.HOST -> "example.com")),
        body = AnyContentAsEmpty
      )
      val result = route(app, request).get
      status(result) mustBe OK
    }

    "hostが example.jp の時 BadRequestを返す" in {
      val request = FakeRequest(
        method = GET,
        uri = "/coc/abilities",
        headers = FakeHeaders(Seq(HeaderNames.HOST -> "example.jp")),
        body = AnyContentAsEmpty
      )
      val result = route(app, request).get
      status(result) mustBe BAD_REQUEST
    }
  }
}
FakeRequestheaders のところを変えて、hostによる制御ができているかの確認をしている。

公式ドキュメント