CVE-2026-41164: Github.com/nuts foundation/nuts node Nuts-node Has JWT Type Confusion Vulnerability

MODERATE
Advisory Identifier: GHSA-9hmg-827w-9rhj
CVSS 4.4
CWE-345
Published
Advisory Summary

CVE-2026-41164 is a MODERATE (CVSS 4.4) nuts-node has jwt type confusion vulnerability (CWE-345: Insufficient Verification of Data Authenticity) affecting Github.com/nuts foundation/nuts node libraries in the Go ecosystem. The issue involves nuts-node has jwt type confusion in v1 access token introspection that allows vp replay as access token. Remediation is available in the latest vendor maintenance releases.

CVE-2026-41164 at a Glance

CVE Identifier CVE-2026-41164
GHSA Identifier GHSA-9hmg-827w-9rhj
Severity MODERATE
CVSS v3.1 Base Score 4.4
CWE Classification CWE-345: Insufficient Verification of Data Authenticity
Affected Product Github.com/nuts foundation/nuts node
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 4.4

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N).

Attack Vector Network (AV:N)
Attack Complexity High (AC:H)
Privileges Required Low (PR:L)
User Interaction Required (UI:R)
Scope Changed (S:C)
Confidentiality Low (C:L)
Integrity Low (I:L)
Availability None (A:N)

How to Fix CVE-2026-41164

To remediate this vulnerability, upgrade all affected Github.com/nuts foundation/nuts node dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/nuts-foundation/nuts-node fixed: Latest secure release
Upgrade github.com/nuts-foundation/nuts-node to Latest secure release
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/nuts-foundation/nuts-node

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-41164, inspect your project dependencies for Github.com/nuts foundation/nuts node:

inspect dependencies for github.com/nuts-foundation/nuts-node

Analyze the terminal output to locate any version of Github.com/nuts foundation/nuts node in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than the patched release), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-345: Insufficient Verification of Data Authenticity, where the product does not sufficiently verify the origin or authenticity of data, in a way that causes it to accept invalid data. In Github.com/nuts foundation/nuts node, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary The v1 access token introspection endpoint (`/auth/v1/introspect_access_token`) accepts any JWT signed by a key present on the node, without validating the JWT type, issuer-to-key binding, or required claims. This allows a Verifiable Presentation (VP) JWT to be replayed as an access token and receive an `active: true` introspection response. ## Background In the v1 auth flow ([Nuts RFC003](https://nuts-foundation.gitbook.io/v1/rfc/rfc003-oauth2-authorization)), access tokens are JWTs signed by the authorizer's key with: - `iss` = authorizer organization DID - `sub` = requester organization DID - `service` = purpose of use (e.g. `"eOverdracht"`) - `typ` header = `"JWT"` (default, not explicitly set) Verifiable Presentations are also JWTs with `typ: "JWT"` (per W3C VC Data Model 1.1). The W3C VC Data Model 2.0 changed this to `vp+jwt` specifically to prevent this class of confusion attack (See [Securing Verifiable Credentials using JOSE and COSE 3.1.1](https://www.w3.org/TR/vc-jose-cose/#securing-with-jose)). ## Vulnerability details The introspection endpoint performs only standard JWT checks. It does not perform the following Nuts-specific access token checks: 1. **Validate the `typ` header**: both ATs and VPs use `"JWT"` 2. **Bind `iss` to the signing key**: it doesn't verify that the `iss` claim matches the DID extracted from the `kid` 3. **Validate required claims**: `service` can be empty; `vp` claim is silently ignored by `FromMap()` which uses lenient JSON unmarshaling ## Attack scenario **Prerequisites:** Attacker (Org B) has received a VP JWT from the victim (Org A) during a normal access token request flow. 1. Org A creates a VP JWT signed with Org A's key and sends it to Org B (normal protocol flow) to request an access token 2. Org B presents this VP JWT to Org A's resource server as a bearer access token 3. Resource server calls Org A's v1 introspection endpoint 4. Introspection checks `privateKeyStore.Exists(kid)`, which passes, because Org A's key is on Org A's node 5. JSON unmarshaling is lenient; the `vp` claim is silently ignored 6. Returns `active: true` with `service: ""`, `iss: ""`, `sub: <Org A's DID>` ## Mitigating factors - **`service` is empty**: resource servers that strictly require a non-empty `service` field may reject the request at the application level - **`iss` is empty**: VP JWTs don't set `iss`, so resource servers checking this field would see an empty value - **Short-lived VPs**: VPs typically expire within minutes, narrowing the attack window - **v1 is legacy**: the v2 flow uses opaque access tokens and is not affected ## Severity rationale While the introspection endpoint incorrectly returns `active: true` for a replayed VP, we consider this not practically exploitable in the current deployment landscape. Resource servers require valid `service`, `iss` and `aud` values to route requests to the correct databases. A replayed VP returns empty `service`, empty `iss`, and wrong `sub` (Org A instead of B), making it unusable for meaningful access. The attack also requires the victim to first present a VP to the attacker through a legitimate protocol flow, and VPs are short-lived. The severity reflects that the protection against exploitation is accidental (resource servers need `service` for routing, not for security) and we cannot guarantee how all resource server implementations handle the `active: true` response with missing fields. ## The fix Affected versions: all v5.x releases prior to v5.4.31, and all v6.x releases prior to v6.2.3. From v5.4.31 and v6.2.3 onward, the following checks have been added to `IntrospectAccessToken`: 1. **`iss`-to-`kid` binding**: extract the DID from the `kid` header and verify it matches the `iss` claim 2. **Required claims validation**: reject tokens where `service` is empty 3. **`typ` header validation**: requires access tokens to be of `typ: "at+jwt"` Additionally, the access token creation code has been updated to use `typ: "at+jwt"` per RFC 9068. ## Patch Patches are available at https://github.com/nuts-foundation/nuts-node/releases/tag/v5.4.31 and https://github.com/nuts-foundation/nuts-node/releases/tag/v6.2.3. ## Workaround If users are unable to update their nuts-node, resource servers can mitigate this risk by explicitly validating the introspection response: reject responses where `service` is empty, where `iss` is empty or does not match the expected authorizer DID, or where `sub` does not match the expected requester DID (Org B instead of A).

