T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/portainer_manager.py:11
- Finding
- Privileged Portainer API Token Transmitted Without TLS Certificate Verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/portainer_manager.py:11-14, 20-26, 35-41, 51-57, 82-88, 96-103, 114-120, 125-134` **Vulnerability Type**: Sensitive credential exposure through disabled TLS authentication **Risk Level**: High ### Vulnerable Code ```python # Attempt to get Portainer API URL from environment, default to https://localhost:9443/api PORTAINER_API_URL = os.environ.get("PORTAINER_API_URL", "https://localhost:9443/api") # Suppress warnings for self-signed certificates urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def get_portainer_api_token(): token = os.environ.get("PORTAINER_API_TOKEN") if not token: raise ValueError("PORTAINER_API_TOKEN environment variable not set.") return token def list_environments(): print(f"Attempting to list Portainer environments via {PORTAINER_API_URL}...", flush=True) try: token = get_portainer_api_token() headers = {"X-API-Key": token, "Content-Type": "application/json"} response = requests.get( f"{PORTAINER_API_URL}/endpoints", headers=headers, timeout=10, verify=False ) ``` The same `verify=False` setting is used by all Portainer requests, including stack inspection, deployment, update, removal, and arbitrary Docker API proxy operations. ### Technical Analysis The Portainer API token is placed in the `X-API-Key` header and transmitted over HTTPS, but every request explicitly disables certificate verification. Consequently, HTTPS encryption is used without authenticating the remote server. Any certificate—including a self-signed certificate controlled by an attacker—is accepted. The application also globally suppresses `InsecureRequestWarning`, preventing operators from receiving the warning normally emitted for this unsafe behavior. Because `PORTAINER_API_URL` is configurable through the environment, an attacker who can alter configuration can redirect t ...[truncated 1995 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `verify=False` from every `requests.get`, `requests.post`, `requests.put`, and `requests.delete` call so that normal certificate validation is enforced. 2. Remove the global suppression of `urllib3.exceptions.InsecureRequestWarning`. 3. For private or self-signed Portainer deployments, support an explicit CA bundle: ```python PORTAINER_CA_BUNDLE = os.environ.get("PORTAINER_CA_BUNDLE", True) response = requests.get( url, headers=headers, timeout=10, verify=PORTAINER_CA_BUNDLE ) ``` 4. Reject non-HTTPS URLs except for an explicitly enabled localhost-only development mode. 5. Validate or allowlist the configured Portainer hostname to reduce credential redirection risk. 6. Use a dedicated, least-privileged Portainer API token rather than a general administrator token. 7. Rotate the existing token after remediation if it has ever been used over an untrusted network. 8. Consider certificate or public-key pinning for especially sensitive infrastructure. ]]>
