T09 · Insecure Skill Coding Practices
- Location
internal/export/login.go:32- Finding
Credential-Bearing Login Request Can Be Replayed to an Untrusted Host Through HTTP Redirects
- Content
View full analysis
Vulnerability Details
File Location:
internal/export/login.go:32-53
Vulnerability Type: Redirect-based credential disclosure
Risk Level: MediumVulnerable Code
go // Never POST the email/password to anything but Lose It's own domain. if err := assertFirstPartyURL(cfg.LoginURL); err != nil { return "", err } form := url.Values{ "username": {cfg.Email}, "password": {cfg.Password}, "grant_type": {"password"}, } req, err := http.NewRequestWithContext(ctx, http.MethodPost, cfg.LoginURL, strings.NewReader(form.Encode())) if err != nil { return "", newErr("building login request: %v", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("User-Agent", userAgent) client := &http.Client{Timeout: 60 * time.Second} resp, err := client.Do(req)Technical Analysis
The code validates only the initial login URL with
assertFirstPartyURL. It then uses a standard Gohttp.Clientwithout aCheckRedirectcallback. By default, this client follows HTTP redirects.For HTTP
307 Temporary Redirectand308 Permanent Redirectresponses, Go can preserve the POST method and replay the request body at the redirect destination. Because the request body contains the user's Lose It email and password, a redirect to a non-Lose It host could disclose those credentials. The first-party URL assertion is not applied to each redirect target.The compiled-in endpoint and initial HTTPS/hostname validation substantially limit exposure, but they do not enforce the documented guarantee across the complete redirect chain.
Attack Path
- A user runs
loseit-cli loginor runsloseit-cli dayswhen automatic authentication is required. - The application validates the compiled-in initial login endpoint and submits a form containing the user's email and password.
- A compromised or malicious first-party login endpoint, reverse p ...[truncated 918 chars]
- A user runs
- Remediation
View remediation
Remediation Suggestions
Configure the HTTP client with a
CheckRedirectcallback that validates every redirect destination before the redirected request is sent:go client := &http.Client{ Timeout: 60 * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error { if err := assertFirstPartyURL(req.URL.String()); err != nil { return err } return nil }, }For stronger hardening:
- Disable automatic redirects for the credential-bearing login request and reject all 3xx responses unless a redirect is explicitly required.
- If redirects must be supported, allow only HTTPS destinations on an exact, minimal hostname allowlist rather than every subdomain.
- Reject cross-origin redirects and place a conservative limit on redirect depth.
- Ensure credentials are never copied into a redirected request unless the destination has passed the same validation as the original URL.
- Apply equivalent redirect validation to session-cookie-bearing export requests to preserve the intended destination restrictions consistently.
- Add regression tests in which an approved test endpoint returns a 307 or 308 redirect to a second server. Verify that the second server receives neither credentials nor session cookies.
