読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

Playframework 2.5 で足りてなさそうなセキュリティヘッダ設定を追加する

Playframework 勉強 日記 Scala

セキュリティヘッダなんて、ヘッダに組み込むだけのものなので、ザクッと。

package filters

import javax.inject.{Inject, Singleton}

import akka.stream.Materializer
import play.api.Configuration
import play.api.mvc.{Filter, RequestHeader, Result}

import scala.concurrent.{ExecutionContext, Future}

/**
 * Add custom security header responds.
 * Created by azalea on 2016/08/29.
 */
@Singleton
class SecureHeaderFilter @Inject() (
  implicit override val mat: Materializer,
  exec: ExecutionContext,
  conf: Configuration
) extends Filter {

  private def prefix(k: String) = s"play.filters.$k"

  val XFrameOptions = conf.getString(prefix("frameOptions"))
  val xssProtection = conf.getString(prefix("xssProtection"))
  val contentTypeOptions = conf.getString(prefix("contentTypeOptions"))
  val permittedCrossDomainPolicies = conf.getString(prefix("permittedCrossDomainPolicies"))
  val contentSecurityPolicy = conf.getString(prefix("contentSecurityPolicy"))
  val strictTransport = conf.getString(prefix("strictTransport"))
  val downloadOptions = conf.getString(prefix("downloadOptions"))
  val cacheControl = conf.getString(prefix("cacheControl"))
  val pragma = conf.getString(prefix("pragma"))
  val expires = conf.getString(prefix("expires"))

  val headers = Seq(
    XFrameOptions.map(v => "X-Frame-Options" -> v),
    xssProtection.map(v => "X-XSS-Protection" -> v),
    contentTypeOptions.map(v => "X-XSS-Protection" -> v),
    contentSecurityPolicy.map(v => "Content-Security-Policy" -> v),
    permittedCrossDomainPolicies.map(v => "X-Permitted-Cross-Domain-Policies" -> v),
    strictTransport.map(v => "Strict-Transport-Security" -> v),
    downloadOptions.map(v => "X-Download-Options" -> v),
    cacheControl.map(v => "Cache-Control" -> v),
    pragma.map(v => "pragma" -> v),
    expires.map(v => "expires" -> v)
  ).flatten

  /**
   * add secure headers.
   *
   * @param nextFilter next filter function.
   * @param rh         request header.
   * @return           header annotated result.
   */
  override def apply(nextFilter: (RequestHeader) => Future[Result])(rh: RequestHeader): Future[Result] = {
    nextFilter(rh).map(res => res.withHeaders(headers:_*))
  }
}

設定ファイルはこんな感じ。もともとあった Play のセキュリティ設定に追記する形。

play.filters {
  headers {
    # The X-Frame-Options header. If null, the header is not set.
    frameOptions = "SAMEORIGIN"

    # The X-XSS-Protection header. If null, the header is not set.
    xssProtection = "1; mode=block"

    # The X-Content-Type-Options header. If null, the header is not set.
    contentTypeOptions = "nosniff"

    # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set.
    permittedCrossDomainPolicies = "master-only"

    # The Content-Security-Policy header. If null, the header is not set.
    contentSecurityPolicy = "default-src 'self'"

    # My custom security headers.
    # The Strict-Transport-Security header.
    #strictTransport = "max-age=31536000; includeSubDomains"

    # The X-Download-Options header.
    #downloadOptions = "noopen"

    # The Cache-Control header.
    cacheControl = "no-cache, no-store, must-revalidate"

    # The pragma header. for old cache server.
    pragma = "no-cache"

    # The expires header.
    expires = "-1"
  }

上から順に説明すると、

  • X-Frame-Options
    画面内フレームコンテンツの制限。 DENY:フレームの全面禁止、SAMEORIGIN:同じドメインコンテンツは許可、ALLOW-FROM origin_uri 指定URLのコンテンツはフレーム表示許可
    意図しない、詐欺サイト表示対策など。
  • X-XSS-Protection
    ブラウザの XSS 保護機能を有効化する。0: で保護を無効化。1で有効化
  • X-Content-Type-Options
    サーバが返してきたヘッダに従ってファイルを処理する。
    特に IE なんかだと、ファイルの中身を確認して勝手に対応アプリを開く悪癖があって、拡張子を js にした JavaScript なんかを仕込む攻撃の対策
  • X-Permitted-Cross-Domain-Policies
    crossdomain.xml の置き換え。JavaScript で複数のサーバと通信する場合の、許可証設定。
  • Content-Security-Policy
    HTML,JavaScript,音声,画像などなど、アクセスしていいドメインの制限。 default-src 'self' は同一ドメイン以外からの一切のデータ取得禁止。
  • Strict-Transport-Security
    このURLは HTTPS でアクセスセーよというお達し。中身はお察し
  • X-Download-Options
    ダウンロードしたファイルは、HTML 内での img タグとかを除いて、勝手に開くんじゃねーよ指定。
    アップロード・ダウンロードをするシステムでは有用
  • Cache-Control
    プロクシとか、キャッシュサーバとかに「キャッシュしてんじゃねーよ」と通知。
    個人情報入りのページをキャッシュされたら個人情報漏洩になるでしょ〜が!
    no-cache: キャッシュすんじゃねーぞ。有効性確認しなけりゃキャッシュ使うんじゃねーぞ
    no-store: リクエスト・レスポンスの一部分をローカルストレージに保存するんじゃない must-revalidate: 毎回サーバに確認しろ
  • pragma
    Cache-Control の no-cache と同様。古いキャッシュサーバ向け。
  • expires
    キャッシュコンテンツの有効期限。-1 では常に「そのキャッシュは無効」