Security Hardening
WordPress Intermediate/Advanced
Chapter 8 · Security Hardening
WordPress Fundamentals 8 described roles as "named bundles of capabilities" and promised the real mechanism would come later, in real code. This chapter is that payoff — plus three more mechanisms giving WordPress-specific teeth to security principles this site has already taught in general: request forgery protection, output escaping, and safe database queries.
Capability Checks — current_user_can()
This is the exact function WordPress Fundamentals 8 promised: real code checking a specific capability, not a role name. "Editor" was never checked directly anywhere in WordPress core — every permission decision comes down to a call exactly like this one.
current_user_can() before doing anything, regardless of what was or wasn't shown in the interface. A user who reaches the underlying action URL directly, bypassing the hidden button entirely, must still be blocked by the check itself.
Nonces — WordPress's Own CSRF Protection
A nonce ("number used once") is a token WordPress generates and verifies to confirm a request genuinely originated from the expected page and user — this is WordPress's own concrete implementation of exactly the defense CSRF — Cross-Site Request Forgery covers in general.
current_user_can() (proving the requester is actually allowed to do this) — neither one alone is a complete defense.
Output Escaping — WordPress's Own Answer to XSS
esc_html(), esc_attr(), and esc_url() are concrete, CMS-specific instances of exactly the context-dependent output encoding XSS — Cross Site Scripting already covers in general — different contexts need different escaping.
| Function | Context |
|---|---|
esc_html() | Plain text inside an HTML element's body |
esc_attr() | Text placed inside an HTML attribute |
esc_url() | A URL, most often inside an href or src attribute |
esc_html() on data being placed inside an attribute (rather than esc_attr()) doesn't provide the correct protection for that specific context — matching exactly the "encode for the context, not generically" lesson XSS — Cross Site Scripting already taught in the abstract.
$wpdb->prepare() — WordPress's Own Parameterized Queries
$wpdb->prepare() is WordPress's own direct implementation of the parameterized-query defense SQL Injections and Defences covers in general — never concatenate user input directly into a SQL string, in WordPress exactly as anywhere else.
%d, %s, and %f are placeholders for integers, strings, and floats respectively — $wpdb safely substitutes the actual value in, exactly the same underlying protection as any other language's parameterized query API.
One Pattern, Four Mechanisms
$wpdb->prepare() is SQL injection prevention. None of these are WordPress inventing new security ideas — they're WordPress giving already-general principles a real, callable API, echoing OWASP Top 10's own framing and directly connecting back to the hardening discipline Setting Up a Web Server on Debian applied at the server layer.
Hands-On Exercises
A developer hides a "Delete Post" button from users without the right role, but the underlying delete action itself never calls current_user_can(). Explain the real, exploitable gap this leaves.
📄 View solutionA developer uses esc_html() to output a URL inside an href attribute instead of esc_url(). Explain why this is still a real vulnerability, connecting your answer to this chapter's own context-dependent escaping material.
📄 View solutionExplain why this chapter describes $wpdb->prepare() as "WordPress giving an already-general principle a real, callable API" rather than a uniquely WordPress security concept.
📄 View solutionChapter 8 Quick Reference
- current_user_can() — the real mechanism behind roles, delivering on WordPress Fundamentals 8's own promise; hiding UI is not a substitute for checking it directly in the privileged action
- Nonces (wp_nonce_field(), wp_verify_nonce()) — WordPress's own CSRF protection; combine with capability checks, never rely on either alone
- esc_html()/esc_attr()/esc_url() — context-dependent output escaping, WordPress's own answer to XSS
- $wpdb->prepare() — WordPress's own parameterized queries, WordPress's own answer to SQL injection
- All four are WordPress-specific implementations of general principles already taught in this site's own security courses, not new inventions
- Next chapter: Performance & Caching