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

Author
unknown
Download Count*
License
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]
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}")
}
}
}
Short Link: https://sg.run/reRR