T09 · Insecure Skill Coding Practices
Error
- Location
- lib/zohoClient.js:4
- Finding
- Zoho OAuth Token Can Be Forwarded to an Arbitrary Configured Host<![CDATA[ ## Vulnerability Details **File Location**: `lib/zohoClient.js:4-10` **Vulnerability Type**: Unvalidated credential destination **Risk Level**: High ### Vulnerable Code ```js const ZOHO_DOMAIN = process.env.ZOHO_DOMAIN || 'desk.zoho.com'; const ZOHO_TOKEN = process.env.ZOHO_TOKEN; if(!ZOHO_TOKEN) log.warn('ZOHO_TOKEN not set — Zoho API calls will fail'); const client = axios.create({ baseURL: `https://${ZOHO_DOMAIN}/api/v1`, headers: { Authorization: `Zoho-oauthtoken ${ZOHO_TOKEN}` } }); ``` ### Technical Analysis The application obtains `ZOHO_DOMAIN` directly from an environment variable and interpolates it into the Axios base URL without validating it against a list of legitimate Zoho endpoints. The Zoho OAuth token is then unconditionally attached to requests made through this client. An attacker who can influence the process environment or deployment configuration can set `ZOHO_DOMAIN` to an attacker-controlled HTTPS host. When either application command makes a Zoho request, the request will include the valid OAuth token in its `Authorization` header. This issue does not provide an unauthenticated remote attacker with direct control over the domain. Exploitation requires the ability to alter application environment variables or configuration, such as through a compromised deployment pipeline, unsafe container configuration, or administrative misconfiguration. ### Attack Path 1. The attacker gains the ability to modify the application's environment configuration. 2. The attacker sets `ZOHO_DOMAIN` to a host under their control, such as `collector.example`. 3. An operator executes `npm run ingest` or `npm run analyse`. 4. The Axios client sends a request to `https://collector.example/api/v1/tickets`. 5. The request includes `Authorization: Zoho-oauthtoken <token>`. 6. The attacker captures the token and uses it against the real Zoho Desk API, subject to the token's scopes and validity. ### Impact Assessment Successful exploitation d ...[truncated 375 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Validate `ZOHO_DOMAIN` against an explicit allowlist of supported Zoho Desk domains. - Reject values containing schemes, paths, user-information components, query strings, fragments, or unexpected ports. - Prefer selecting from fixed regional Zoho endpoints rather than accepting an arbitrary hostname. - Disable or strictly validate redirects for authenticated API requests so credentials cannot be forwarded to another origin. - Use a minimally scoped OAuth token and rotate it immediately if unauthorized forwarding is suspected. - Fail closed during startup when the domain is invalid. Example hardening: ```js const ALLOWED_ZOHO_DOMAINS = new Set([ 'desk.zoho.com', 'desk.zoho.eu', 'desk.zoho.in', 'desk.zoho.com.au' ]); const ZOHO_DOMAIN = process.env.ZOHO_DOMAIN || 'desk.zoho.com'; if (!ALLOWED_ZOHO_DOMAINS.has(ZOHO_DOMAIN)) { throw new Error('Unsupported ZOHO_DOMAIN'); } const client = axios.create({ baseURL: `https://${ZOHO_DOMAIN}/api/v1`, maxRedirects: 0, headers: { Authorization: `Zoho-oauthtoken ${ZOHO_TOKEN}` } }); ``` ]]>
