scala.play.security.webservice-ssrf.webservice-ssrf

profile photo of semgrepsemgrep
Author
unknown
Download Count*

A parameter being passed directly into WSClient most likely lead to SSRF. This could allow an attacker to send data to their own server, potentially exposing sensitive data sent with this request. They could also probe internal servers or other resources that the server runnig this code can access. Do not allow arbitrary hosts. Instead, create an allowlist for approved hosts hardcode the correct host.

Run Locally

Run in CI

Defintion

rules:
  - id: webservice-ssrf
    patterns:
      - pattern: $WS.url($URL)
      - pattern-either:
          - pattern-inside: |
              class $CLASS (..., $WS: WSClient, ...) {
                ...
              }
          - pattern-inside: |
              def $FUNC(..., $WS: WSClient, ...) = {
                ...
              }
          - pattern-inside: |
              $WS = AhcWSClient(...)
              ...
      - pattern-either:
          - pattern-inside: |
              def $FUNC(..., $URL: $T, ...) = $A {
                ...
              }
          - pattern-inside: |
              def $FUNC(..., $URL: $T, ...) = {
                ...
              }
    message: A parameter being passed directly into `WSClient` most likely lead to
      SSRF. This could allow an attacker to send data to their own server,
      potentially exposing sensitive data sent with this request. They could
      also probe internal servers or other resources that the server runnig this
      code can access. Do not allow arbitrary hosts. Instead, create an
      allowlist for approved hosts hardcode the correct host.
    metadata:
      cwe:
        - "CWE-918: Server-Side Request Forgery (SSRF)"
      owasp:
        - A10:2021 - Server-Side Request Forgery (SSRF)
      references:
        - https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
        - https://www.playframework.com/documentation/2.8.x/ScalaWS
      category: security
      technology:
        - scala
        - play
      confidence: LOW
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - audit
      likelihood: LOW
      impact: HIGH
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Server-Side Request Forgery (SSRF)
    languages:
      - scala
    severity: WARNING

Examples

webservice-ssrf.scala

package controllers

import javax.inject._
import play.api._
import play.api.mvc._
import play.api.libs.ws._
import scala.concurrent.Future
import scala.util.Success
import scala.util.Failure
import scala.concurrent.ExecutionContext

object Smth {
  def call1(wsClient: WSClient, url: String): Future[Unit] = {
    // ruleid: webservice-ssrf
    wsClient.url(url).get().map { response =>
      val statusText: String = response.statusText
      println(s"Got a response $statusText")
    }
  }

  def call2(wsClient: WSClient): Future[Unit] = {
    // ok: webservice-ssrf
    wsClient.url("https://www.google.com").get().map { response =>
      val statusText: String = response.statusText
      println(s"Got a response $statusText")
    }
  }
}

object FooBar {
  def call1(url: String): Future[Unit] = {
    val wsClient = AhcWSClient()
    // ruleid: webservice-ssrf
    wsClient.url(url).get().map { response =>
      val statusText: String = response.statusText
      println(s"Got a response $statusText")
    }
  }

  def call2(): Future[Unit] = {
    val wsClient = AhcWSClient()
    // ok: webservice-ssrf
    wsClient.url("https://www.google.com").get().map { response =>
      val statusText: String = response.statusText
      println(s"Got a response $statusText")
    }
  }
}

@Singleton
class HomeController @Inject()(
  ws: WSClient,
  val controllerComponents: ControllerComponents,
  implicit val ec: ExecutionContext
) extends BaseController {

  def req1(url: String) = Action.async { implicit request: Request[AnyContent] =>
    // ruleid: webservice-ssrf
    val futureResponse = ws.url(url).get()
    futureResponse.map { response =>
      Ok(s"it works: ${response.statusText}")
    }
  }

  def req2(url: String) = Action.async { implicit request: Request[AnyContent] =>
    // ok: webservice-ssrf
    val futureResponse = ws.url("https://www.google.com").get()
    futureResponse.map { response =>
      Ok(s"it works: ${url}")
    }
  }

}