T09 · Insecure Skill Coding Practices
Error
- Location
- references/create-wallet-pin.md:201
- Finding
- Wallet credentials persisted in browser localStorage in the PIN workflow<![CDATA[ ## Vulnerability Details **File Location**: `references/create-wallet-pin.md:201-209` **Vulnerability Type**: Browser-accessible storage of sensitive authentication material **Risk Level**: High ### Vulnerable Code ```tsx const handleGetUserToken = async () => { if (!userId) return; const response = await fetch(`${apiBaseUrl}/api/wallet/get-token`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId }), }); const data = await response.json(); setCredentials({ userToken: data.userToken, encryptionKey: data.encryptionKey }); localStorage.setItem("userToken", data.userToken); localStorage.setItem("encryptionKey", data.encryptionKey); }; ``` ### Technical Analysis The example persists both `userToken` and `encryptionKey` in `localStorage`. Any JavaScript running under the application's origin can read these values. This includes injected scripts resulting from cross-site scripting, compromised third-party frontend packages, malicious analytics scripts, and potentially hostile browser extensions. These credentials are subsequently passed to `sdk.setAuthentication()` and are therefore security-sensitive wallet authorization material. Although `SKILL.md` warns that `localStorage` should not be used in production, the reference presents this behavior as part of a directly reusable implementation. ### Attack Path 1. A user authenticates through the PIN workflow. 2. The application saves the Circle `userToken` and `encryptionKey` in `localStorage`. 3. An attacker obtains same-origin script execution through an XSS vulnerability or compromised frontend dependency. 4. The attacker reads: ```js localStorage.getItem("userToken"); localStorage.getItem("encryptionKey"); ``` 5. The attacker exfiltrates the credentials and attempts to initialize the wallet SDK or execute available wallet challenges before the credentials expire. ### Impact Assessment Successful exploitation exp ...[truncated 377 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Remove `userToken` and `encryptionKey` persistence from `localStorage`. - Replace the example with a production-safe design rather than relying only on a warning. - Use a short-lived, server-managed session represented by an opaque cookie configured with `HttpOnly`, `Secure`, and an appropriate `SameSite` policy. - Ensure sensitive Circle credentials are never returned to unnecessary frontend components. - If client-side access to a credential is unavoidable for SDK operation, keep it only in memory, minimize its lifetime, clear it on logout or inactivity, and deploy a strict Content Security Policy. - Audit all frontend dependencies and eliminate XSS sinks because an `HttpOnly` session alone does not prevent an injected script from issuing authenticated actions. ]]>
