T09 · Insecure Skill Coding Practices
Warning
- Location
- protocol.py:162
- Finding
- Gemini API Key Exposed in Request URL Query String<![CDATA[ ## Vulnerability Details **File Location**: `protocol.py`, lines 162–170 **Vulnerability Type**: API credential exposure through URL query parameters **Risk Level**: Medium ### Vulnerable Code ```python def call_gemini(prompt, api_key, max_tokens=2000): """Call Gemini API.""" url = f"{GEMINI_URL}?key={api_key}" payload = { "contents": [{"parts": [{"text": prompt}]}], "generationConfig": {"maxOutputTokens": max_tokens, "temperature": 0.8}, } req = urllib.request.Request( url, data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"}, method="POST", ) ``` ### Technical Analysis The Gemini API key is appended directly to the request URL as the `key` query parameter. HTTPS protects the URL while it is in transit, but query strings are commonly captured by HTTP client instrumentation, proxy logs, gateway logs, debugging tools, exception telemetry, and monitoring systems. The key is legitimately required to access Gemini, and the request is sent to Google's official API endpoint. Therefore, this is not evidence of intentional credential exfiltration. However, placing the key in the URL creates avoidable exposure beyond the minimum necessary for the Skill's declared functionality. ### Attack Path 1. A user runs an automated debate round or synthesis operation. 2. `call_gemini` constructs a URL containing the user's Gemini API key. 3. A local monitoring tool, instrumented HTTP client, TLS-inspecting proxy, gateway, or diagnostic logger records the complete request URL. 4. An attacker who can access those records extracts the `key` query parameter. 5. The attacker submits requests to the Gemini API using the stolen key, subject to the key's configured API restrictions and quotas. This path requires access to request telemetry, logs, or local monitoring facilities; it is not remotely exploitable from the code alone. ### Impact Assessment A disclosed key may allow unauthorized ...[truncated 308 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Use the provider-supported `x-goog-api-key` request header rather than a URL query parameter: ```python def call_gemini(prompt, api_key, max_tokens=2000): payload = { "contents": [{"parts": [{"text": prompt}]}], "generationConfig": { "maxOutputTokens": max_tokens, "temperature": 0.8, }, } req = urllib.request.Request( GEMINI_URL, data=json.dumps(payload).encode("utf-8"), headers={ "Content-Type": "application/json", "x-goog-api-key": api_key, }, method="POST", ) with urllib.request.urlopen(req, timeout=90) as resp: data = json.loads(resp.read()) return data["candidates"][0]["content"]["parts"][0]["text"] ``` Additional hardening measures: - Configure Google API restrictions so the key can access only the required Generative Language API. - Apply appropriate quotas and billing alerts. - Ensure request headers and exception objects are redacted before logging. - Rotate the key if complete request URLs may already have been retained. - Avoid printing or persisting constructed request objects that may include credentials. ]]>
