Skip to content

Bug Report: Critical Security Vulnerability - Private Key Exposure in Frontend #168

@ayushshrivastv

Description

@ayushshrivastv

Frontend Private Key Exposure Risks Total Wallet Compromise (Critical)

Describe the bug

The Solana RPC handling code in the frontend directly requests the user's raw private key from the connected wallet provider. The key is then used within the browser's JavaScript environment to manually sign and send transactions. This practice fundamentally breaks the security model of modern crypto wallets and exposes users to a catastrophic risk of total fund loss.

Vulnerability
A user's private key should never leave the secure, isolated environment of their wallet (Phantom, Solflare, or a Web3Auth vault). By calling this.provider.request({ method: "solanaPrivateKey" }), the application brings the most sensitive piece of user data into the insecure context of a web page. If the application is compromised by a Cross Site Scripting (XSS) attack, a malicious script could easily steal this key.

Impact
An attacker who successfully exploits an XSS vulnerability on the site could steal the user's private key. With the private key, the attacker gains complete and permanent control over the user's wallet, allowing them to drain all SOL and any associated tokens (USDC, etc.) and NFTs. This is the most severe impact possible for a user.

Complete Violation of Wallet Security Principles: This design pattern undermines the primary security guarantee of non-custodial wallets, which is that the private key never leaves the user's direct control.

Vulnerable Code
File: frontend/src/solanaRPC.ts

getPrivateKey(): This function explicitly requests the raw private key from the wallet provider.

sendToken(): This function calls getPrivateKey() and then uses the exposed key to create a Keypair and sign a transaction in the browser (Keypair.fromSecretKey(privateKeyArray)).

To Reproduce
Open the file frontend/src/solanaRPC.ts.

Inspect the getPrivateKey function. Observe that it uses this.provider.request to request the solanaPrivateKey, which is the user's raw secret.

Inspect the sendToken function. Observe that it calls getPrivateKey() to fetch the key.

Further in sendToken, observe that the retrieved privateKey is used to create a senderKeypair (Keypair.fromSecretKey(...)).

Finally, observe that this senderKeypair is passed directly into sendAndConfirmTransaction to sign the transaction, confirming that the signing is happening in the web application's context, not inside the secure wallet.

Present Behavior
The application requests the user's raw private key, brings it into the browser's JavaScript environment, and uses it to sign transactions.

Expected behavior
The application should never request, see, or handle the user's private key. It should construct an unsigned transaction and pass it to the wallet provider's signAndSendTransaction or signTransaction method. The wallet would then ask the user for approval and perform the signing securely in its own isolated environment, returning only the resulting transaction signature to the application.

Fix
Refactor the sendToken function and any other transaction sending functions to delegate signing to the wallet provider.

Remove getPrivateKey: This function and all calls to it must be deleted from the codebase.

Modify the sendToken function to use the wallet provider's secure signing method. The existing solanaWallet.signAndSendTransaction(transaction) pattern, which is already used in other functions within the same file (e.g sendTransaction), is the correct approach. The sendAndConfirmTransaction call should be replaced with a call to the wallet provider's method.

Correct Implementation

This pattern is already used correctly in the sendTransaction function in the same file.

// From the sendTransaction function in the same file
const { signature } = await solanaWallet.signAndSendTransaction(
  transaction
);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions