alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Play Framework/Scala example source code file (SecurityHeadersFilter.scala)

This example Play Framework source code file (SecurityHeadersFilter.scala) is included in my "Source Code Warehouse" project. The intent of this project is to help you more easily find Play Framework (and Scala) source code examples by using tags.

All credit for the original source code belongs to Play Framework; I'm just trying to make examples easier to find. (For my Scala work, see my Scala examples and tutorials.)

Play Framework tags/keywords

api, concurrent, configuration, content_security_policy_config_path, defaultsecurityheadersconfig, filter, future, mvc, option, play, play framework, requestheader, securityheadersconfig, securityheadersfilter, seq, string

The SecurityHeadersFilter.scala Play Framework example source code

/*
 * Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
 */
package play.filters.headers

import play.api.mvc._
import scala.concurrent.Future
import play.api.Configuration

/**
 * This class sets a number of common security headers on the HTTP request.
 *
 * NOTE: Because these are security headers, they are "secure by default."  If the filter is applied, but these
 * fields are NOT defined in Configuration, the defaults on the filter are NOT omitted, but are instead
 * set to the strictest possible value.
 *
 * <ul>
 * <li>{{play.filters.headers.frameOptions}} - sets frameOptions.  Some("DENY") by default.
 * <li>{{play.filters.headers.xssProtection}} - sets xssProtection.  Some("1; mode=block") by default.
 * <li>{{play.filters.headers.contentTypeOptions}} - sets contentTypeOptions. Some("nosniff") by default.
 * <li>{{play.filters.headers.permittedCrossDomainPolicies}} - sets permittedCrossDomainPolicies. Some("master-only") by default.
 * <li>{{play.filters.headers.contentSecurityPolicy}} - sets contentSecurityPolicy. Some("default-src 'self'") by default.
 * </ul>
 *
 * @see <a href="https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options">X-Frame-Options</a>
 * @see <a href="http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx">X-Content-Type-Options</a>
 * @see <a href="http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx">X-XSS-Protection</a>
 * @see <a href="http://www.html5rocks.com/en/tutorials/security/content-security-policy/">Content-Security-Policy</a>
 * @see <a href="http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html">Cross Domain Policy File Specification</a>
 */
object SecurityHeadersFilter {
  val X_FRAME_OPTIONS_HEADER = "X-Frame-Options"
  val X_XSS_PROTECTION_HEADER = "X-XSS-Protection"
  val X_CONTENT_TYPE_OPTIONS_HEADER = "X-Content-Type-Options"
  val X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER = "X-Permitted-Cross-Domain-Policies"
  val CONTENT_SECURITY_POLICY_HEADER = "Content-Security-Policy"

  val DEFAULT_FRAME_OPTIONS = "DENY"
  val DEFAULT_XSS_PROTECTION = "1; mode=block"
  val DEFAULT_CONTENT_TYPE_OPTIONS = "nosniff"
  val DEFAULT_PERMITTED_CROSS_DOMAIN_POLICIES = "master-only"
  val DEFAULT_CONTENT_SECURITY_POLICY = "default-src 'self'"

  /**
   * Convenience method for creating a SecurityHeadersFilter that reads settings from application.conf.  Generally speaking,
   * you'll want to use this or the apply(SecurityHeadersConfig) method.
   *
   * @return a configured SecurityHeadersFilter.
   */
  def apply(): SecurityHeadersFilter = {
    new SecurityHeadersFilter()
  }

  /**
   * Convenience method for creating a filter using play.api.Configuration.  Good for testing.
   *
   * @param config a configuration object that may contain string settings.
   * @return a configured SecurityHeadersFilter.
   */
  def apply(config: Configuration): SecurityHeadersFilter = {
    apply(new SecurityHeadersParser().parse(config))
  }

  /**
   * Convenience method for creating a filter using SecurityHeadersConfig case class.  Use this if you have settings
   * that you want to specifically turn off by setting to None.
   *
   * @param securityHeaderConfig
   * @return
   */
  def apply(securityHeaderConfig: => SecurityHeadersConfig): SecurityHeadersFilter = {
    new SecurityHeadersFilter(securityHeaderConfig)
  }
}

/**
 * SecurityHeaders trait.  The default case class doesn't use it, but if you create a class which may return
 * different values based off the header and the result, this is where to start.
 */
trait SecurityHeadersConfig {
  def frameOptions: Option[String]

  def xssProtection: Option[String]

  def contentTypeOptions: Option[String]

  def permittedCrossDomainPolicies: Option[String]

  def contentSecurityPolicy: Option[String]
}

