Mass Assignment on Docker
How Mass Assignment Manifests in Docker
Mass assignment occurs when an application blindly maps incoming request fields onto internal objects without validating which properties should be mutable. In the Docker ecosystem this most commonly appears in custom wrappers or middleware that interact with the Docker Engine API (e.g., POST /containers/create, POST /images/create, POST /networks/create). If a developer takes the raw JSON payload and passes it directly to a Docker SDK struct or to json.Unmarshal into a generic map, any field present in the request — even those not documented — will be forwarded to the daemon.
Consider a simple Go service that exposes a wrapper for creating containers. The handler reads the request body into a map[string]interface{} and then copies every key into the container.Config struct before calling the Docker client:
func createContainerHandler(w http.ResponseWriter, r *http.Request) {
var payload map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
config := &container.Config{}
// Unsafe: copy every key without validation
for k, v := range payload {
switch k {
case "Image":
config.Image = v.(string)
case "Cmd":
config.Cmd = toStringSlice(v)
// ... other expected fields
default:
// <-- mass assignment: unknown fields are ignored here,
// but if the code instead used reflection to set struct tags,
// they would be applied.
}
}
// If the code used something like:
// mapstructure.Decode(payload, config) // without a whitelist
// then any field such as "Privileged", "User", "CapAdd" would be set.
resp, err := cli.ContainerCreate(ctx, config, nil, nil, nil, "")
// ...
}
An attacker could send a payload like:
{ "Image": "alpine", "Cmd": ["sh"], "Privileged": true, "User": "0:0", "CapAdd": ["SYS_ADMIN"] }If the wrapper unintentionally honors those extra fields, the resulting container runs with root privileges and extended capabilities — a classic privilege‑escalation vector. This matches OWASP API Security Top 10 2023 API4: Mass Assignment and has been observed in real‑world incidents such as the 2021 Tesla API breach where excessive property assignment allowed remote vehicle control.
Docker-Specific Detection
Detecting mass assignment in Docker‑adjacent APIs requires checking whether the endpoint accepts and acts upon undocumented properties that influence container runtime privileges. middleBrick’s Property Authorization check (one of its 12 parallel tests) performs exactly this: it sends a baseline request, then re‑sends the same request with additional fields that are known to affect Docker security (e.g., Privileged, User, CapAdd, SecurityOpt). If the response indicates a state change — such as a container being created with those privileges — the finding is reported.
You can trigger this check yourself with the middleBrick CLI:
middlebrick scan https://api.example.com/containers/create
The CLI returns a JSON report that includes a property_authorization section. Example excerpt:
{
"check": "property_authorization",
"severity": "high",
"finding": "Endpoint accepted undocumented field 'Privileged' and created a privileged container.",
"remediation": "Restrict deserialization to an explicit allow‑list of fields; use json.Decoder.DisallowUnknownFields or a whitelist struct."
}
middleBrick’s GitHub Action can run this scan on every pull request, failing the build if the property_authorization severity exceeds a threshold you define (e.g., high). The MCP Server lets you invoke the same scan directly from your IDE while authoring Docker‑related code, giving immediate feedback before you push.
Because middleBrick works unauthenticated and black‑box, it mirrors an attacker’s view: no agents, no configuration, just the public endpoint. The scan completes in 5–15 seconds, providing rapid feedback in CI pipelines.
Docker-Specific Remediation
The fix is to ensure that only explicitly allowed properties are transferred from the request to the Docker SDK objects. In Go, the idiomatic way is to define a narrow struct that lists the fields you intend to accept and enable DisallowUnknownFields on the JSON decoder:
type createContainerRequest struct {
Image string `json:"image"`
Cmd []string `json:"cmd,omitempty"`
Env []string `json:"env,omitempty"`
// Add any other fields you deliberately expose
}
func createContainerHandler(w http.ResponseWriter, r *http.Request) {
var req createContainerRequest
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields() // will return error if unknown keys appear
if err := dec.Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
config := &container.Config{
Image: req.Image,
Cmd: req.Cmd,
Env: req.Env,
}
// HostConfig, etc., can be built similarly from a separate whitelist struct
hostConfig := &container.HostConfig{
// only set fields you explicitly allow
}
resp, err := cli.ContainerCreate(ctx, config, hostConfig, nil, nil, "")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(resp)
}
If you must use a map for flexibility, validate the keys before applying them:
allowed := map[string]bool{
"image": true, "cmd": true, "env": true,
}
for k := range payload {
if !allowed[strings.ToLower(k)] {
http.Error(w, fmt.Sprintf("forbidden field: %s", k), http.StatusBadRequest)
return
}
}
// now safely copy known fields
Beyond input validation, leverage Docker’s native security defaults:
- Run containers with a non‑root user (
User: "1000:1000") unless absolutely required. - Drop unnecessary capabilities via
HostConfig.CapDrop(e.g.,["ALL"]) and only add back those you need. - Enable user namespaces (
--userns-remap) so that even a privileged container maps to an unprivileged host UID. - Set
SecurityOptto["no-new-privileges"]to prevent privilege escalation via execve.
These configurations are enforced by the Docker daemon; your API should never rely on them to block malicious input — instead, treat them as defense‑in‑depth layers. By combining strict field whitelisting with Docker’s built‑in hardening, you eliminate the mass‑assignment vector while preserving legitimate functionality.
Remember: middleBrick only detects and reports the issue; it does not modify your code or runtime. Use its findings to drive the fixes outlined above.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |