T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/call_api.py:48
- Finding
- API credential disclosure through a configurable upstream endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config.py:10-17`; `scripts/call_api.py:48-72` **Vulnerability Type**: Credential disclosure through an untrusted configurable endpoint **Risk Level**: Medium ### Vulnerable Code `scripts/config.py:10-17`: ```python model_config = SettingsConfigDict( env_prefix="XBY_GAOKAO_", env_file=".env", env_file_encoding="utf-8", extra="ignore", ) # API配置 base_url: str = "https://mcp.xiaobenyang.com" ``` `scripts/call_api.py:48-72`: ```python def call_tool( self, mcp_id: str, tool_name: str, params: dict[str, Any], ) -> HttpResult: """调用小笨羊MCP工具""" url = f"{settings.base_url}/api" mcp_id = mcp_id or settings.mcp_id api_key = get_api_key() if not api_key: raise UpstreamError("API密钥未设置,请先调用 set_api_key()") headers = { "XBY-APIKEY": api_key, "func": tool_name, "mcpid": mcp_id, "Content-Type": "application/json", } # data = {k: str(v) if v is not None else "" for k, v in params.items()} t0 = time.time() try: resp = self._session.post( url=url, headers=headers, data=json.dumps(params), timeout=settings.timeout_seconds, ) ``` ### Technical Analysis The upstream `base_url` is a security-sensitive setting because every API request sends the user's `XBY-APIKEY` credential to that destination. Pydantic Settings permits this value to be overridden through the `XBY_GAOKAO_BASE_URL` environment variable or the working-directory `.env` file. The request logic constructs a URL directly from this configurable value and attaches the API key without validating the URL scheme, hostname, port, or origin. Consequently, an attacker who can influence the process environment or place a crafted `.env` file in the working directory can redirect authenticated requests to an attacker-controlled server. TLS does not prevent this attack if the subst ...[truncated 1742 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Make the credential destination immutable.** Hardcode the trusted API origin if endpoint customization is not required: ```python TRUSTED_API_ORIGIN = "https://mcp.xiaobenyang.com" url = f"{TRUSTED_API_ORIGIN}/api" ``` 2. **If configuration is required, validate the parsed URL before attaching credentials.** Enforce an exact HTTPS origin allowlist: ```python from urllib.parse import urlparse TRUSTED_HOSTS = {"mcp.xiaobenyang.com"} parsed = urlparse(settings.base_url) if ( parsed.scheme != "https" or parsed.hostname not in TRUSTED_HOSTS or parsed.username is not None or parsed.password is not None or parsed.port not in (None, 443) ): raise UpstreamError("Untrusted API endpoint") ``` 3. **Do not expose `base_url` through the ordinary `.env` configuration source.** Separate security-sensitive trust configuration from user-editable settings. 4. **Validate the final request URL immediately before transmission.** This provides defense in depth against future URL-construction or redirect-related changes. 5. **Restrict redirects for credential-bearing requests.** Use `allow_redirects=False`, or validate every redirect destination before forwarding the credential. 6. **Protect persisted credentials.** Prefer an operating-system credential store. If `.env` storage remains necessary, create the file with owner-only permissions such as mode `0600`, use a dedicated configuration directory, and exclude it from source control. ]]>
