Skip to content

Fix/348 wallet funding replay protection#390

Merged
3m1n3nc3 merged 10 commits into
geevapp:mainfrom
Clinton6801:fix/348-wallet-funding-replay-protection
Jun 28, 2026
Merged

Fix/348 wallet funding replay protection#390
3m1n3nc3 merged 10 commits into
geevapp:mainfrom
Clinton6801:fix/348-wallet-funding-replay-protection

Conversation

@Clinton6801

Copy link
Copy Markdown
Contributor

Fix on-chain wallet funding replay protection and destination binding

Summary

This PR addresses critical security vulnerabilities in the on-chain wallet funding system by implementing:

  1. Replay protection - Prevents the same transaction from being credited multiple times
  2. Destination binding - Ensures deposits only go to the platform's custodial address
  3. Ownership binding - Validates that the payment sender matches the user's wallet address

Problem Statement

Vulnerability 1: Replay Attack (CWE-603)

Issue: An attacker could submit the same Stellar transaction hash multiple times to credit their wallet repeatedly without sending additional funds.

  • Impact: Unlimited wallet funding with a single on-chain transaction
  • Root Cause: No mechanism to track previously processed transactions

Vulnerability 2: Destination Address Manipulation (CWE-20)

Issue: Users could specify an arbitrary destination address, potentially bypassing custody validation.

  • Impact: Loss of platform custody over deposited funds
  • Root Cause: No validation that destination matches platform's custodial address

Vulnerability 3: Ownership Binding (CWE-603)

Issue: A user could attribute another user's on-chain payment to their own account.

  • Impact: Account takeover and wallet fraud
  • Root Cause: No verification that payment sender matches the user submitting the request

Solution

1. Replay Protection

  • Added @unique constraint on WalletTransaction.txHash column
  • Implemented pre-check before transaction creation to detect duplicates
  • Added database migration for the constraint
  • Handles Prisma P2002 unique constraint violations with proper error response
  • Response: Returns 409 Conflict with message "Transaction already credited"

2. Destination Binding

  • Reads STELLAR_CUSTODIAL_ADDRESS from environment variables
  • Validates that incoming stellarAddress parameter matches the configured custodial address
  • Returns 400 Bad Request if addresses don't match
  • Ensures all deposits are custody-bound

3. Ownership Binding

  • Updated verifyStellarPayment() to return the payment's from address
  • Validates that verified.from matches currentUser.walletAddress
  • Returns 403 Forbidden if sender doesn't match caller
  • Prevents cross-user payment attribution

Changes

Files Modified

app/prisma/schema.prisma

txHash String? @unique  // Added @unique constraint

app/prisma/migrations/20260626000000_add_unique_constraint_on_wallet_transaction_txhash/migration.sql

  • Created new migration to apply unique constraint in database

app/lib/stellar.ts

// Updated return type to include `from` address
export async function verifyStellarPayment(
  txHash: string,
  destinationAddress: string,
  expectedAmount: number,
): Promise<{ amount: number; asset: string; from: string }>

app/app/api/wallet/fund/route.ts

  • Added custodial address validation (lines 47-55)
  • Added ownership binding validation (lines 66-68)
  • Added replay protection check (lines 72-77)
  • Added P2002 error handling for concurrency (lines 109-112)

app/.env.example

  • Added STELLAR_CUSTODIAL_ADDRESS environment variable reference

app/tests/api/wallet-fund.test.ts (NEW)

  • Comprehensive test suite with 40+ test cases covering:
    • Simulated funding
    • On-chain funding validations
    • Destination binding (400 errors)
    • Ownership binding (403 errors)
    • Replay protection (409 errors)
    • Authorization
    • Input validation
    • Stellar verification
    • Concurrency and edge cases

Error Responses

Scenario Status Message
Transaction already credited (replay) 409 "Transaction already credited"
Invalid destination address 400 "Invalid destination address"
Payment sender mismatch 403 "Payment sender does not match your wallet address"
Missing txHash 400 "txHash is required for on-chain funding"
Missing stellarAddress 400 "stellarAddress is required for on-chain funding"
Stellar verification failure 400 [Stellar error message]
Unauthorized 401 "Unauthorized"
Custodial address not configured 500 "Platform custodial address not configured"

Testing

Test Coverage

  • ✅ 40+ test cases created
  • ✅ All security scenarios covered
  • ✅ Edge cases tested (concurrent requests, multiple users, etc.)
  • ✅ Error handling validated

Test Categories

  1. Simulated funding
  2. On-chain validation
  3. Destination binding
  4. Ownership binding
  5. Replay protection
  6. Authorization
  7. Input validation
  8. Stellar integration
  9. Concurrency scenarios

Running Tests

npm run test -- wallet-fund.test.ts

Deployment Checklist

  • Apply database migration: npx prisma migrate deploy
  • Set STELLAR_CUSTODIAL_ADDRESS environment variable to platform's custodial Stellar address
  • Run full test suite: npm run test
  • Manual testing with Stellar testnet
  • Monitor production logs for replay attempts

Security Impact

Before Fix

  • ❌ Same transaction could be replayed unlimited times
  • ❌ Users could specify arbitrary destination addresses
  • ❌ Cross-user payment attribution possible
  • ❌ No custody control over deposits

After Fix

  • ✅ Replay attacks blocked with 409 Conflict
  • ✅ Destination address enforced and validated
  • ✅ Payment sender verified against caller's wallet
  • ✅ Full custody control with ownership binding
  • ✅ Atomic transaction processing prevents race conditions

Related Issues

Additional Notes

Implementation Details

  • Uses existing Prisma transaction patterns for atomicity
  • Leverages existing verifyStellarPayment() infrastructure
  • Follows project's error handling conventions
  • Compatible with both testnet and mainnet Stellar deployments
  • No breaking changes to existing simulated funding flow

Future Improvements

  • Consider implementing rate limiting for funding attempts
  • Add webhook notifications for large deposits
  • Implement transaction signature verification for additional security

Reviewers

Please verify:

  • Database migration is correct
  • Environment variable documentation is clear
  • Test coverage is comprehensive
  • Error messages are user-friendly
  • Security assumptions are valid

Alu-card19 and others added 9 commits June 25, 2026 17:47
…allet funding

- Add @unique constraint on WalletTransaction.txHash to prevent replay attacks
- Validate destination address matches STELLAR_CUSTODIAL_ADDRESS env var
- Bind payment sender to caller's walletAddress for ownership validation
- Return 409 Conflict on transaction replay attempts
- Return 400 Bad Request for invalid destinations
- Return 403 Forbidden for sender mismatches
- Handle Prisma P2002 unique constraint violations
- Update verifyStellarPayment to return payment's from address
- Add comprehensive test coverage (40+ test cases)
- Add STELLAR_CUSTODIAL_ADDRESS to .env.example

Fixes: Issue geevapp#348
@drips-wave

drips-wave Bot commented Jun 26, 2026

Copy link
Copy Markdown

@Clinton6801 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@3m1n3nc3

Copy link
Copy Markdown
Contributor

Your PR is not a contrat issue, please remove all changes that touch on the contracts. The failures were from a bad PR we acidentally merged which has since been reversed. You just need to update your fork and the contract failures would be gone.

Keep only wallet funding security fixes:
- Replay protection via unique txHash constraint
- Destination binding validation
- Ownership binding validation
- Supporting tests and migrations
@3m1n3nc3 3m1n3nc3 merged commit 6497c52 into geevapp:main Jun 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

On-chain wallet funding has no replay protection and trusts a client-supplied destination — arbitrary balance inflation

3 participants