/**
 * A type safe configuration object for setting security headers.
 *
 * @param frameOptions "X-Frame-Options":
 * @param xssProtection "X-XSS-Protection":
 * @param contentTypeOptions "X-Content-Type-Options"
 * @param permittedCrossDomainPolicies "X-Permitted-Cross-Domain-Policies".
 * @param contentSecurityPolicy "Content-Security-Policy"
 */
case class DefaultSecurityHeadersConfig(frameOptions: Option[String],
  xssProtection: Option[String],
  contentTypeOptions: Option[String],
  permittedCrossDomainPolicies: Option[String],
  contentSecurityPolicy: Option[String]) extends SecurityHeadersConfig

/**
 * Parses out a SecurityHeadersConfig from play.api.Configuration (usually this means application.conf).
 */
class SecurityHeadersParser {

  val FRAME_OPTIONS_CONFIG_PATH: String = "play.filters.headers.frameOptions"
  val XSS_PROTECTION_CONFIG_PATH: String = "play.filters.headers.xssProtection"
  val CONTENT_TYPE_OPTIONS_CONFIG_PATH: String = "play.filters.headers.contentTypeOptions"
  val PERMITTED_CROSS_DOMAIN_POLICIES_CONFIG_PATH: String = "play.filters.headers.permittedCrossDomainPolicies"
  val CONTENT_SECURITY_POLICY_CONFIG_PATH: String = "play.filters.headers.contentSecurityPolicy"

  def parse(config: Configuration): SecurityHeadersConfig = {
    import SecurityHeadersFilter._

    val frameOptions: String = config.getString(FRAME_OPTIONS_CONFIG_PATH).getOrElse(DEFAULT_FRAME_OPTIONS)
    val xssProtection: String = config.getString(XSS_PROTECTION_CONFIG_PATH).getOrElse(DEFAULT_XSS_PROTECTION)
    val contentTypeOptions: String = config.getString(CONTENT_TYPE_OPTIONS_CONFIG_PATH).getOrElse(DEFAULT_CONTENT_TYPE_OPTIONS)
    val permittedCrossDomainPolicies: String = config.getString(PERMITTED_CROSS_DOMAIN_POLICIES_CONFIG_PATH).getOrElse(DEFAULT_PERMITTED_CROSS_DOMAIN_POLICIES)
    val contentSecurityPolicy: String = config.getString(CONTENT_SECURITY_POLICY_CONFIG_PATH).getOrElse(DEFAULT_CONTENT_SECURITY_POLICY)

    DefaultSecurityHeadersConfig(
      frameOptions = Option(frameOptions),
      xssProtection = Option(xssProtection),
      contentTypeOptions = Option(contentTypeOptions),
      permittedCrossDomainPolicies = Option(permittedCrossDomainPolicies),
      contentSecurityPolicy = Option(contentSecurityPolicy))
  }
}

/**
 * The case class that implements the filter.  This gives you the most control, but you may want to use the apply()
 * method on the companion singleton for convenience.
 */
class SecurityHeadersFilter(config: => SecurityHeadersConfig) extends Filter {

  private lazy val securityHeadersConfig: SecurityHeadersConfig = config

  /**
   * Zero argument constructor.  This allows the Java GlobalSettings class to call this class and load configured
   * options from application.conf.
   *
   * @return a new configured instance of SecurityHeadersFilter.
   */
  def this() = this(new SecurityHeadersParser().parse(play.api.Play.current.configuration))

  /**
   * Applies the filter to an action, appending the headers to the result so it shows in the HTTP response.
   *
   * @param f the rawest form of Action.
   * @param rh the request header.
   * @return a result with the security headers included, using r.withHeaders.
   */
  def apply(f: (RequestHeader) => Future[Result])(rh: RequestHeader): Future[Result] = {
    import play.api.libs.concurrent.Execution.Implicits.defaultContext
    import SecurityHeadersFilter._

    val result = f(rh)
    result.map {
      r =>
        val headers: Seq[(String, String)] = Seq(
          securityHeadersConfig.frameOptions.map(X_FRAME_OPTIONS_HEADER -> _),
          securityHeadersConfig.xssProtection.map(X_XSS_PROTECTION_HEADER -> _),
          securityHeadersConfig.contentTypeOptions.map(X_CONTENT_TYPE_OPTIONS_HEADER -> _),
          securityHeadersConfig.permittedCrossDomainPolicies.map(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER -> _),
          securityHeadersConfig.contentSecurityPolicy.map(CONTENT_SECURITY_POLICY_HEADER -> _)
        ).flatten
        r.withHeaders(headers: _*)
    }
  }
}

Other Play Framework source code examples

Here is a short list of links related to this Play Framework SecurityHeadersFilter.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.