logo

Database

Lack of data validation - Trust boundary violation

Need

Loading of code only from artifacts that are part of the application

Context

• Usage of Kotlin 1.9+ on the JVM for building web applications

• Usage of Spring Web MVC for REST controllers

• Usage of java.net.URLClassLoader

Description

1. Non compliant code

import java.net.URI
import java.net.URLClassLoader
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@RestController
class PluginController {...

The `load` endpoint below receives a URL from the request and adds it to a `URLClassLoader` shared by the application, so classes from that JAR can be loaded and instantiated afterwards. This moves the trust boundary to whatever server the caller chooses. An attacker points `jar` to a JAR they host, and the next time the application loads a plugin class, its static initializers and constructors run inside the application, with its privileges, secrets and network access. The result is remote code execution. Any feature that turns request data into class names, JAR locations or reflective method calls has the same problem.

2. Steps

• Never create a `URLClassLoader`, call `addURL` or load classes from locations or names taken from requests.

• Compile plugins with the application and select them through an explicit registry, such as Spring beans indexed by name.

• Discover separately packaged extensions with `ServiceLoader` from the application classpath only, and sign and review those artifacts.

• Avoid reflection driven by request data, such as `Class.forName` or `getMethod` with user-supplied names.

• Run the application with outbound network restrictions so it cannot fetch code from arbitrary hosts.

3. Secure code example

import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException

interface Plugin {
    val name: String...

The corrected controller never loads code from the request. The available plugins are ordinary classes compiled with the application and registered as Spring beans that implement the `Plugin` interface. Spring injects all of them into the controller, which indexes them by name. The request can only select one of those names; an unknown name returns `404 Not Found`. User input therefore chooses among behaviors that the developers wrote and reviewed, but it can never introduce new code. When extensions must be installed separately, they should be signed, reviewed artifacts deployed with the application, discovered with `ServiceLoader` from its own classpath, never downloaded from locations chosen at runtime.