This is a security bugfix release for the 3.8 line, no new features since
3.8.12 were added. It contains only the fixes for the two security
vulnerabilities also fixed in 3.9.7 — a JSON template injection in the connectivity
service’s ImplicitThingCreation payload mapper and a Server-Side Request Forgery when fetching WoT
ThingModels in the things service — and no other changes.
Changelog
Security fixes
Both vulnerabilities were reported and fixed privately, and are disclosed through the GitHub Security Advisory process. As the fixes were merged from private forks, there are no public pull requests for them; the advisories are linked instead and carry the full details, including the affected code, the fix and the available workarounds.
Security fix for CVE-2026-82958 — JSON template injection in the ImplicitThingCreation mapper
Advisory GHSA-cgfq-3fv9-5c44 / CVE CVE-2026-82958
Severity high (CVSS 4.0 score 7.6,
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N).
The connectivity service’s ImplicitThingCreationMessageMapper built its CreateThing command by
substituting placeholder values (e.g. {{ header:device_id }}) resolved from inbound message
headers into the configured JSON thing template as raw, un-escaped strings, and only then parsed the result
as JSON. The placeholder engine performs no JSON escaping and is unaware of the surrounding JSON string
context, so a resolved value containing a " character could break out of its string and inject additional
JSON structure — most importantly an inline _policy object, which overrides the administrator-configured
policyId (CWE-1336). _copyPolicyFrom and policyId
could be injected the same way.
An attacker able to publish on such a connection could therefore assign an arbitrary access-control policy to the newly created digital twin — granting themselves full read/write access and potentially revoking the legitimate owner’s access — without any administrator interaction.
On the 3.8 line, all versions up to and including 3.8.12 are affected, but only deployments that use the
(non-default) ImplicitThingCreation payload mapper and whose thing template reflects a header value the
publishing device can control (e.g. an MQTT 5 user property, an AMQP 1.0 application property or a Kafka
record header). The policy-override outcome additionally requires the connection’s authorization subjects to
be permitted to create policy entities, which is the default. Deployments whose template only reflects values
asserted by trusted middleware (e.g. an Eclipse Hono provided device_id derived from device
authentication), or that do not use this mapper at all, are not exploitable through a device.
The mapper now resolves placeholders per JSON key and per JSON leaf value through the JSON model — which
escapes them — instead of substituting them into the raw template string before parsing. A resolved value is
confined to the single key or string value it originated from and can no longer introduce additional JSON
fields such as _policy.
If you cannot upgrade immediately, either of the following mitigates the issue:
- Restrict entity creation for the connection (recommended, blocks the policy hijack): configure
ditto.entity-creationso the connection’s authorization subjects may create onlythingentities, notpolicyentities. The inline_policyis applied via a separateCreatePolicycommand, which is then rejected withEntityNotCreatableException, failing the injected creation. Reference an existing, admin-owned policy viapolicyIdin the template. Note that entity creation is unrestricted by default. - Do not reflect device-controllable headers into the ImplicitThingCreation thing template.
Security fix for CVE-2026-84175 — Server-Side Request Forgery via WoT ThingModel definition URL
Advisory GHSA-7f3j-xpvm-wwmg / CVE CVE-2026-84175
Severity medium (CVSS 4.0 score 5.3,
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:L/SC:L/SI:N/SA:N).
When a Thing or Feature definition contains an http(s):// URL, the things service fetches the referenced
WoT ThingModel from that URL. Affected versions performed no validation of the
target host: there was no allow-list, no blocking of loopback / link-local / site-local / unique-local
addresses, and HTTP redirects were followed without re-validating the redirect target and without a hop limit
(CWE-918).
Because the fetch originates inside the deployment’s network, any authenticated user who may create a Thing —
or who holds WRITE permission on an existing Thing — could make the things service issue arbitrary HTTP
GET requests to internal endpoints, such as cloud instance-metadata services (169.254.169.254,
fd00:ec2::254), the Kubernetes API server or internal admin interfaces. The error returned to the caller
differs depending on the target’s behaviour (HTTP 421 wot:tm.notfound vs. HTTP 400 wot:tm.invalid), which
could be used as an oracle to enumerate internal services, and where an internal target returned a JSON
object parseable as a ThingModel, parts of it could be incorporated into the created Thing and returned to the
caller. A single API call could be amplified into many internal requests, because tm:extends, tm:ref and
tm:submodel links of a fetched ThingModel are resolved and fetched as well. Secondarily, redirects were
followed recursively without a hop limit (CWE-674), so a
server redirecting to itself could occupy a WoT dispatcher thread for the duration of the 10 second fetch
timeout.
The affected endpoints are POST /api/2/things, PUT /api/2/things/{thingId},
PUT /api/2/things/{thingId}/definition, PUT /api/2/things/{thingId}/features/{featureId}/definition and
POST /api/2/things/{thingId}/migrateDefinition. All 3.8.x versions up to and including 3.8.12
are affected in the default configuration, because the WoT integration feature toggle is enabled by
default since 3.0.0.
The fix validates the target host of every WoT ThingModel fetch:
- Hosts resolving to loopback, link-local (covering
169.254.169.254), site-local, IPv6 unique-localfc00::/7(coveringfd00:ec2::254), multicast and wildcard addresses are blocked by default, as is the carrier-grade NAT range100.64.0.0/10(RFC 6598), which no address-class check covers. - Every redirect target is re-validated before it is followed, and the number of redirects is capped
(default
5), which also resolves the uncontrolled-recursion issue. tm:extends,tm:refandtm:submodellinks are resolved through the same download path and validated identically, and onlyhttpandhttpstargets are fetched — including for redirect targets.
The validation is enabled by default and configured under ditto.things.wot.http.security, with
corresponding environment variables and Helm values:
ditto.things.wot.http.security {
enabled = true
allowed-hostnames = "" # comma separated, always allowed, overrides the blocked address checks
blocked-hostnames = "" # comma separated, on top of the always-blocked address classes
blocked-subnets = "100.64.0.0/10" # comma separated, CIDR notation
blocked-host-regex = "" # empty string disables the check
max-redirects = 5
}
allowed-hostnames allow-list — otherwise those fetches now fail. See the
Migration notes below.Residual limitation: host validation resolves the URL’s hostname to decide whether to allow the fetch, while the subsequent HTTP request resolves the hostname again independently. For hostnames whose DNS is controlled by an attacker, the two lookups can in principle return different addresses (DNS rebinding), which a hostname-based check cannot fully prevent. Targets given as literal IP addresses are always validated. Operators are advised to combine this check with network-level egress controls — see Restricting WoT ThingModel fetching in the operating documentation.
If you cannot upgrade immediately, apply network-level egress controls (e.g. Kubernetes NetworkPolicies or
firewall rules) preventing the things service from reaching internal address ranges and the cloud
instance-metadata endpoint — recommended as defense in depth even after upgrading — restrict entity creation
via ditto.entity-creation including its
thing-definitions allow-list (this covers the creation path only, not migrateDefinition or the
definition-modification endpoints), grant WRITE permission on Things narrowly, or disable the WoT integration entirely via
DITTO_DEVOPS_FEATURE_WOT_INTEGRATION_ENABLED=false where it is not used.
Helm Chart
The Helm chart was updated to version 3.8.13, bumping the Ditto appVersion to 3.8.13.
It exposes the new WoT ThingModel host validation of the SSRF fix under things.config.wot.hostValidation
(enabled, allowedHostnames, blockedHostnames, blockedSubnets, blockedHostRegex, maxRedirects).
There are no other chart changes.
Migration notes
There are no migration steps required for the Ditto data model or APIs.
However, the fix for CVE-2026-84175
changes the default behaviour of WoT ThingModel fetching: hosts resolving to loopback, link-local,
site-local, IPv6 unique-local, multicast and wildcard addresses, plus the carrier-grade NAT range
100.64.0.0/10, are blocked by default. Deployments that intentionally serve ThingModels from such an
internal host — e.g. a model registry running inside the same cluster and addressed by a cluster-internal
name — must add that host to the allow-list before upgrading:
ditto.things.wot.http.security.allowed-hostnames = "my-model-registry.internal,10.0.0.5"
via the environment variable THINGS_WOT_THING_MODEL_HTTP_SECURITY_ALLOWED_HOSTNAMES or the Helm value
things.config.wot.hostValidation.allowedHostnames. Fetching a ThingModel from a blocked host fails with
HTTP 421 wot:tm.notfound.
Additionally, deployments using the ImplicitThingCreation payload mapper should be aware that placeholder values resolved into the thing template are now JSON-escaped and confined to the key or value they originate from. Templates that (intentionally) relied on a resolved header value injecting JSON structure will no longer work — this was the vulnerability fixed by CVE-2026-82958.