Skip to content

Security

D edited this page Aug 2, 2026 · 1 revision

Security

Overview

The plugin was audited and fixed for several classes of vulnerability, most notably an unauthenticated order payment bypass (see changelog for 1.0.8). It follows WordPress security best practices throughout.

Core Practices

  • Per-order nonces: All four AJAX endpoints (check_payment, confirm_payment, mark_paid, save_ndebit) use wp_create_nonce() / check_ajax_referer() with order-bound nonce actions (clink_*_<order_id>). A nonce from one order cannot be reused on another.
  • Order-key ownership: Every AJAX handler verifies the caller via wc_clink_verify_order_access(). Logged-in users must own the order; guests must present the matching WooCommerce order key (compared with hash_equals).
  • Escaping: All output uses WordPress escaping functions (esc_html, esc_url, esc_js, wp_kses_post).
  • Sanitization: All input uses sanitize_text_field() / absint().
  • No direct DB queries: All data access goes through WooCommerce APIs.
  • Ephemeral keys: Each checkout generates a fresh Nostr key pair for the CLINK session — keys are never reused.

Payment Flow Guards

  • mark_paid only completes orders that are still pending or on-hold, that carry a stored _clink_invoice, and that belong to the caller.
  • confirm_payment performs strict BOLT11 validation before storing an invoice:
    • checksum and format are verified
    • the invoice network must match the configured Bitcoin Network
    • the invoice amount must match the order total within 1 sat
    • invoices expiring in under 60 seconds are rejected
  • save_ndebit requires a valid ndebit1... string and resolves the target subscription server-side from the verified parent order — the client cannot supply a subscription ID.

Payments Are Confirmed by the Client

The CLINK protocol confirms payments via a cryptographically signed receipt delivered to the checkout page over Nostr. The receipt is verified by the client-side @shocknet/clink-sdk, then the order is marked paid via mark_paid. This is the inherent CLINK design: the merchant's node signs the receipt, and the plugin trusts it to complete the order.

Reporting Issues

Please report any security concern privately by opening an issue on the GitHub repository or contacting the plugin author on WordPress.org.

Clone this wiki locally