Skip to content

fix: use forwarded deviceIp for web-context MoonPay buy payment URLs#4205

Open
shadi-bp wants to merge 1 commit into
bitpay:masterfrom
shadi-bp:fix/moonpay-web-buy-device-ip
Open

fix: use forwarded deviceIp for web-context MoonPay buy payment URLs#4205
shadi-bp wants to merge 1 commit into
bitpay:masterfrom
shadi-bp:fix/moonpay-web-buy-device-ip

Conversation

@shadi-bp

Copy link
Copy Markdown

Description

Fixes BitPay web Buy via MoonPay dropping to 0 starting ~July 10 (internal ticket B1-3576). Sell and the app Buy flow were unaffected.

4e698ca5b implemented MoonPay's on-ramp security upgrade, binding each signed widget URL to the customer's public IP via the allowedIpAddress param (HMAC of the IP that requested the URL). Web buy requests don't come from the customer's device, though — they're proxied through the bitpay.com backend (frontend proxy wallet), so BWS hashed the proxy server's IP. Every web Buy URL was IP-locked to that server, and MoonPay's widget rejected the session for 100% of web users. The app flow was unaffected because the device calls BWS directly, and sell URL generation never included allowedIpAddress.

https://git.b-pay.net/bitpay/bitpay/-/merge_requests/11570

This PR has a companion bitpay MR that forwards the customer's real public IP (Cloudflare cf-connecting-ip) as deviceIp in the signed payment URL request. MoonPay's integration guide explicitly requires the true client IP behind the proxy/CDN and warns against using a server-side address.

Changelog

  • moonpayGetSignedPaymentUrl: for web-context requests, hash the deviceIp forwarded by the bitpay.com proxy instead of the BWS request IP. The forwarded value is trusted only for the web context, since web credentials are held exclusively by the proxy; app-context requests ignore a body deviceIp and behave exactly as before.
  • If a web-context request has no deviceIp (proxy not yet updated), omit allowedIpAddress from the URL rather than sign an IP the customer can never match — the param is optional per MoonPay, so deploying this change alone restores web Buy immediately; the companion bitpay MR then re-enables the IP lock with the correct customer IP.
  • Canonicalize the IP before hashing (trim, strip IPv4-mapped IPv6 ::ffff: prefix) — HMAC is byte-exact and MoonPay hashes the plain IPv4 it observes.
  • Tests: 4 new cases covering forwarded-IP hashing for web (using the productionWeb keys), IPv4-mapped IPv6 canonicalization, omission of allowedIpAddress when web sends no deviceIp, and that non-web context ignores a body deviceIp.

Testing Notes


Checklist

  • I have read CONTRIBUTING.md and verified that this PR follows the guidelines and requirements outlined in it.

The allowedIpAddress param added in 4e698ca hashes the IP of the
request to BWS. Web buy requests are proxied through the bitpay
backend, so every web URL was IP-locked to that server instead of the
customer, and MoonPay blocked the widget for all web buys.

For web context, hash the customer IP forwarded by the proxy as
deviceIp (web credentials are only held by the proxy, so the value is
trusted for that context). If no deviceIp is forwarded, omit
allowedIpAddress rather than sign an IP the customer can never match.
Canonicalize the IP before hashing since HMAC has no tolerance for
formatting differences. App-context behavior is unchanged.
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.

1 participant