T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/strava_oauth_login.py:224
- Finding
- OAuth Authorization Flow Does Not Validate the State Parameter<![CDATA[ ## Vulnerability Details **File Location**: `scripts/strava_oauth_login.py`, lines 164-169 and 224-250 **Vulnerability Type**: OAuth login CSRF and authorization-session confusion **Risk Level**: Medium ### Vulnerable Code ```python q = urllib.parse.parse_qs(parsed.query) code = (q.get("code") or [None])[0] if code: got["code"] = code ``` ```python params = { "client_id": client_id, "redirect_uri": redirect_uri, "response_type": "code", "approval_prompt": "auto", "scope": scopes, } auth_url = AUTH_URL + "?" + urllib.parse.urlencode(params) print("Open this URL in a browser and approve access:\n") print(auth_url) if args.loopback: print("\nWaiting for redirect on:") print(redirect_uri) code = listen_for_code(redirect_uri) else: print("\nAfter approval, paste either the full redirect URL or just the code:") code = parse_code(input("> ")) tok = exchange_code_for_token( code=code, client_id=client_id, client_secret=client_secret ) ``` ### Technical Analysis The OAuth authorization request does not contain a cryptographically random `state` parameter. Correspondingly, neither the copy-and-paste flow nor the loopback callback verifies that an incoming authorization response belongs to the authorization transaction initiated by the script. OAuth `state` is used to bind the authorization response to the initiating client session and prevent login CSRF or authorization-response substitution. The script accepts any authorization code supplied through standard input or received as the first valid loopback callback. The token exchange still occurs against Strava's fixed HTTPS token endpoint, so this is not arbitrary credential exfiltration. The weakness instead concerns the identity and authorization transaction associated with the accepted code. ### Attack Path 1. An attacker initiates or influences a separate Strava authorization flow using the same registered client and redirect URI. 2. The attacker o ...[truncated 972 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Generate a fresh, unpredictable state value before constructing each authorization URL: ```python import secrets expected_state = secrets.token_urlsafe(32) params["state"] = expected_state ``` 2. Require the callback or pasted redirect URL to contain a `state` value exactly matching `expected_state`. 3. Reject responses with a missing, malformed, or mismatched state before exchanging the authorization code. 4. Explicitly process OAuth error responses such as `error` and `error_description`. 5. In loopback mode, accept only the configured callback path and terminate the listener after the first valid state-matched response. 6. Prefer a loopback address of `127.0.0.1` over hostname-based resolution where practical. ]]>
