All Downloads are FREE. Search and download functionalities are using the official Maven repository.

skinny.engine.csrf.CsrfTokenSupport.scala Maven / Gradle / Ivy

The newest version!
package skinny.engine.csrf

import skinny.engine.SkinnyEngineBase
import skinny.engine.base.BeforeAfterDsl
import skinny.engine.context.SkinnyEngineContext

/**
 * Provides cross-site request forgery protection.
 *
 * If a request is determined to be forged, the `handleForgery()` hook is invoked.
 * Otherwise, a token for the next request is prepared with `prepareCsrfToken`.
 */
trait CsrfTokenSupport { this: SkinnyEngineBase with BeforeAfterDsl =>

  before(isForged) { handleForgery() }
  before() { prepareCsrfToken() }

  /**
   * Tests whether a request with a unsafe method is a potential cross-site
   * forgery.
   *
   * @return true if the request is an unsafe method (POST, PUT, DELETE, TRACE,
   * CONNECT, PATCH) and the request parameter at `csrfKey` does not match
   * the session key of the same name.
   */
  protected def isForged: Boolean =
    !request.requestMethod.isSafe &&
      session(context).get(csrfKey) != params(context).get(csrfKey) &&
      !CsrfTokenSupport.HeaderNames.map(request.headers.get).contains(session(context).get(csrfKey))

  /**
   * Take an action when a forgery is detected. The default action
   * halts further request processing and returns a 403 HTTP status code.
   */
  protected def handleForgery(): Unit = {
    halt(403, "Request tampering detected!")
  }

  /**
   * Prepares a CSRF token.  The default implementation uses `GenerateId`
   * and stores it on the session.
   */
  // NOTE: keep return type as Any for backward compatibility
  protected def prepareCsrfToken(): Any = {
    session(context).getOrElseUpdate(csrfKey, CsrfTokenGenerator.apply()).toString
  }

  /**
   * The key used to store the token on the session, as well as the parameter
   * of the request.
   */
  def csrfKey: String = CsrfTokenSupport.DefaultKey

  /**
   * Returns the token from the session.
   */
  protected[skinny] def csrfToken(implicit context: SkinnyEngineContext): String =
    context.request.getSession.getAttribute(csrfKey).asInstanceOf[String]

}

object CsrfTokenSupport {

  val DefaultKey = "skinny.engine.CsrfTokenSupport.key"

  val HeaderNames = Vector("X-CSRF-TOKEN")

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy