Security for E-Commerce Sites

WordPress E-Commerce with WooCommerce

Chapter 7 · Security for E-Commerce Sites

WordPress Intermediate/Advanced 8 taught four mechanisms — capability checks, nonces, output escaping, and $wpdb->prepare() — as WordPress's own concrete implementations of general security principles this site already covered in depth. Nothing in this chapter is a new vulnerability class. What's new is the stakes: applied to a checkout page or an account form, the same four mechanisms stand between an attacker and real money, real payment metadata, and real customer PII — not a defaced blog post.

HTTPS: Non-Negotiable for a Store

HTTPS/TLS Fundamentals already covered why encryption in transit matters generally. For a store, it stops being a best practice and becomes a hard requirement: Chapter 5's own tokenized payment fields (Stripe Elements, PayPal) require a secure context to function at all — modern browsers block or restrict the APIs these gateways depend on over plain HTTP, and PCI-DSS itself requires TLS for any transmission of payment-related data. A store running checkout over HTTP isn't taking a small risk; in practice, its payment gateway simply won't work correctly.

XSS on Reviews & Account Fields

XSS — Cross Site Scripting's general lesson — encode output for its specific context, never trust rendered user input — applies directly to two store-specific surfaces: product reviews (user-submitted text, rendered back to every visitor of that product page) and account fields like a saved shipping address (rendered back to the customer, and often to store staff processing the order).

<?php // vulnerable: a customer's own saved address, echoed with no escaping echo '<p>' . $customer_address . '</p>'; // fixed: WordPress Intermediate/Advanced 8's own esc_html(), applied here echo '<p>' . esc_html( $customer_address ) . '</p>'; ?>
A store-specific reason this matters more than a typical comment field
An unescaped review or account field being rendered on an order-processing screen means store staff — often with far more system access than an ordinary site visitor — are the ones whose browser executes any injected script, potentially against an admin session with real order-management and refund capabilities.

CSRF on Checkout & Account Actions — Nonces Again

CSRF — Cross-Site Request Forgery's general defense — a token proving a request genuinely originated from the expected page — is exactly what protects "Place order" and account-changing actions. WooCommerce's own checkout form includes a nonce field (commonly named woocommerce-process-checkout-nonce), verified server-side before an order is ever created — the same wp_nonce_field()/wp_verify_nonce() pair WordPress Intermediate/Advanced 8 already covered, doing real work at the single most consequential form on the entire site.

Custom checkout fields inherit this protection automatically
Any custom field added to the checkout form (Chapter 8 covers adding these via hooks) submits inside the same form, protected by the same nonce, as long as it isn't processed through a separate, custom AJAX endpoint that skips WooCommerce's own verification — a real, common mistake worth watching for specifically when extending checkout.

SQL Injection in Custom Order Queries

SQL Injections and Defences's general defense — never concatenate user input into a SQL string — applies directly the moment a store adds any custom feature that queries orders by user-supplied criteria, such as an "order lookup by email" tool.

<?php // vulnerable: customer-supplied email concatenated directly into SQL $results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}wc_orders WHERE billing_email = '" . $_GET['email'] . "'" ); // fixed: WordPress Intermediate/Advanced 8's own $wpdb->prepare(), applied here $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wc_orders WHERE billing_email = %s", $_GET['email'] ) ); ?>
This applies whether or not the store uses HPOS
Chapter 1 named High-Performance Order Storage as moving order data into dedicated tables like wc_orders. Custom code querying either the older wp_posts-based order storage or the newer HPOS tables needs $wpdb->prepare() exactly the same way — the underlying storage location changed, but the injection risk from unsanitized concatenation didn't.

Broken Access Control: Viewing Another Customer's Order