CVE-2026-41164 Affected Github.com/nuts foundation/nuts node Versions

1 component(s)
Product / Package Ecosystem Affected Versions Fixed Version
github.com/nuts-foundation/nuts-node
Go
≤ 1.1.0

References

Frequently Asked Questions

What is CVE-2026-41164?
CVE-2026-41164 is a security vulnerability in Github.com/nuts foundation/nuts node involving nuts-node has JWT type confusion in v1 access token introspection that allows VP replay as access token under CWE-345: Insufficient Verification of Data Authenticity.
Which Github.com/nuts foundation/nuts node versions are affected?
Affected versions include releases prior to current release patches across affected packages: github.com/nuts-foundation/nuts-node.
What version fixes CVE-2026-41164?
Upgrading to current release patches (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/nuts-foundation/nuts-node affected?
Yes, github.com/nuts-foundation/nuts-node is affected in releases prior to Latest secure release. Upgrade your dependency to Latest secure release or later.
What is the CVSS score of CVE-2026-41164?
CVE-2026-41164 has a CVSS v3.1 base score of 4.4 (MODERATE) with vector string CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N.
What CWE is associated with CVE-2026-41164?
CVE-2026-41164 is classified under CWE-345: Insufficient Verification of Data Authenticity (The product does not sufficiently verify the origin or authenticity of data, in a way that causes it to accept invalid data.).
How can I check Go dependencies for CVE-2026-41164?
Run the dependency tree command: 'inspect dependencies for github.com/nuts-foundation/nuts-node'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-9hmg-827w-9rhj",
  "modified" : "2026-06-08T23:28:00Z",
  "published" : "2026-05-05T17:15:32Z",
  "aliases" : [ "CVE-2026-41164" ],
  "summary" : "nuts-node has JWT type confusion in v1 access token introspection that allows VP replay as access token",
  "details" : "## Summary\n\nThe v1 access token introspection endpoint (`/auth/v1/introspect_access_token`) accepts any JWT signed by a key present on the node, without validating the JWT type, issuer-to-key binding, or required claims. This allows a Verifiable Presentation (VP) JWT to be replayed as an access token and receive an `active: true` introspection response.\n\n## Background\n\nIn the v1 auth flow ([Nuts RFC003](https://nuts-foundation.gitbook.io/v1/rfc/rfc003-oauth2-authorization)), access tokens are JWTs signed by the authorizer's key with:\n- `iss` = authorizer organization DID\n- `sub` = requester organization DID\n- `service` = purpose of use (e.g. `\"eOverdracht\"`)\n- `typ` header = `\"JWT\"` (default, not explicitly set)\n\nVerifiable Presentations are also JWTs with `typ: \"JWT\"` (per W3C VC Data Model 1.1). The W3C VC Data Model 2.0 changed this to `vp+jwt` specifically to prevent this class of confusion attack (See [Securing Verifiable Credentials using JOSE and COSE 3.1.1](https://www.w3.org/TR/vc-jose-cose/#securing-with-jose)).\n\n## Vulnerability details\n\nThe introspection endpoint performs only standard JWT checks. It does not perform the following Nuts-specific access token checks:\n\n1. **Validate the `typ` header**: both ATs and VPs use `\"JWT\"`\n2. **Bind `iss` to the signing key**: it doesn't verify that the `iss` claim matches the DID extracted from the `kid`\n3. **Validate required claims**: `service` can be empty; `vp` claim is silently ignored by `FromMap()` which uses lenient JSON unmarshaling\n\n## Attack scenario\n\n**Prerequisites:** Attacker (Org B) has received a VP JWT from the victim (Org A) during a normal access token request flow.\n\n1. Org A creates a VP JWT signed with Org A's key and sends it to Org B (normal protocol flow) to request an access token\n2. Org B presents this VP JWT to Org A's resource server as a bearer access token\n3. Resource server calls Org A's v1 introspection endpoint\n4. Introspection checks `privateKeyStore.Exists(kid)`, which passes, because Org A's key is on Org A's node\n5. JSON unmarshaling is lenient; the `vp` claim is silently ignored\n6. Returns `active: true` with `service: \"\"`, `iss: \"\"`, `sub: <Org A's DID>`\n\n## Mitigating factors\n\n- **`service` is empty**: resource servers that strictly require a non-empty `service` field may reject the request at the application level\n- **`iss` is empty**: VP JWTs don't set `iss`, so resource servers checking this field would see an empty value\n- **Short-lived VPs**: VPs typically expire within minutes, narrowing the attack window\n- **v1 is legacy**: the v2 flow uses opaque access tokens and is not affected\n\n## Severity rationale\n\nWhile the introspection endpoint incorrectly returns `active: true` for a replayed VP, we consider this not practically exploitable in the current deployment landscape. Resource servers require valid `service`, `iss` and `aud` values to route requests to the correct databases. A replayed VP returns empty `service`, empty `iss`, and wrong `sub` (Org A instead of B), making it unusable for meaningful access. The attack also requires the victim to first present a VP to the attacker through a legitimate protocol flow, and VPs are short-lived.\n\nThe severity reflects that the protection against exploitation is accidental (resource servers need `service` for routing, not for security) and we cannot guarantee how all resource server implementations handle the `active: true` response with missing fields.\n\n## The fix\n\nAffected versions: all v5.x releases prior to v5.4.31, and all v6.x releases prior to v6.2.3. From v5.4.31 and v6.2.3 onward, the following checks have been added to `IntrospectAccessToken`:\n\n1. **`iss`-to-`kid` binding**: extract the DID from the `kid` header and verify it matches the `iss` claim\n2. **Required claims validation**: reject tokens where `service` is empty\n3. **`typ` header validation**: requires access tokens to be of `typ: \"at+jwt\"`\n\nAdditionally, the access token creation code has been updated to use `typ: \"at+jwt\"` per RFC 9068.\n\n## Patch\n\nPatches are available at https://github.com/nuts-foundation/nuts-node/releases/tag/v5.4.31 and https://github.com/nuts-foundation/nuts-node/releases/tag/v6.2.3.\n\n## Workaround\n\nIf users are unable to update their nuts-node, resource servers can mitigate this risk by explicitly validating the introspection response: reject responses where `service` is empty, where `iss` is empty or does not match the expected authorizer DID, or where `sub` does not match the expected requester DID (Org B instead of A).",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/nuts-foundation/nuts-node"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "last_affected" : "1.1.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/nuts-foundation/nuts-node/security/advisories/GHSA-9hmg-827w-9rhj"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-41164"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/nuts-foundation/nuts-node"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nuts-foundation/nuts-node/releases/tag/v5.4.31"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nuts-foundation/nuts-node/releases/tag/v6.2.3"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-345" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-05-05T17:15:32Z",
    "nvd_published_at" : "2026-05-26T18:16:47Z"
  }
}