Keep NWC connection secrets out of localStorage#436
Conversation
|
@ayushshrivastv is attempting to deploy a commit to the shopstr-eng Team on Vercel. A member of the Team first needs to authorize it. |
|
Does this require re-inputting the NWC connectionstribg for every session? Because if so this is not good for usability. The string should persist, otherwise it's very clunky to support and use. A better method for this would be to encryot the stored data using user passphrase or similar. |
|
@calvadev I handled the immediate fix for the current issue, but I agree the better long term solution here is encrypted persistence rather than forcing a full reconnect every session. Are you suggesting we should store the NWC connection only as an encrypted blob, with the decryption key coming from something user controlled like a passphrase or WebAuthn style unlock, so the app cannot silently decrypt it on its own? Then in later sessions, Shopstr could show that a wallet is already configured and ask the user to unlock it instead of reconnecting from scratch. After unlock, the decrypted NWC string would stay only in memory for the active session, while only non sensitive metadata remains persisted. |
Yes, similar to how the nsec is handled. It's encrypted using the user inputted passphrase and only when necessary the user is prompted and the raw nsec is handled in-memory for the actions needed. |
I’ll align this patch with how nsec is handled. |
Also For nsec, Shopstr already had a signer flow where the encrypted private key is stored, the passphrase is asked only when needed, and the raw secret is handled in memory for signing actions. That logic lives in n What I changed is that NWC now follows the same security pattern, but it is still a different type of secret and flow. Instead of a signer decrypting an nsec to sign Nostr events, the app now decrypts the saved NWC connection only when it needs to create a wallet provider for payment actions. One small difference still is that |
|
This pull request has been automatically marked as stale because it has not had recent activity for 3 weeks. |
The security issue here was that Shopstr was storing the full Nostr Wallet Connect connection string directly in localStorage, including the secret parameter inside the
nostr+walletconnect://...URL. That means the wallet credential was being treated like normal browser data even though it is actually a sensitive secret.Because localStorage is readable by any JavaScript running in the Shopstr origin, this created a real secret exposure risk. If there is any XSS, unsafe script execution, compromised dependency, or malicious injected script in the browser context, that stored NWC secret could be read and reused to perform wallet actions within the permissions of that connection. So the issue was not just “metadata in storage,” it was persistent client-side storage of a live wallet credential.
Safe reproduction: connect any NWC wallet in Settings, then open DevTools and read
localStorage.getItem("nwcString"). The fullnostr+walletconnect://...URL, including the secret parameter, is exposed in browser readable storage and later reused by checkout flows.What Changed?
saveNWCString()no longer writes to localStorage; it keeps the value in runtime memory only.saveNWCInfo()does the same for wallet metadata.getLocalStorageData()now deletes legacy nwcString and nwcInfo entries fromlocalStorage on read.
wallet-connect.tsxwere updated to use the new in-memory path.