Jul 22, 2026·7 min read·0 visits
n8n leaked Google Service Account private keys inside the unencrypted 'kid' parameter of outbound JWT headers, permitting complete Google Cloud Platform resource compromise upon network or log exposure.
A credential exposure vulnerability in n8n (CVE-2026-65599) transmits the complete PEM-formatted Google Service Account private key in the 'kid' (Key ID) parameter of outbound JWT headers during Google API authentication. Because JWT headers are encoded in plain Base64URL and not encrypted, any entity that intercepts, logs, or processes these outbound requests can immediately extract the plaintext private key. This enables unauthorized third parties to fully compromise the corresponding Google Cloud Platform (GCP) service account and access any authorized cloud resources.
The workflow automation platform n8n enables users to design multi-step data integration tasks involving third-party APIs. To interact with Google Cloud Platform (GCP) resources or Google Workspace services, workflows often leverage Google Service Account credentials. These credentials typically rely on a JSON-formatted configuration payload containing metadata, a unique Key ID, and an RSA-2048 private key in PEM format.
When authenticating against Google OAuth endpoints, the application must construct and sign a JSON Web Token (JWT) to obtain an access token. In standard cryptographic protocol design, the JWT header includes a Key ID (kid) parameter, which is a public label helping the verifying system locate the correct public certificate. In affected versions of n8n, a structural design error caused the platform to transmit the entire secret PEM-formatted private key inside this unencrypted header parameter.
Because the JWT header is merely Base64URL-encoded and transmitted as part of the Authorization header in outbound HTTP requests, this behavior violates fundamental security principles associated with cleartext transmission of credentials (CWE-312). The resulting attack surface exposes highly sensitive cloud-infrastructure credentials to intermediate network equipment, internal application logs, corporate HTTP proxies, and monitoring systems.
To authenticate a Google Service Account, the client application must generate a JWT containing standard OAuth claims (such as the issuer, requested scopes, target audience, and expiration times) and sign it using the service account's private key. The specification for JSON Web Signature (JWS) defines the kid header parameter as a hint indicating which key was used to secure the payload. It is mathematically designed to hold a non-sensitive cryptographic fingerprint, such as a hash or public key ID.
In the affected versions of n8n, the logic responsible for compiling the JWS headers assigned the value of the private_key field from the user's Google Service Account credentials directly to the kid parameter. This mistake caused the application to generate JWS headers similar to the following structured example:
{
"alg": "RS256",
"typ": "JWT",
"kid": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDhv8g6xU...\n-----END PRIVATE KEY-----\n"
}When n8n encodes this structure using the standard RFC 7515 Base64URL specification, the plaintext PEM key remains unencrypted. Since the application transmits the resulting serialized JWT directly to the Google OAuth endpoint (https://oauth2.googleapis.com/token) in cleartext form over SSL/TLS, the transport layer security only protects the secret in transit. Any component that has visibility into the application layers of the client or the intermediate gateways will capture the full private key.
The vulnerability stems from improper structural mapping during JWT compilation. The following pseudo-code contrasts the vulnerable implementation against the patched implementation introduced in fixed releases:
// VULNERABLE: Direct assignment of the sensitive PEM private key to the JWT 'kid' header field
import * as jwt from 'jsonwebtoken';
function generateGoogleJwtVulnerable(credentials: GoogleServiceAccountCreds) {
const payload = { ... };
const options: jwt.SignOptions = {
algorithm: 'RS256',
header: {
kid: credentials.privateKey, // SEVERE ERROR: Private key placed in public-facing header
}
};
return jwt.sign(payload, credentials.privateKey, options);
}To correct this exposure, n8n developers updated the JWT construction sequence to map the kid parameter to the unique, non-sensitive public key identifier string (private_key_id) from the Google Cloud credential JSON, as illustrated below:
// PATCHED: Assigning the non-sensitive public key identifier to 'kid'
import * as jwt from 'jsonwebtoken';
function generateGoogleJwtPatched(credentials: GoogleServiceAccountCreds) {
const payload = { ... };
const options: jwt.SignOptions = {
algorithm: 'RS256',
header: {
kid: credentials.privateKeyId, // FIXED: Correctly uses non-sensitive Key ID
}
};
return jwt.sign(payload, credentials.privateKey, options);
}This structural alteration completely resolves the leak. While the private key is still utilized to sign the JWT, it is strictly kept in memory on the n8n application host and is never written into the serialized header string transmitted over the network.
Exploitation of CVE-2026-65599 does not require an active exploit payload or complex memory corruption techniques. It relies entirely on passive or active interception of outbound API traffic. An attacker must position themselves to observe either the outbound network request or the application logs generated by the n8n instance.
First, an administrative user or workflow creator must configure a Google Service Account in n8n and execute a workflow that utilizes Google node integrations. Once executed, the application generates the outbound HTTP request. If the organization uses internal HTTP logging, debugging proxies (such as mitmproxy or Fiddler), or security monitoring solutions that record outbound HTTP payloads, the complete JWT header is recorded in plain text.
Once an adversary extracts the JWT from a captured request or system log, extraction of the credential requires only simple Base64URL decoding of the first dot-separated segment:
# Extract the JWS Header (the first dot-separated block)
JWT_TOKEN="eyJhbGciOiJSUzI1NiIsImtpZCI6Ii0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLVxuTUlJRXZnS..."
HEADER_B64=$(echo "$JWT_TOKEN" | cut -d'.' -f1)
# Decode and extract the plaintext PEM-formatted Private Key
echo "$HEADER_B64" | tr '_-' '/+' | openssl base64 -d | jq -r '.kid'The resulting output yields the fully functional, plaintext -----BEGIN PRIVATE KEY----- block, allowing the attacker to construct their own authentication tokens to gain unauthorized access to Google Cloud resources.
Although the CVSS v4.0 score is designated as 5.1 (Medium) because creating or configuring workflows in n8n typically requires administrative access (PR:H), the downstream impact to the organization's cloud environment is critical. The compromised credential provides full programmatic access under the identity of the targeted Google Service Account.
If the compromised Service Account holds expansive administrative privileges, such as Project Owner, Editor, or specific IAM roles (such as Storage Admin or BigQuery Admin), the attacker can gain full read and write access to enterprise cloud assets. This can lead to database exfiltration, modification of infrastructure-as-code configurations, or deployment of rogue compute instances for crypto-mining or lateral network movement.
Because the key is leaked via normal runtime API client operations, it is highly likely that the credential has been written to local or centralized SIEM logs, intermediate proxy history, or performance monitoring databases. This legacy log footprint means that patching the n8n software alone is insufficient to mitigate the risk; the exposed keys remain active on GCP until manually revoked.
Remediation of CVE-2026-65599 requires a two-phased approach consisting of software updates to eliminate the programmatic leak and credential rotation to address historical exposure.
First, n8n instances must be upgraded to one of the officially patched versions: 1.123.64, 2.29.8, or 2.30.1. These versions correctly populate the kid parameter with the non-sensitive public Key ID, ensuring that the private key is never transmitted.
Second, administrators must immediately perform Google Service Account key rotation. Because exposed keys may already exist in proxy or application logs, the following actions must be taken in the Google Cloud Console:
To detect historical exploitation, security teams should inspect historical web proxy and application logs for JWT header payloads containing the signature substring -----BEGIN PRIVATE KEY----- or its Base64-encoded equivalents.
CVSS:4.0/AV:N/AC:L/AT:P/PR:H/UI:N/VC:L/VI:N/VA:N/SC:H/SI:L/SA:L| Product | Affected Versions | Fixed Version |
|---|---|---|
n8n n8n-io | < 1.123.64 | 1.123.64 |
n8n n8n-io | < 2.29.8 | 2.29.8 |
n8n n8n-io | < 2.30.1 | 2.30.1 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-65599 |
| CWE ID | CWE-312 |
| Attack Vector | Network (AV:N) |
| CVSS v4.0 Score | 5.1 (Medium) |
| CVSS Vector | CVSS:4.0/AV:N/AC:L/AT:P/PR:H/UI:N/VC:L/VI:N/VA:N/SC:H/SI:L/SA:L |
| Exploit Status | none |
| KEV Status | Not Listed |
The application stores or transmits sensitive information (the private key) in cleartext or in a format (Base64) that is easily reversible without a secret key.
A Stored DOM-based Cross-Site Scripting (XSS) vulnerability exists within the frontend Resource Locator component of n8n. The flaw stems from insecure usage of window.open() where the application evaluates the workflow-persisted parameter 'cachedResultUrl' without verifying its protocol scheme. Authenticated attackers with permissions to create or edit workflows can insert a 'javascript:' URI payload, leading to arbitrary code execution in the victim's browser context upon interaction.
CVE-2026-47300 is a high-severity Elevation of Privilege (EoP) vulnerability in Microsoft's ASP.NET Core framework, affecting the Negotiate Authentication Handler when configured to retrieve security groups and role mappings via an LDAP/Active Directory directory service. In cross-platform Linux and macOS environments where native Windows token group expansion is unavailable, an authenticated attacker with low-privileged network access can manipulate LDAP query structures or force connections to untrusted directory resources, resulting in unauthorized administrative role assignment.
Gitea versions from 1.5.0 before 1.26.3 contain a security vulnerability in their multi-factor authentication (MFA) logic. This vulnerability allows valid TOTP codes to be accepted multiple times across web authentication flows and the Basic Auth X-Gitea-OTP header path. Due to a TOCTOU race condition and a lack of state tracking in programmatic auth pathways, attackers with valid credentials can replay single-use OTP codes.
A critical validation bypass exists in FasterXML jackson-core due to an incomplete fix for GHSA-72hv-8253-57qq. When parsing JSON asynchronously using NonBlockingUtf8JsonParserBase, the StreamReadConstraints.maxNumberLength constraint is bypassed when numeric inputs are received in small, non-terminating chunks. The parser continually accumulates digit-only input in an internal buffer without triggering validation constraints, resulting in potential heap memory exhaustion and application-level Denial of Service.
A critical second-order code injection vulnerability exists in the migration generation engine of TypeORM. When TypeORM introspects a database schema to automatically generate migration files, it writes schema metadata directly into JavaScript/TypeScript migration files inside ES2015 template literals. Because the generator failed to sanitize template literal string interpolation markers and backslashes, attackers with control over database metadata can execute arbitrary code on the developer environment or within a CI/CD pipeline.
A security logic flaw in Loofah, a Ruby HTML/XML sanitization library, allowed unauthenticated attackers to bypass local-reference restrictions on SVG elements. Prior to version 2.25.2, Loofah only sanitized the legacy namespaced "xlink:href" attribute when enforcing local URI fragments on SVG elements like "<use>". It did not validate or sanitize the plain, non-namespaced "href" attribute introduced in the SVG 2 standard. This structural omission allowed attackers to construct SVG elements referencing external domains, paving the way for arbitrary resource loading and cross-site scripting (XSS).