A customer's own My Account → Orders page correctly shows only their own orders, because WooCommerce's own code checks that the logged-in user actually owns each order before displaying it. Custom code that adds an order-lookup feature — say, a URL like /order-status/?order_id=482 — can easily reintroduce exactly the gap WordPress Intermediate/Advanced 8's own capability-check material warned about, if it fetches order #482 and displays it without confirming the currently logged-in user actually owns order #482.

Hiding the link is not the same fix here either
Exactly as WordPress Intermediate/Advanced 8 warned about a hidden "Delete Post" button, not linking directly to other customers' order IDs doesn't stop someone from simply guessing or incrementing the number in the URL. The order-fetching code itself must check ownership — comparing the order's own customer ID against the currently logged-in user's ID — independent of what links are or aren't shown anywhere in the interface.

A Compromised Checkout Page Is a Fraud Vector, Not Just a Defacement

A real, important limit on Chapter 5's own tokenization material
Chapter 5 explained that Stripe's tokenized fields keep raw card numbers off the store's own server. That protects the server — it does not automatically protect the browser. Malicious JavaScript injected into a checkout page — through an unpatched, vulnerable plugin, exactly the risk WordPress Fundamentals 6 and WordPress Fundamentals 10 already warned about — can still read keystrokes or overlay a fake payment form directly in the customer's own browser, before Stripe.js ever gets a chance to tokenize anything. This general pattern (real-world attacks against e-commerce checkout pages of exactly this shape are commonly called "Magecart-style" attacks, after the group that popularized it) is precisely why keeping every plugin patched and vetted matters even more on a store than on an ordinary blog — Chapter 5's own architecture doesn't make plugin security optional.

The Synthesis: Same Vulnerabilities, Store-Specific Stakes

Tying it back to OWASP Top 10 directly
Every issue this chapter covers maps directly onto categories OWASP Top 10 already taught in general — Broken Access Control (the order-lookup example), Injection (the SQL example), Security Misconfiguration and Vulnerable/Outdated Components (the plugin-patching material, echoing WordPress Fundamentals 10's own update-risk chapter), and Cryptographic Failures (the HTTPS material). None of it is new to e-commerce specifically. What changes on a store is what a successful exploit is actually worth to an attacker: real payment fraud, a real customer PII breach with genuine legal exposure under regimes like GDPR, and direct revenue loss — not merely reputational embarrassment.

Hands-On Exercises

Exercise 1

A developer builds an order-status lookup page at /order-status/?order_id=N, and fetches/displays whatever order matches that ID with no further check. Explain the exploit this leaves open, and the specific fix, referencing this chapter's own comparison to WordPress Intermediate/Advanced 8.

📄 View solution
Exercise 2

A store owner argues that since they use Stripe's tokenized checkout fields, a vulnerable plugin on their site can't put customer card data at risk. Explain why this chapter says that reasoning is incomplete.

📄 View solution
Exercise 3

Explain why this chapter says none of the vulnerability classes it covers are new to e-commerce specifically, while also arguing this chapter is still necessary and not just a repeat of WordPress Intermediate/Advanced 8.

📄 View solution

Chapter 7 Quick Reference

  • HTTPS is a hard requirement for a store — Chapter 5's own payment gateways need a secure context to function at all
  • XSS — escape reviews and saved account fields on output, especially since store-staff sessions often carry real order-management privileges
  • CSRF — WooCommerce's own checkout form already uses a nonce; custom AJAX-based checkout additions must not bypass it
  • SQL injection — any custom order query, against either legacy post-based storage or HPOS, needs $wpdb->prepare()
  • Broken access control — any custom order-lookup feature must verify the requesting user actually owns that order, not just avoid linking to it
  • Tokenization protects the server, not the browser — a compromised plugin can still enable client-side, "Magecart-style" card skimming even with Stripe/PayPal tokenization in place
  • None of these are new vulnerability classes — what changes on a store is the real-world value of a successful exploit: fraud, PII breach, and direct revenue loss
  • Next chapter: Extending WooCommerce: Hooks & Custom Functionality