<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>wordpress security Archives - Developry Plugins</title>
	<atom:link href="https://developryplugins.com/tag/wordpress-security/feed/" rel="self" type="application/rss+xml" />
	<link>https://developryplugins.com/tag/wordpress-security/</link>
	<description></description>
	<lastBuildDate>Sun, 21 Jun 2026 08:38:16 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://developryplugins.com/wp-content/uploads/2026/06/cropped-favicon-alt-32x32.webp</url>
	<title>wordpress security Archives - Developry Plugins</title>
	<link>https://developryplugins.com/tag/wordpress-security/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How to Block Brute Force Attacks on WordPress Login Pages</title>
		<link>https://developryplugins.com/how-to-block-brute-force-attacks-on-wordpress-login-pages/</link>
		
		<dc:creator><![CDATA[Krasen Slavov]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Security & Protection]]></category>
		<category><![CDATA[brute force protection]]></category>
		<category><![CDATA[firewall]]></category>
		<category><![CDATA[limit login attempts]]></category>
		<category><![CDATA[login security]]></category>
		<category><![CDATA[wordpress security]]></category>
		<guid isPermaLink="false">https://developryplugins.com/?p=130</guid>

					<description><![CDATA[<p>Brute force attacks on WordPress login pages are relentless. Bots try thousands of username/password combinations until they gain access. These attacks consume server resources, slow down your site, and eventually...</p>
<p>The post <a href="https://developryplugins.com/how-to-block-brute-force-attacks-on-wordpress-login-pages/">How to Block Brute Force Attacks on WordPress Login Pages</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- @format --></p>
<p>Brute force attacks on WordPress login pages are relentless. Bots try thousands of username/password combinations until they gain access. These attacks consume server resources, slow down your site, and eventually succeed against weak passwords.</p>
<p>This guide provides multiple defensive layers to block brute force attacks, from limiting login attempts to IP whitelisting and CAPTCHA implementation.</p>
<h2 id="understanding-brute-force-attacks">Understanding Brute Force Attacks</h2>
<h3 id="how-they-work">How They Work</h3>
<p>Attackers use automated scripts to try common passwords against wp-login.php:</p>
<pre><code>admin / password
admin / 123456
admin / admin123
administrator / password
...thousands more combinations</code></pre>
<h3 id="signs-of-an-attack">Signs of an Attack</h3>
<ul>
<li>Hundreds of failed login attempts in logs</li>
<li>Increased server CPU/memory usage</li>
<li>Slow admin dashboard</li>
<li>Email flood of failed login notifications</li>
<li>IP addresses from foreign countries</li>
<li>Automated bot patterns (rapid-fire attempts)</li>
</ul>
<h2 id="layer-1-limit-login-attempts">Layer 1: Limit Login Attempts</h2>
<h3 id="using-limit-login-attempts-reloaded">Using Limit Login Attempts Reloaded</h3>
<div class="sourceCode" id="cb2">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="co"># Install via WP-CLI</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="ex">wp</span> plugin install limit-login-attempts-reloaded --activate</span></code></pre>
</div>
<p><strong>Recommended Settings:</strong></p>
<ul>
<li><strong>Allowed attempts:</strong> 4</li>
<li><strong>Lockout duration:</strong> 20 minutes</li>
<li><strong>Reset after:</strong> 12 hours</li>
<li><strong>Long lockout:</strong> 24 hours after 4 lockouts</li>
</ul>
<p><strong>Manual Implementation:</strong></p>
<div class="sourceCode" id="cb3">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="co">// In functions.php or custom plugin</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a>add_action<span class="ot">(</span><span class="st">&#39;wp_login_failed&#39;</span><span class="ot">,</span> <span class="st">&#39;track_failed_login&#39;</span><span class="ot">);</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a><span class="kw">function</span> track_failed_login<span class="ot">(</span><span class="kw">$username</span><span class="ot">)</span> {</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a>    <span class="kw">$ip</span> = <span class="kw">$_SERVER</span><span class="ot">[</span><span class="st">&#39;REMOTE_ADDR&#39;</span><span class="ot">];</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a>    <span class="kw">$attempts</span> = get_transient<span class="ot">(</span><span class="st">&#39;failed_login_&#39;</span> . <span class="kw">$ip</span><span class="ot">)</span> <span class="ot">?:</span> <span class="dv">0</span><span class="ot">;</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a>    <span class="kw">$attempts</span>++<span class="ot">;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a>    set_transient<span class="ot">(</span><span class="st">&#39;failed_login_&#39;</span> . <span class="kw">$ip</span><span class="ot">,</span> <span class="kw">$attempts</span><span class="ot">,</span> <span class="dv">20</span> * <span class="kw">MINUTE_IN_SECONDS</span><span class="ot">);</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span><span class="kw">$attempts</span> &gt;= <span class="dv">5</span><span class="ot">)</span> {</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true"></a>        <span class="co">// Lock out this IP</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true"></a>        set_transient<span class="ot">(</span><span class="st">&#39;lockout_&#39;</span> . <span class="kw">$ip</span><span class="ot">,</span> <span class="kw">true</span><span class="ot">,</span> <span class="kw">HOUR_IN_SECONDS</span><span class="ot">);</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true"></a>        wp_die<span class="ot">(</span><span class="st">&#39;Too many failed login attempts. Please try again in 1 hour.&#39;</span><span class="ot">);</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true"></a>    }</span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true"></a>}</span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true"></a></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true"></a><span class="co">// Check before allowing login</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true"></a>add_filter<span class="ot">(</span><span class="st">&#39;authenticate&#39;</span><span class="ot">,</span> <span class="st">&#39;check_lockout&#39;</span><span class="ot">,</span> <span class="dv">30</span><span class="ot">,</span> <span class="dv">3</span><span class="ot">);</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true"></a></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true"></a><span class="kw">function</span> check_lockout<span class="ot">(</span><span class="kw">$user</span><span class="ot">,</span> <span class="kw">$username</span><span class="ot">,</span> <span class="kw">$password</span><span class="ot">)</span> {</span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true"></a>    <span class="kw">$ip</span> = <span class="kw">$_SERVER</span><span class="ot">[</span><span class="st">&#39;REMOTE_ADDR&#39;</span><span class="ot">];</span></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true"></a></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span>get_transient<span class="ot">(</span><span class="st">&#39;lockout_&#39;</span> . <span class="kw">$ip</span><span class="ot">))</span> {</span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true"></a>        <span class="kw">return</span> <span class="kw">new</span> WP_Error<span class="ot">(</span><span class="st">&#39;lockout&#39;</span><span class="ot">,</span> <span class="st">&#39;Account locked due to too many failed attempts.&#39;</span><span class="ot">);</span></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true"></a>    }</span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true"></a></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">$user</span><span class="ot">;</span></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="layer-2-add-captcha-protection">Layer 2: Add CAPTCHA Protection</h2>
<h3 id="google-recaptcha-v3-integration">Google reCAPTCHA v3 Integration</h3>
<div class="sourceCode" id="cb4">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co">// Add to functions.php</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a><span class="co">// Enqueue reCAPTCHA script</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a>add_action<span class="ot">(</span><span class="st">&#39;login_enqueue_scripts&#39;</span><span class="ot">,</span> <span class="st">&#39;add_recaptcha_to_login&#39;</span><span class="ot">);</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a><span class="kw">function</span> add_recaptcha_to_login<span class="ot">()</span> {</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a>    <span class="kw">?&gt;</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true"></a>    &lt;script src=<span class="st">&quot;https://www.google.com/recaptcha/api.js&quot;</span> async defer&gt;&lt;/script&gt;</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true"></a>    &lt;script&gt;</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true"></a>    <span class="kw">function</span> onSubmit<span class="ot">(</span>token<span class="ot">)</span> {</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true"></a>        document.getElementById<span class="ot">(</span><span class="st">&quot;loginform&quot;</span><span class="ot">)</span>.submit<span class="ot">();</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true"></a>    }</span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true"></a>    &lt;/script&gt;</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true"></a>    &lt;<span class="ot">?</span>php</span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true"></a>}</span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true"></a></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true"></a><span class="co">// Add reCAPTCHA to login form</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true"></a>add_action<span class="ot">(</span><span class="st">&#39;login_form&#39;</span><span class="ot">,</span> <span class="st">&#39;display_recaptcha&#39;</span><span class="ot">);</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true"></a></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true"></a><span class="kw">function</span> display_recaptcha<span class="ot">()</span> {</span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true"></a>    <span class="kw">?&gt;</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true"></a>    &lt;div <span class="kw">class</span>=<span class="st">&quot;g-recaptcha&quot;</span> data-sitekey=<span class="st">&quot;YOUR_SITE_KEY&quot;</span> data-callback=<span class="st">&quot;onSubmit&quot;</span>&gt;&lt;/div&gt;</span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true"></a>    &lt;<span class="ot">?</span>php</span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true"></a>}</span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true"></a></span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true"></a><span class="co">// Verify reCAPTCHA</span></span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true"></a>add_filter<span class="ot">(</span><span class="st">&#39;authenticate&#39;</span><span class="ot">,</span> <span class="st">&#39;verify_recaptcha&#39;</span><span class="ot">,</span> <span class="dv">30</span><span class="ot">,</span> <span class="dv">3</span><span class="ot">);</span></span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true"></a></span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true"></a><span class="kw">function</span> verify_recaptcha<span class="ot">(</span><span class="kw">$user</span><span class="ot">,</span> <span class="kw">$username</span><span class="ot">,</span> <span class="kw">$password</span><span class="ot">)</span> {</span>
<span id="cb4-30"><a href="#cb4-30" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span><span class="kw">empty</span><span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;g-recaptcha-response&#39;</span><span class="ot">]))</span> {</span>
<span id="cb4-31"><a href="#cb4-31" aria-hidden="true"></a>        <span class="kw">return</span> <span class="kw">new</span> WP_Error<span class="ot">(</span><span class="st">&#39;captcha_failed&#39;</span><span class="ot">,</span> <span class="st">&#39;Please complete the reCAPTCHA.&#39;</span><span class="ot">);</span></span>
<span id="cb4-32"><a href="#cb4-32" aria-hidden="true"></a>    }</span>
<span id="cb4-33"><a href="#cb4-33" aria-hidden="true"></a></span>
<span id="cb4-34"><a href="#cb4-34" aria-hidden="true"></a>    <span class="kw">$recaptcha</span> = <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;g-recaptcha-response&#39;</span><span class="ot">];</span></span>
<span id="cb4-35"><a href="#cb4-35" aria-hidden="true"></a>    <span class="kw">$secret_key</span> = <span class="st">&#39;YOUR_SECRET_KEY&#39;</span><span class="ot">;</span></span>
<span id="cb4-36"><a href="#cb4-36" aria-hidden="true"></a></span>
<span id="cb4-37"><a href="#cb4-37" aria-hidden="true"></a>    <span class="kw">$response</span> = wp_remote_post<span class="ot">(</span><span class="st">&#39;https://www.google.com/recaptcha/api/siteverify&#39;</span><span class="ot">,</span> <span class="ot">[</span></span>
<span id="cb4-38"><a href="#cb4-38" aria-hidden="true"></a>        <span class="st">&#39;body&#39;</span> =&gt; <span class="ot">[</span></span>
<span id="cb4-39"><a href="#cb4-39" aria-hidden="true"></a>            <span class="st">&#39;secret&#39;</span> =&gt; <span class="kw">$secret_key</span><span class="ot">,</span></span>
<span id="cb4-40"><a href="#cb4-40" aria-hidden="true"></a>            <span class="st">&#39;response&#39;</span> =&gt; <span class="kw">$recaptcha</span><span class="ot">,</span></span>
<span id="cb4-41"><a href="#cb4-41" aria-hidden="true"></a>            <span class="st">&#39;remoteip&#39;</span> =&gt; <span class="kw">$_SERVER</span><span class="ot">[</span><span class="st">&#39;REMOTE_ADDR&#39;</span><span class="ot">]</span></span>
<span id="cb4-42"><a href="#cb4-42" aria-hidden="true"></a>        <span class="ot">]</span></span>
<span id="cb4-43"><a href="#cb4-43" aria-hidden="true"></a>    <span class="ot">]);</span></span>
<span id="cb4-44"><a href="#cb4-44" aria-hidden="true"></a></span>
<span id="cb4-45"><a href="#cb4-45" aria-hidden="true"></a>    <span class="kw">$response_body</span> = <span class="fu">json_decode</span><span class="ot">(</span>wp_remote_retrieve_body<span class="ot">(</span><span class="kw">$response</span><span class="ot">));</span></span>
<span id="cb4-46"><a href="#cb4-46" aria-hidden="true"></a></span>
<span id="cb4-47"><a href="#cb4-47" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span>!<span class="kw">$response_body</span>-&gt;success || <span class="kw">$response_body</span>-&gt;score &lt; <span class="fl">0.5</span><span class="ot">)</span> {</span>
<span id="cb4-48"><a href="#cb4-48" aria-hidden="true"></a>        <span class="kw">return</span> <span class="kw">new</span> WP_Error<span class="ot">(</span><span class="st">&#39;captcha_failed&#39;</span><span class="ot">,</span> <span class="st">&#39;reCAPTCHA verification failed.&#39;</span><span class="ot">);</span></span>
<span id="cb4-49"><a href="#cb4-49" aria-hidden="true"></a>    }</span>
<span id="cb4-50"><a href="#cb4-50" aria-hidden="true"></a></span>
<span id="cb4-51"><a href="#cb4-51" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">$user</span><span class="ot">;</span></span>
<span id="cb4-52"><a href="#cb4-52" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="layer-3-change-login-url">Layer 3: Change Login URL</h2>
<h3 id="using-wps-hide-login-plugin">Using WPS Hide Login Plugin</h3>
<div class="sourceCode" id="cb5">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="ex">wp</span> plugin install wps-hide-login --activate</span></code></pre>
</div>
<p><strong>Settings:</strong> Change /wp-login.php to /my-secure-login</p>
<p><strong>Manual Method (via .htaccess):</strong></p>
<div class="sourceCode" id="cb6">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="co"># Redirect wp-login.php to custom URL</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="ex">RewriteEngine</span><span class="ch"> </span><span class="kw">On</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>RewriteCond<span class="st"> %{REQUEST_URI} ^/wp-login\.php</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a>RewriteCond<span class="st"> %{QUERY_STRING} !^action=logout</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a>RewriteCond<span class="st"> %{QUERY_STRING} !^action=rp</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a>RewriteCond<span class="st"> %{QUERY_STRING} !^action=register</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a>RewriteRule<span class="st"> ^(.*)$ /custom-login-url? [R=301,L]</span></span></code></pre>
</div>
<p><strong>Caution:</strong> Remember your custom URL. Losing it locks you out.</p>
<h2 id="layer-4-ip-whitelisting">Layer 4: IP Whitelisting</h2>
<h3 id="restrict-wp-admin-by-ip">Restrict wp-admin by IP</h3>
<div class="sourceCode" id="cb7">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="co"># In wp-admin/.htaccess</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="fu">&lt;Files</span><span class="at"> admin-ajax.php</span><span class="fu">&gt;</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a>    <span class="ex">Order</span><span class="ch"> </span><span class="kw">allow,deny</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a>    Allow<span class="st"> from all</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a>    <span class="ex">Satisfy</span><span class="ch"> </span><span class="kw">any</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a><span class="fu">&lt;/Files&gt;</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a><span class="co"># Block all other wp-admin access except your IP</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a><span class="ex">Order</span><span class="ch"> </span><span class="kw">deny,allow</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a>Deny<span class="st"> from all</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a>Allow<span class="st"> from 123.456.789.0</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a>Allow<span class="st"> from 987.654.321.0</span></span></code></pre>
</div>
<p><strong>For Nginx:</strong></p>
<pre class="nginx"><code>location ~* /wp-admin/ {
    allow 123.456.789.0;
    deny all;
}</code></pre>
<p><strong>Dynamic IP Solution (for VPN/mobile users):</strong></p>
<div class="sourceCode" id="cb9">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="co">// In functions.php - Email-based IP whitelisting</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a>add_action<span class="ot">(</span><span class="st">&#39;wp_login_failed&#39;</span><span class="ot">,</span> <span class="st">&#39;email_whitelist_request&#39;</span><span class="ot">);</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a><span class="kw">function</span> email_whitelist_request<span class="ot">(</span><span class="kw">$username</span><span class="ot">)</span> {</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a>    <span class="kw">$ip</span> = <span class="kw">$_SERVER</span><span class="ot">[</span><span class="st">&#39;REMOTE_ADDR&#39;</span><span class="ot">];</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a>    <span class="kw">$whitelist</span> = get_option<span class="ot">(</span><span class="st">&#39;ip_whitelist&#39;</span><span class="ot">,</span> <span class="ot">[]);</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span>!<span class="fu">in_array</span><span class="ot">(</span><span class="kw">$ip</span><span class="ot">,</span> <span class="kw">$whitelist</span><span class="ot">))</span> {</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a>        <span class="co">// Send email to admin with &quot;approve&quot; link</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a>        <span class="kw">$approve_url</span> = admin_url<span class="ot">(</span><span class="st">&#39;admin.php?action=approve_ip&amp;ip=&#39;</span> . <span class="kw">$ip</span><span class="ot">);</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true"></a>        wp_mail<span class="ot">(</span></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true"></a>            get_option<span class="ot">(</span><span class="st">&#39;admin_email&#39;</span><span class="ot">),</span></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true"></a>            <span class="st">&#39;New IP Login Attempt&#39;</span><span class="ot">,</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true"></a>            <span class="st">&quot;Approve IP </span><span class="kw">$ip</span><span class="st">: </span><span class="kw">$approve_url</span><span class="st">&quot;</span></span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true"></a>        <span class="ot">);</span></span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true"></a>    }</span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="layer-5-wordpress-firewall">Layer 5: WordPress Firewall</h2>
<h3 id="wordfence-configuration">Wordfence Configuration</h3>
<div class="sourceCode" id="cb10">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="ex">wp</span> plugin install wordfence --activate</span></code></pre>
</div>
<p><strong>Essential Settings:</strong></p>
<ol type="1">
<li>
<p><strong>Enable Extended Protection</strong> (premium feature)</p>
</li>
<li>
<p><strong>Brute Force Protection:</strong></p>
<ul>
<li>Enable login page CAPTCHA</li>
<li>Immediately block invalid usernames</li>
<li>Lock out after 5 failed logins</li>
</ul>
</li>
<li>
<p><strong>Advanced Blocking:</strong></p>
<ul>
<li>Block attackers from specific countries</li>
<li>Throttle login attempts</li>
<li>Block known malicious IPs</li>
</ul>
</li>
</ol>
<p><strong>Rate Limiting Example:</strong></p>
<div class="sourceCode" id="cb11">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="co">// Wordfence alternative - manual rate limiting</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a>add_action<span class="ot">(</span><span class="st">&#39;login_form&#39;</span><span class="ot">,</span> <span class="st">&#39;add_login_delay&#39;</span><span class="ot">);</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a><span class="kw">function</span> add_login_delay<span class="ot">()</span> {</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a>    <span class="kw">$ip</span> = <span class="kw">$_SERVER</span><span class="ot">[</span><span class="st">&#39;REMOTE_ADDR&#39;</span><span class="ot">];</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a>    <span class="kw">$last_attempt</span> = get_transient<span class="ot">(</span><span class="st">&#39;last_login_attempt_&#39;</span> . <span class="kw">$ip</span><span class="ot">);</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span><span class="kw">$last_attempt</span> &amp;&amp; <span class="ot">(</span><span class="fu">time</span><span class="ot">()</span> - <span class="kw">$last_attempt</span><span class="ot">)</span> &lt; <span class="dv">3</span><span class="ot">)</span> {</span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true"></a>        <span class="fu">sleep</span><span class="ot">(</span><span class="dv">3</span><span class="ot">);</span> <span class="co">// Force 3-second delay between attempts</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true"></a>    }</span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true"></a></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true"></a>    set_transient<span class="ot">(</span><span class="st">&#39;last_login_attempt_&#39;</span> . <span class="kw">$ip</span><span class="ot">,</span> <span class="fu">time</span><span class="ot">(),</span> <span class="kw">MINUTE_IN_SECONDS</span><span class="ot">);</span></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="layer-6-cloudflare-protection">Layer 6: Cloudflare Protection</h2>
<h3 id="enable-cloudflare-firewall-rules">Enable Cloudflare Firewall Rules</h3>
<ol type="1">
<li>Sign up for Cloudflare (free plan works)</li>
<li>Add your domain</li>
<li>Update nameservers</li>
<li>Enable “Under Attack Mode” during active brute force</li>
</ol>
<p><strong>Firewall Rule:</strong></p>
<pre><code>(http.request.uri.path contains &quot;/wp-login.php&quot;) and
(not ip.geoip.country in {&quot;US&quot; &quot;CA&quot; &quot;GB&quot;})</code></pre>
<p><strong>This blocks non-US/CA/GB traffic to login page.</strong></p>
<h2 id="layer-7-two-factor-authentication">Layer 7: Two-Factor Authentication</h2>
<p>Combine 2FA with login limits for ultimate protection:</p>
<div class="sourceCode" id="cb13">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="ex">wp</span> plugin install two-factor --activate</span></code></pre>
</div>
<p>Even if attackers guess passwords, they can’t login without the second factor.</p>
<h2 id="layer-8-disable-xml-rpc">Layer 8: Disable XML-RPC</h2>
<p>XML-RPC can be used for brute force attacks:</p>
<div class="sourceCode" id="cb14">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="co"># In .htaccess</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="fu">&lt;Files</span><span class="at"> xmlrpc.php</span><span class="fu">&gt;</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a>    <span class="ex">Order</span><span class="ch"> </span><span class="kw">deny,allow</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a>    Deny<span class="st"> from all</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a><span class="fu">&lt;/Files&gt;</span></span></code></pre>
</div>
<p><strong>Or via plugin code:</strong></p>
<div class="sourceCode" id="cb15">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="co">// Completely disable XML-RPC</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a>add_filter<span class="ot">(</span><span class="st">&#39;xmlrpc_enabled&#39;</span><span class="ot">,</span> <span class="st">&#39;__return_false&#39;</span><span class="ot">);</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a><span class="co">// Or disable only certain methods</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a>add_filter<span class="ot">(</span><span class="st">&#39;xmlrpc_methods&#39;</span><span class="ot">,</span> <span class="st">&#39;disable_xmlrpc_methods&#39;</span><span class="ot">);</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a><span class="kw">function</span> disable_xmlrpc_methods<span class="ot">(</span><span class="kw">$methods</span><span class="ot">)</span> {</span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a>    <span class="kw">unset</span><span class="ot">(</span><span class="kw">$methods</span><span class="ot">[</span><span class="st">&#39;wp.getUsersBlogs&#39;</span><span class="ot">]);</span></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a>    <span class="kw">unset</span><span class="ot">(</span><span class="kw">$methods</span><span class="ot">[</span><span class="st">&#39;system.multicall&#39;</span><span class="ot">]);</span></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true"></a>    <span class="kw">unset</span><span class="ot">(</span><span class="kw">$methods</span><span class="ot">[</span><span class="st">&#39;system.listMethods&#39;</span><span class="ot">]);</span></span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">$methods</span><span class="ot">;</span></span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="layer-9-server-level-protection-fail2ban">Layer 9: Server-Level Protection (Fail2Ban)</h2>
<h3 id="configure-fail2ban-for-wordpress">Configure Fail2Ban for WordPress</h3>
<div class="sourceCode" id="cb16">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="co"># Install Fail2Ban (Ubuntu/Debian)</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a><span class="fu">sudo</span> apt-get install fail2ban</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true"></a><span class="co"># Create WordPress filter</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true"></a><span class="fu">sudo</span> nano /etc/fail2ban/filter.d/wordpress.conf</span></code></pre>
</div>
<p><strong>Filter Content:</strong></p>
<div class="sourceCode" id="cb17">
<pre class="sourceCode ini"><code class="sourceCode ini"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a><span class="kw">[Definition]</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true"></a><span class="dt">failregex </span><span class="ot">=</span><span class="st"> ^&lt;HOST&gt; .* &quot;POST /wp-login.php</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true"></a><span class="dt">            ^&lt;HOST&gt; .* &quot;POST /xmlrpc.php</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true"></a><span class="dt">ignoreregex </span><span class="ot">=</span></span></code></pre>
</div>
<p><strong>Jail Configuration:</strong></p>
<div class="sourceCode" id="cb18">
<pre class="sourceCode ini"><code class="sourceCode ini"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a><span class="co"># In /etc/fail2ban/jail.local</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true"></a><span class="kw">[wordpress]</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true"></a><span class="dt">enabled </span><span class="ot">=</span><span class="st"> </span><span class="kw">true</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true"></a><span class="dt">port </span><span class="ot">=</span><span class="st"> http,https</span></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true"></a><span class="dt">filter </span><span class="ot">=</span><span class="st"> wordpress</span></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true"></a><span class="dt">logpath </span><span class="ot">=</span><span class="st"> /var/log/apache2/access.log</span></span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true"></a><span class="dt">maxretry </span><span class="ot">=</span><span class="st"> </span><span class="dv">5</span></span>
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true"></a><span class="dt">bantime </span><span class="ot">=</span><span class="st"> </span><span class="dv">3600</span></span>
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true"></a><span class="dt">findtime </span><span class="ot">=</span><span class="st"> </span><span class="dv">600</span></span></code></pre>
</div>
<p>Restart Fail2Ban:</p>
<div class="sourceCode" id="cb19">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a><span class="fu">sudo</span> systemctl restart fail2ban</span></code></pre>
</div>
<h2 id="monitoring-and-alerts">Monitoring and Alerts</h2>
<h3 id="email-notifications">Email Notifications</h3>
<div class="sourceCode" id="cb20">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true"></a><span class="co">// Email admin on failed logins</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true"></a>add_action<span class="ot">(</span><span class="st">&#39;wp_login_failed&#39;</span><span class="ot">,</span> <span class="st">&#39;notify_failed_login&#39;</span><span class="ot">);</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true"></a></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true"></a><span class="kw">function</span> notify_failed_login<span class="ot">(</span><span class="kw">$username</span><span class="ot">)</span> {</span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true"></a>    <span class="kw">$ip</span> = <span class="kw">$_SERVER</span><span class="ot">[</span><span class="st">&#39;REMOTE_ADDR&#39;</span><span class="ot">];</span></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true"></a>    <span class="kw">$time</span> = current_time<span class="ot">(</span><span class="st">&#39;mysql&#39;</span><span class="ot">);</span></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true"></a></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true"></a>    wp_mail<span class="ot">(</span></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true"></a>        get_option<span class="ot">(</span><span class="st">&#39;admin_email&#39;</span><span class="ot">),</span></span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true"></a>        <span class="st">&#39;Failed Login Attempt&#39;</span><span class="ot">,</span></span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true"></a>        <span class="st">&quot;Failed login for username: </span><span class="kw">$username\n</span><span class="st">IP: </span><span class="kw">$ip\n</span><span class="st">Time: </span><span class="kw">$time</span><span class="st">&quot;</span></span>
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true"></a>    <span class="ot">);</span></span>
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true"></a>}</span></code></pre>
</div>
<h3 id="log-analysis">Log Analysis</h3>
<div class="sourceCode" id="cb21">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true"></a><span class="co"># View recent failed logins (Apache)</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true"></a><span class="fu">grep</span> <span class="st">&quot;wp-login.php&quot;</span> /var/log/apache2/access.log <span class="kw">|</span> <span class="fu">grep</span> <span class="st">&quot;POST&quot;</span> <span class="kw">|</span> <span class="fu">tail</span> -50</span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true"></a></span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true"></a><span class="co"># Count attempts by IP</span></span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true"></a><span class="fu">awk</span> <span class="st">&#39;{print $1}&#39;</span> /var/log/apache2/access.log <span class="kw">|</span> <span class="fu">grep</span> -v <span class="st">&quot;^$&quot;</span> <span class="kw">|</span> <span class="fu">sort</span> <span class="kw">|</span> <span class="fu">uniq</span> -c <span class="kw">|</span> <span class="fu">sort</span> -rn <span class="kw">|</span> <span class="fu">head</span> -20</span></code></pre>
</div>
<h2 id="testing-your-protection">Testing Your Protection</h2>
<h3 id="safe-simulation">Safe Simulation</h3>
<ol type="1">
<li>Use incognito browser</li>
<li>Attempt 5-6 failed logins</li>
<li>Verify lockout occurs</li>
<li>Check email notifications</li>
<li>Confirm CAPTCHA appears (if configured)</li>
<li>Test IP whitelist (from different IP)</li>
</ol>
<p><strong>Automated Test:</strong></p>
<div class="sourceCode" id="cb22">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true"></a><span class="co"># Test with curl (safely)</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true"></a><span class="kw">for</span> <span class="ex">i</span> in <span class="dt">{1..6}</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true"></a>    <span class="ex">curl</span> -X POST https://yoursite.com/wp-login.php <span class="kw">\</span></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true"></a>    <span class="ex">-d</span> <span class="st">&quot;log=testuser&amp;pwd=wrongpassword&quot;</span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true"></a>    <span class="fu">sleep</span> 2</span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true"></a><span class="kw">done</span></span></code></pre>
</div>
<h2 id="best-practices-summary">Best Practices Summary</h2>
<p>✅ <strong>Do:</strong></p>
<ul>
<li>Limit login attempts (4-5 max)</li>
<li>Add CAPTCHA protection</li>
<li>Use strong, unique passwords</li>
<li>Enable 2FA for all admins</li>
<li>Monitor failed login logs</li>
<li>Update security plugins regularly</li>
</ul>
<p>❌ <strong>Don’t:</strong></p>
<ul>
<li>Use “admin” as username</li>
<li>Allow unlimited login attempts</li>
<li>Ignore failed login notifications</li>
<li>Use same password across accounts</li>
<li>Disable security features for convenience</li>
</ul>
<h2 id="complete-protection-stack">Complete Protection Stack</h2>
<p><strong>Recommended combination:</strong></p>
<ol type="1">
<li><strong>Strong passwords</strong> (20+ characters)</li>
<li><strong>Limit Login Attempts Reloaded</strong> (free)</li>
<li><strong>Google reCAPTCHA v3</strong> (free)</li>
<li><strong>Wordfence</strong> or <strong>Sucuri</strong> (free/premium)</li>
<li><strong>Two-Factor Authentication</strong> (free)</li>
<li><strong>Cloudflare</strong> (free tier sufficient)</li>
<li><strong>Fail2Ban</strong> (server-level, free)</li>
</ol>
<p>This multi-layered approach stops 99.9% of brute force attacks while maintaining usability for legitimate users.</p>
<p>Brute force attacks are preventable. By implementing these protective layers, you transform WordPress login from a vulnerable entry point into a hardened fortress. Start with basic login limits and CAPTCHA, then add additional layers based on your threat level and resources.</p>
<h2 id="external-links">External Links</h2>
<ol type="1">
<li><a href="https://wordpress.org/plugins/limit-login-attempts-reloaded/">Limit Login Attempts Reloaded</a></li>
<li><a href="https://www.google.com/recaptcha/">Google reCAPTCHA</a></li>
<li><a href="https://wordpress.org/plugins/wordfence/">Wordfence Security</a></li>
<li><a href="https://www.cloudflare.com/">Cloudflare</a></li>
<li><a href="https://www.fail2ban.org/">Fail2Ban</a></li>
</ol>
<h2 id="call-to-action">Call to Action</h2>
<p>Secure your site with bulletproof backups! <a href="https://backupcopilotplugin.com/">Backup Copilot Pro</a> offers automated security audits, malware scanning before backups, and instant recovery—try it free!</p>
<p>The post <a href="https://developryplugins.com/how-to-block-brute-force-attacks-on-wordpress-login-pages/">How to Block Brute Force Attacks on WordPress Login Pages</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Preventing SQL Injection in WordPress: Security Best Practices</title>
		<link>https://developryplugins.com/preventing-sql-injection-in-wordpress-security-best-practices/</link>
		
		<dc:creator><![CDATA[Krasen Slavov]]></dc:creator>
		<pubDate>Tue, 05 May 2026 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Security & Protection]]></category>
		<category><![CDATA[database security]]></category>
		<category><![CDATA[prepared statements]]></category>
		<category><![CDATA[sql injection]]></category>
		<category><![CDATA[wordpress security]]></category>
		<category><![CDATA[wpdb]]></category>
		<guid isPermaLink="false">https://developryplugins.com/?p=151</guid>

					<description><![CDATA[<p>SQL injection remains one of the most dangerous web vulnerabilities, capable of compromising entire databases, stealing user data, and granting attackers administrative access. In WordPress development, improper database queries are...</p>
<p>The post <a href="https://developryplugins.com/preventing-sql-injection-in-wordpress-security-best-practices/">Preventing SQL Injection in WordPress: Security Best Practices</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- @format --></p>
<p>SQL injection remains one of the most dangerous web vulnerabilities, capable of compromising entire databases, stealing user data, and granting attackers administrative access. In WordPress development, improper database queries are a leading cause of plugin and theme vulnerabilities.</p>
<p>This comprehensive guide teaches WordPress developers how to write secure database queries using prepared statements, proper escaping, and validation techniques to prevent SQL injection attacks.</p>
<h2 id="understanding-sql-injection">Understanding SQL Injection</h2>
<h3 id="what-is-sql-injection">What Is SQL Injection?</h3>
<p>SQL injection occurs when user input is inserted directly into SQL queries without proper sanitization, allowing attackers to manipulate the query logic.</p>
<p><strong>Vulnerable Code Example:</strong></p>
<div class="sourceCode" id="cb1">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co">// DANGEROUS - NEVER DO THIS</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="kw">$user_id</span> = <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;user_id&#39;</span><span class="ot">];</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span><span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;users}</span><span class="st"> WHERE ID = </span><span class="kw">$user_id</span><span class="st">&quot;</span><span class="ot">);</span></span></code></pre>
</div>
<p><strong>Attack:</strong></p>
<pre><code>?user_id=1 OR 1=1</code></pre>
<p>This would return ALL users instead of just one.</p>
<h3 id="real-world-impact">Real-World Impact</h3>
<p><strong>Data Theft:</strong></p>
<ul>
<li>Extract entire user database</li>
<li>Steal password hashes</li>
<li>Access sensitive customer information</li>
</ul>
<p><strong>Privilege Escalation:</strong></p>
<ul>
<li>Modify user roles to admin</li>
<li>Create backdoor admin accounts</li>
</ul>
<p><strong>Site Compromise:</strong></p>
<ul>
<li>Delete database tables</li>
<li>Inject malicious content</li>
<li>Modify wp-options for persistent access</li>
</ul>
<h2 id="the-wordpress-wpdb-class">The WordPress $wpdb Class</h2>
<p>WordPress provides the <code>$wpdb</code> global object for all database interactions. Using it correctly prevents SQL injection.</p>
<h3 id="core-principles">Core Principles</h3>
<p><strong>1. NEVER concatenate user input into queries</strong> <strong>2. ALWAYS use prepared statements</strong> <strong>3. VALIDATE input before database operations</strong> <strong>4. ESCAPE output when displaying data</strong></p>
<h2 id="using-wpdb-prepare-correctly">Using $wpdb-&gt;prepare() Correctly</h2>
<h3 id="prepared-statement-syntax">Prepared Statement Syntax</h3>
<div class="sourceCode" id="cb3">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="kw">global</span> <span class="kw">$wpdb</span><span class="ot">;</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a><span class="co">// Secure query using prepared statement</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a><span class="kw">$user_id</span> = <span class="fu">intval</span><span class="ot">(</span><span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;user_id&#39;</span><span class="ot">]);</span> <span class="co">// Always validate first</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;users}</span><span class="st"> WHERE ID = %d&quot;</span><span class="ot">,</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a>        <span class="kw">$user_id</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h3 id="placeholders-explained">Placeholders Explained</h3>
<p><strong>%d &#8211; Integer placeholder</strong></p>
<div class="sourceCode" id="cb4">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="kw">$post_id</span> = <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;post_id&#39;</span><span class="ot">];</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="kw">$post</span> = <span class="kw">$wpdb</span>-&gt;get_row<span class="ot">(</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE ID = %d&quot;</span><span class="ot">,</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a>        <span class="kw">$post_id</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p><strong>%s &#8211; String placeholder</strong></p>
<div class="sourceCode" id="cb5">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="kw">$username</span> = <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;username&#39;</span><span class="ot">];</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a><span class="kw">$user</span> = <span class="kw">$wpdb</span>-&gt;get_row<span class="ot">(</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;users}</span><span class="st"> WHERE user_login = %s&quot;</span><span class="ot">,</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a>        <span class="kw">$username</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p><strong>%f &#8211; Float placeholder</strong></p>
<div class="sourceCode" id="cb6">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="kw">$price</span> = <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;price&#39;</span><span class="ot">];</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="kw">$products</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;prefix}</span><span class="st">products WHERE price &gt;= %f&quot;</span><span class="ot">,</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a>        <span class="kw">$price</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h3 id="common-prepare-mistakes">Common prepare() Mistakes</h3>
<p><strong>❌ WRONG: Preparing table/column names</strong></p>
<div class="sourceCode" id="cb7">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="co">// This doesn&#39;t work - prepare() doesn&#39;t escape identifiers</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="kw">$table</span> = <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;table&#39;</span><span class="ot">];</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span><span class="st">&quot;SELECT * FROM %s&quot;</span><span class="ot">,</span> <span class="kw">$table</span><span class="ot">)</span> <span class="co">// UNSAFE!</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p><strong>✅ CORRECT: Whitelist approach</strong></p>
<div class="sourceCode" id="cb8">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="kw">$allowed_tables</span> = <span class="ot">[</span><span class="st">&#39;posts&#39;</span><span class="ot">,</span> <span class="st">&#39;users&#39;</span><span class="ot">,</span> <span class="st">&#39;comments&#39;</span><span class="ot">];</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="kw">$table</span> = <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;table&#39;</span><span class="ot">];</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span>!<span class="fu">in_array</span><span class="ot">(</span><span class="kw">$table</span><span class="ot">,</span> <span class="kw">$allowed_tables</span><span class="ot">))</span> {</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>    wp_die<span class="ot">(</span><span class="st">&#39;Invalid table&#39;</span><span class="ot">);</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a>}</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a><span class="co">// Now safe to use</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span><span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;prefix}{$table}</span><span class="st">&quot;</span><span class="ot">);</span></span></code></pre>
</div>
<p><strong>❌ WRONG: Using prepare() with already concatenated strings</strong></p>
<div class="sourceCode" id="cb9">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="kw">$sql</span> = <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE post_author = &quot;</span> . <span class="kw">$author_id</span><span class="ot">;</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span><span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span><span class="kw">$sql</span><span class="ot">));</span> <span class="co">// Too late!</span></span></code></pre>
</div>
<h2 id="safe-database-operations">Safe Database Operations</h2>
<h3 id="insert-operations">INSERT Operations</h3>
<p><strong>Using $wpdb-&gt;insert():</strong></p>
<div class="sourceCode" id="cb10">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="co">// Secure insert method</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;insert<span class="ot">(</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prefix . <span class="st">&#39;custom_table&#39;</span><span class="ot">,</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a>    <span class="ot">[</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a>        <span class="st">&#39;user_id&#39;</span>    =&gt; absint<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;user_id&#39;</span><span class="ot">]),</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a>        <span class="st">&#39;title&#39;</span>      =&gt; sanitize_text_field<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;title&#39;</span><span class="ot">]),</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a>        <span class="st">&#39;content&#39;</span>    =&gt; wp_kses_post<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;content&#39;</span><span class="ot">]),</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a>        <span class="st">&#39;created_at&#39;</span> =&gt; current_time<span class="ot">(</span><span class="st">&#39;mysql&#39;</span><span class="ot">)</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a>    <span class="ot">],</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a>    <span class="ot">[</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a>        <span class="st">&#39;%d&#39;</span><span class="ot">,</span> <span class="co">// user_id format</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a>        <span class="st">&#39;%s&#39;</span><span class="ot">,</span> <span class="co">// title format</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true"></a>        <span class="st">&#39;%s&#39;</span><span class="ot">,</span> <span class="co">// content format</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true"></a>        <span class="st">&#39;%s&#39;</span>  <span class="co">// created_at format</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true"></a>    <span class="ot">]</span></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true"></a></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span><span class="kw">$wpdb</span>-&gt;insert_id<span class="ot">)</span> {</span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true"></a>    <span class="kw">echo</span> <span class="st">&quot;Inserted with ID: &quot;</span> . <span class="kw">$wpdb</span>-&gt;insert_id<span class="ot">;</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true"></a>}</span></code></pre>
</div>
<h3 id="update-operations">UPDATE Operations</h3>
<p><strong>Using $wpdb-&gt;update():</strong></p>
<div class="sourceCode" id="cb11">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="kw">$updated</span> = <span class="kw">$wpdb</span>-&gt;update<span class="ot">(</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prefix . <span class="st">&#39;custom_table&#39;</span><span class="ot">,</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a>    <span class="ot">[</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a>        <span class="st">&#39;status&#39;</span> =&gt; sanitize_text_field<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;status&#39;</span><span class="ot">]),</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a>        <span class="st">&#39;updated_at&#39;</span> =&gt; current_time<span class="ot">(</span><span class="st">&#39;mysql&#39;</span><span class="ot">)</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a>    <span class="ot">],</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a>    <span class="ot">[</span> <span class="st">&#39;id&#39;</span> =&gt; absint<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;id&#39;</span><span class="ot">])</span> <span class="ot">],</span> <span class="co">// WHERE clause</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a>    <span class="ot">[</span> <span class="st">&#39;%s&#39;</span><span class="ot">,</span> <span class="st">&#39;%s&#39;</span> <span class="ot">],</span> <span class="co">// Format for UPDATE data</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true"></a>    <span class="ot">[</span> <span class="st">&#39;%d&#39;</span> <span class="ot">]</span>        <span class="co">// Format for WHERE clause</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true"></a></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span><span class="kw">false</span> === <span class="kw">$updated</span><span class="ot">)</span> {</span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true"></a>    <span class="co">// Error occurred</span></span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true"></a>    <span class="fu">error_log</span><span class="ot">(</span><span class="kw">$wpdb</span>-&gt;last_error<span class="ot">);</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true"></a>}</span></code></pre>
</div>
<h3 id="delete-operations">DELETE Operations</h3>
<p><strong>Using $wpdb-&gt;delete():</strong></p>
<div class="sourceCode" id="cb12">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="kw">$deleted</span> = <span class="kw">$wpdb</span>-&gt;delete<span class="ot">(</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prefix . <span class="st">&#39;custom_table&#39;</span><span class="ot">,</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a>    <span class="ot">[</span> <span class="st">&#39;id&#39;</span> =&gt; absint<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;id&#39;</span><span class="ot">])</span> <span class="ot">],</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a>    <span class="ot">[</span> <span class="st">&#39;%d&#39;</span> <span class="ot">]</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span><span class="kw">$deleted</span><span class="ot">)</span> {</span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true"></a>    <span class="kw">echo</span> <span class="st">&quot;Deleted </span><span class="kw">$deleted</span><span class="st"> row(s)&quot;</span><span class="ot">;</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true"></a>}</span></code></pre>
</div>
<h3 id="select-queries">SELECT Queries</h3>
<p><strong>Using prepare() with get_results():</strong></p>
<div class="sourceCode" id="cb13">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="co">// Multiple results</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a><span class="kw">$posts</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true"></a><span class="st">        WHERE post_type = %s</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true"></a><span class="st">        AND post_status = %s</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true"></a><span class="st">        ORDER BY post_date DESC</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true"></a><span class="st">        LIMIT %d&quot;</span><span class="ot">,</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true"></a>        <span class="st">&#39;post&#39;</span><span class="ot">,</span></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true"></a>        <span class="st">&#39;publish&#39;</span><span class="ot">,</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true"></a>        <span class="dv">10</span></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true"></a></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true"></a><span class="co">// Single row</span></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true"></a><span class="kw">$post</span> = <span class="kw">$wpdb</span>-&gt;get_row<span class="ot">(</span></span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE ID = %d&quot;</span><span class="ot">,</span></span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true"></a>        <span class="kw">$post_id</span></span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true"></a></span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true"></a><span class="co">// Single variable</span></span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true"></a><span class="kw">$count</span> = <span class="kw">$wpdb</span>-&gt;get_var<span class="ot">(</span></span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true"></a>        <span class="st">&quot;SELECT COUNT(*) FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE post_author = %d&quot;</span><span class="ot">,</span></span>
<span id="cb13-27"><a href="#cb13-27" aria-hidden="true"></a>        <span class="kw">$author_id</span></span>
<span id="cb13-28"><a href="#cb13-28" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb13-29"><a href="#cb13-29" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb13-30"><a href="#cb13-30" aria-hidden="true"></a></span>
<span id="cb13-31"><a href="#cb13-31" aria-hidden="true"></a><span class="co">// Single column</span></span>
<span id="cb13-32"><a href="#cb13-32" aria-hidden="true"></a><span class="kw">$post_ids</span> = <span class="kw">$wpdb</span>-&gt;get_col<span class="ot">(</span></span>
<span id="cb13-33"><a href="#cb13-33" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb13-34"><a href="#cb13-34" aria-hidden="true"></a>        <span class="st">&quot;SELECT ID FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE post_type = %s&quot;</span><span class="ot">,</span></span>
<span id="cb13-35"><a href="#cb13-35" aria-hidden="true"></a>        <span class="st">&#39;page&#39;</span></span>
<span id="cb13-36"><a href="#cb13-36" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb13-37"><a href="#cb13-37" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h2 id="input-validation-and-sanitization">Input Validation and Sanitization</h2>
<h3 id="validate-before-database">Validate Before Database</h3>
<div class="sourceCode" id="cb14">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="co">// Validate integer</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="kw">$user_id</span> = absint<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;user_id&#39;</span><span class="ot">]);</span> <span class="co">// Forces positive integer</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a><span class="co">// Validate email</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a><span class="kw">$email</span> = sanitize_email<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;email&#39;</span><span class="ot">]);</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span>!is_email<span class="ot">(</span><span class="kw">$email</span><span class="ot">))</span> {</span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true"></a>    wp_die<span class="ot">(</span><span class="st">&#39;Invalid email address&#39;</span><span class="ot">);</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true"></a>}</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true"></a></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true"></a><span class="co">// Validate URL</span></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true"></a><span class="kw">$url</span> = esc_url_raw<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;url&#39;</span><span class="ot">]);</span></span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true"></a></span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true"></a><span class="co">// Sanitize text field</span></span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true"></a><span class="kw">$title</span> = sanitize_text_field<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;title&#39;</span><span class="ot">]);</span></span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true"></a></span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true"></a><span class="co">// Sanitize textarea (allows newlines)</span></span>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true"></a><span class="kw">$description</span> = sanitize_textarea_field<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;description&#39;</span><span class="ot">]);</span></span>
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true"></a></span>
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true"></a><span class="co">// HTML content (allows safe HTML)</span></span>
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true"></a><span class="kw">$content</span> = wp_kses_post<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;content&#39;</span><span class="ot">]);</span></span>
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true"></a></span>
<span id="cb14-22"><a href="#cb14-22" aria-hidden="true"></a><span class="co">// Alphanumeric only</span></span>
<span id="cb14-23"><a href="#cb14-23" aria-hidden="true"></a><span class="kw">$slug</span> = sanitize_key<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;slug&#39;</span><span class="ot">]);</span></span></code></pre>
</div>
<h3 id="custom-validation-functions">Custom Validation Functions</h3>
<div class="sourceCode" id="cb15">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="kw">function</span> validate_username<span class="ot">(</span><span class="kw">$username</span><span class="ot">)</span> {</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a>    <span class="co">// Only letters, numbers, underscores, hyphens</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span>!<span class="fu">preg_match</span><span class="ot">(</span><span class="st">&#39;/^[a-zA-Z0-9_-]+$/&#39;</span><span class="ot">,</span> <span class="kw">$username</span><span class="ot">))</span> {</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a>        <span class="kw">return</span> <span class="kw">false</span><span class="ot">;</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a>    }</span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a>    <span class="co">// Length check</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span><span class="fu">strlen</span><span class="ot">(</span><span class="kw">$username</span><span class="ot">)</span> &lt; <span class="dv">3</span> || <span class="fu">strlen</span><span class="ot">(</span><span class="kw">$username</span><span class="ot">)</span> &gt; <span class="dv">20</span><span class="ot">)</span> {</span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a>        <span class="kw">return</span> <span class="kw">false</span><span class="ot">;</span></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true"></a>    }</span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true"></a></span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true"></a>    <span class="kw">return</span> sanitize_user<span class="ot">(</span><span class="kw">$username</span><span class="ot">);</span></span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true"></a>}</span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true"></a></span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true"></a><span class="co">// Usage</span></span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true"></a><span class="kw">$username</span> = validate_username<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;username&#39;</span><span class="ot">]);</span></span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span>!<span class="kw">$username</span><span class="ot">)</span> {</span>
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true"></a>    wp_die<span class="ot">(</span><span class="st">&#39;Invalid username format&#39;</span><span class="ot">);</span></span>
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="like-queries-and-wildcards">LIKE Queries and Wildcards</h2>
<h3 id="using-wpdb-esc_like">Using $wpdb-&gt;esc_like()</h3>
<div class="sourceCode" id="cb16">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="co">// Search with LIKE query</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a><span class="kw">$search_term</span> = <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;search&#39;</span><span class="ot">];</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true"></a><span class="st">        WHERE post_title LIKE %s&quot;</span><span class="ot">,</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true"></a>        <span class="st">&#39;%&#39;</span> . <span class="kw">$wpdb</span>-&gt;esc_like<span class="ot">(</span><span class="kw">$search_term</span><span class="ot">)</span> . <span class="st">&#39;%&#39;</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p><strong>Why esc_like() is necessary:</strong></p>
<p>Without it, users could inject wildcards:</p>
<div class="sourceCode" id="cb17">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a><span class="co">// Malicious input: %</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true"></a><span class="co">// Would match everything</span></span></code></pre>
</div>
<h2 id="handling-dynamic-order-by">Handling Dynamic ORDER BY</h2>
<h3 id="whitelist-approach">Whitelist Approach</h3>
<div class="sourceCode" id="cb18">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a><span class="co">// UNSAFE - Never do this</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true"></a><span class="kw">$orderby</span> = <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;orderby&#39;</span><span class="ot">];</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true"></a><span class="kw">$sql</span> = <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> ORDER BY </span><span class="kw">$orderby</span><span class="st">&quot;</span><span class="ot">;</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true"></a></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true"></a><span class="co">// SAFE - Whitelist allowed columns</span></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true"></a><span class="kw">$orderby</span> = <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;orderby&#39;</span><span class="ot">];</span></span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true"></a><span class="kw">$allowed_orderby</span> = <span class="ot">[</span></span>
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true"></a>    <span class="st">&#39;title&#39;</span> =&gt; <span class="st">&#39;post_title&#39;</span><span class="ot">,</span></span>
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true"></a>    <span class="st">&#39;date&#39;</span>  =&gt; <span class="st">&#39;post_date&#39;</span><span class="ot">,</span></span>
<span id="cb18-10"><a href="#cb18-10" aria-hidden="true"></a>    <span class="st">&#39;author&#39;</span> =&gt; <span class="st">&#39;post_author&#39;</span></span>
<span id="cb18-11"><a href="#cb18-11" aria-hidden="true"></a><span class="ot">];</span></span>
<span id="cb18-12"><a href="#cb18-12" aria-hidden="true"></a></span>
<span id="cb18-13"><a href="#cb18-13" aria-hidden="true"></a><span class="kw">$orderby_column</span> = <span class="kw">isset</span><span class="ot">(</span><span class="kw">$allowed_orderby</span><span class="ot">[</span><span class="kw">$orderby</span><span class="ot">])</span></span>
<span id="cb18-14"><a href="#cb18-14" aria-hidden="true"></a>    <span class="ot">?</span> <span class="kw">$allowed_orderby</span><span class="ot">[</span><span class="kw">$orderby</span><span class="ot">]</span></span>
<span id="cb18-15"><a href="#cb18-15" aria-hidden="true"></a>    <span class="ot">:</span> <span class="st">&#39;post_date&#39;</span><span class="ot">;</span></span>
<span id="cb18-16"><a href="#cb18-16" aria-hidden="true"></a></span>
<span id="cb18-17"><a href="#cb18-17" aria-hidden="true"></a><span class="kw">$order</span> = <span class="ot">(</span><span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;order&#39;</span><span class="ot">]</span> === <span class="st">&#39;ASC&#39;</span><span class="ot">)</span> <span class="ot">?</span> <span class="st">&#39;ASC&#39;</span> <span class="ot">:</span> <span class="st">&#39;DESC&#39;</span><span class="ot">;</span></span>
<span id="cb18-18"><a href="#cb18-18" aria-hidden="true"></a></span>
<span id="cb18-19"><a href="#cb18-19" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb18-20"><a href="#cb18-20" aria-hidden="true"></a>    <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span></span>
<span id="cb18-21"><a href="#cb18-21" aria-hidden="true"></a><span class="st">    ORDER BY </span><span class="kw">{$orderby_column}</span><span class="st"> </span><span class="kw">{$order}</span><span class="st">&quot;</span></span>
<span id="cb18-22"><a href="#cb18-22" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h2 id="in-clause-with-multiple-values">IN Clause with Multiple Values</h2>
<h3 id="safe-array-handling">Safe Array Handling</h3>
<div class="sourceCode" id="cb19">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a><span class="co">// Get posts with specific IDs</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true"></a><span class="kw">$post_ids</span> = <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;post_ids&#39;</span><span class="ot">];</span> <span class="co">// Array of IDs</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true"></a></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true"></a><span class="co">// Sanitize each ID</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true"></a><span class="kw">$post_ids</span> = <span class="fu">array_map</span><span class="ot">(</span><span class="st">&#39;absint&#39;</span><span class="ot">,</span> <span class="kw">$post_ids</span><span class="ot">);</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true"></a></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true"></a><span class="co">// Create placeholders</span></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true"></a><span class="kw">$placeholders</span> = <span class="fu">implode</span><span class="ot">(</span><span class="st">&#39;,&#39;</span><span class="ot">,</span> <span class="fu">array_fill</span><span class="ot">(</span><span class="dv">0</span><span class="ot">,</span> <span class="fu">count</span><span class="ot">(</span><span class="kw">$post_ids</span><span class="ot">),</span> <span class="st">&#39;%d&#39;</span><span class="ot">));</span></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true"></a></span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb19-12"><a href="#cb19-12" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE ID IN (</span><span class="kw">$placeholders</span><span class="st">)&quot;</span><span class="ot">,</span></span>
<span id="cb19-13"><a href="#cb19-13" aria-hidden="true"></a>        <span class="kw">$post_ids</span></span>
<span id="cb19-14"><a href="#cb19-14" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb19-15"><a href="#cb19-15" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p><strong>For string arrays:</strong></p>
<div class="sourceCode" id="cb20">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true"></a><span class="kw">$categories</span> = <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;categories&#39;</span><span class="ot">];</span> <span class="co">// Array of strings</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true"></a></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true"></a><span class="co">// Sanitize</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true"></a><span class="kw">$categories</span> = <span class="fu">array_map</span><span class="ot">(</span><span class="st">&#39;sanitize_text_field&#39;</span><span class="ot">,</span> <span class="kw">$categories</span><span class="ot">);</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true"></a></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true"></a><span class="co">// Placeholders</span></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true"></a><span class="kw">$placeholders</span> = <span class="fu">implode</span><span class="ot">(</span><span class="st">&#39;,&#39;</span><span class="ot">,</span> <span class="fu">array_fill</span><span class="ot">(</span><span class="dv">0</span><span class="ot">,</span> <span class="fu">count</span><span class="ot">(</span><span class="kw">$categories</span><span class="ot">),</span> <span class="st">&#39;%s&#39;</span><span class="ot">));</span></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true"></a></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;terms}</span><span class="st"> WHERE slug IN (</span><span class="kw">$placeholders</span><span class="st">)&quot;</span><span class="ot">,</span></span>
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true"></a>        <span class="kw">$categories</span></span>
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb20-14"><a href="#cb20-14" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h2 id="using-wordpress-core-functions">Using WordPress Core Functions</h2>
<h3 id="prefer-wp-functions-over-raw-sql">Prefer WP Functions Over Raw SQL</h3>
<p><strong>Instead of raw queries, use:</strong></p>
<div class="sourceCode" id="cb21">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true"></a><span class="co">// Get posts</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true"></a><span class="kw">$posts</span> = get_posts<span class="ot">([</span></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true"></a>    <span class="st">&#39;post_type&#39;</span>   =&gt; <span class="st">&#39;product&#39;</span><span class="ot">,</span></span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true"></a>    <span class="st">&#39;post_status&#39;</span> =&gt; <span class="st">&#39;publish&#39;</span><span class="ot">,</span></span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true"></a>    <span class="st">&#39;author&#39;</span>      =&gt; <span class="kw">$author_id</span><span class="ot">,</span></span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true"></a>    <span class="st">&#39;numberposts&#39;</span> =&gt; <span class="dv">10</span></span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true"></a><span class="ot">]);</span></span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true"></a></span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true"></a><span class="co">// Get users</span></span>
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true"></a><span class="kw">$users</span> = get_users<span class="ot">([</span></span>
<span id="cb21-11"><a href="#cb21-11" aria-hidden="true"></a>    <span class="st">&#39;role&#39;</span>    =&gt; <span class="st">&#39;subscriber&#39;</span><span class="ot">,</span></span>
<span id="cb21-12"><a href="#cb21-12" aria-hidden="true"></a>    <span class="st">&#39;orderby&#39;</span> =&gt; <span class="st">&#39;registered&#39;</span><span class="ot">,</span></span>
<span id="cb21-13"><a href="#cb21-13" aria-hidden="true"></a>    <span class="st">&#39;number&#39;</span>  =&gt; <span class="dv">50</span></span>
<span id="cb21-14"><a href="#cb21-14" aria-hidden="true"></a><span class="ot">]);</span></span>
<span id="cb21-15"><a href="#cb21-15" aria-hidden="true"></a></span>
<span id="cb21-16"><a href="#cb21-16" aria-hidden="true"></a><span class="co">// Get post meta</span></span>
<span id="cb21-17"><a href="#cb21-17" aria-hidden="true"></a><span class="kw">$value</span> = get_post_meta<span class="ot">(</span><span class="kw">$post_id</span><span class="ot">,</span> <span class="st">&#39;custom_field&#39;</span><span class="ot">,</span> <span class="kw">true</span><span class="ot">);</span></span>
<span id="cb21-18"><a href="#cb21-18" aria-hidden="true"></a></span>
<span id="cb21-19"><a href="#cb21-19" aria-hidden="true"></a><span class="co">// WP_Query for complex queries</span></span>
<span id="cb21-20"><a href="#cb21-20" aria-hidden="true"></a><span class="kw">$query</span> = <span class="kw">new</span> WP_Query<span class="ot">([</span></span>
<span id="cb21-21"><a href="#cb21-21" aria-hidden="true"></a>    <span class="st">&#39;post_type&#39;</span> =&gt; <span class="st">&#39;post&#39;</span><span class="ot">,</span></span>
<span id="cb21-22"><a href="#cb21-22" aria-hidden="true"></a>    <span class="st">&#39;meta_query&#39;</span> =&gt; <span class="ot">[</span></span>
<span id="cb21-23"><a href="#cb21-23" aria-hidden="true"></a>        <span class="ot">[</span></span>
<span id="cb21-24"><a href="#cb21-24" aria-hidden="true"></a>            <span class="st">&#39;key&#39;</span>     =&gt; <span class="st">&#39;price&#39;</span><span class="ot">,</span></span>
<span id="cb21-25"><a href="#cb21-25" aria-hidden="true"></a>            <span class="st">&#39;value&#39;</span>   =&gt; <span class="dv">100</span><span class="ot">,</span></span>
<span id="cb21-26"><a href="#cb21-26" aria-hidden="true"></a>            <span class="st">&#39;compare&#39;</span> =&gt; <span class="st">&#39;&gt;=&#39;</span><span class="ot">,</span></span>
<span id="cb21-27"><a href="#cb21-27" aria-hidden="true"></a>            <span class="st">&#39;type&#39;</span>    =&gt; <span class="st">&#39;NUMERIC&#39;</span></span>
<span id="cb21-28"><a href="#cb21-28" aria-hidden="true"></a>        <span class="ot">]</span></span>
<span id="cb21-29"><a href="#cb21-29" aria-hidden="true"></a>    <span class="ot">]</span></span>
<span id="cb21-30"><a href="#cb21-30" aria-hidden="true"></a><span class="ot">]);</span></span></code></pre>
</div>
<h2 id="custom-table-best-practices">Custom Table Best Practices</h2>
<h3 id="creating-secure-custom-tables">Creating Secure Custom Tables</h3>
<div class="sourceCode" id="cb22">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true"></a><span class="kw">function</span> create_custom_table<span class="ot">()</span> {</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true"></a>    <span class="kw">global</span> <span class="kw">$wpdb</span><span class="ot">;</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true"></a>    <span class="kw">$table_name</span> = <span class="kw">$wpdb</span>-&gt;prefix . <span class="st">&#39;my_custom_table&#39;</span><span class="ot">;</span></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true"></a>    <span class="kw">$charset_collate</span> = <span class="kw">$wpdb</span>-&gt;get_charset_collate<span class="ot">();</span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true"></a></span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true"></a>    <span class="kw">$sql</span> = <span class="st">&quot;CREATE TABLE IF NOT EXISTS </span><span class="kw">$table_name</span><span class="st"> (</span></span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true"></a><span class="st">        id mediumint(9) NOT NULL AUTO_INCREMENT,</span></span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true"></a><span class="st">        user_id bigint(20) NOT NULL,</span></span>
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true"></a><span class="st">        title varchar(255) NOT NULL,</span></span>
<span id="cb22-10"><a href="#cb22-10" aria-hidden="true"></a><span class="st">        content longtext NOT NULL,</span></span>
<span id="cb22-11"><a href="#cb22-11" aria-hidden="true"></a><span class="st">        status varchar(20) NOT NULL,</span></span>
<span id="cb22-12"><a href="#cb22-12" aria-hidden="true"></a><span class="st">        created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,</span></span>
<span id="cb22-13"><a href="#cb22-13" aria-hidden="true"></a><span class="st">        PRIMARY KEY  (id),</span></span>
<span id="cb22-14"><a href="#cb22-14" aria-hidden="true"></a><span class="st">        KEY user_id (user_id)</span></span>
<span id="cb22-15"><a href="#cb22-15" aria-hidden="true"></a><span class="st">    ) </span><span class="kw">$charset_collate</span><span class="st">;&quot;</span><span class="ot">;</span></span>
<span id="cb22-16"><a href="#cb22-16" aria-hidden="true"></a></span>
<span id="cb22-17"><a href="#cb22-17" aria-hidden="true"></a>    <span class="kw">require_once</span><span class="ot">(</span><span class="kw">ABSPATH</span> . <span class="st">&#39;wp-admin/includes/upgrade.php&#39;</span><span class="ot">);</span></span>
<span id="cb22-18"><a href="#cb22-18" aria-hidden="true"></a>    dbDelta<span class="ot">(</span><span class="kw">$sql</span><span class="ot">);</span></span>
<span id="cb22-19"><a href="#cb22-19" aria-hidden="true"></a>}</span></code></pre>
</div>
<h3 id="querying-custom-tables-safely">Querying Custom Tables Safely</h3>
<div class="sourceCode" id="cb23">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true"></a><span class="kw">$table_name</span> = <span class="kw">$wpdb</span>-&gt;prefix . <span class="st">&#39;my_custom_table&#39;</span><span class="ot">;</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true"></a></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true"></a><span class="co">// Insert</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;insert<span class="ot">(</span></span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true"></a>    <span class="kw">$table_name</span><span class="ot">,</span></span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true"></a>    <span class="ot">[</span></span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true"></a>        <span class="st">&#39;user_id&#39;</span> =&gt; get_current_user_id<span class="ot">(),</span></span>
<span id="cb23-8"><a href="#cb23-8" aria-hidden="true"></a>        <span class="st">&#39;title&#39;</span>   =&gt; sanitize_text_field<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;title&#39;</span><span class="ot">]),</span></span>
<span id="cb23-9"><a href="#cb23-9" aria-hidden="true"></a>        <span class="st">&#39;content&#39;</span> =&gt; wp_kses_post<span class="ot">(</span><span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;content&#39;</span><span class="ot">]),</span></span>
<span id="cb23-10"><a href="#cb23-10" aria-hidden="true"></a>        <span class="st">&#39;status&#39;</span>  =&gt; <span class="st">&#39;pending&#39;</span></span>
<span id="cb23-11"><a href="#cb23-11" aria-hidden="true"></a>    <span class="ot">],</span></span>
<span id="cb23-12"><a href="#cb23-12" aria-hidden="true"></a>    <span class="ot">[</span><span class="st">&#39;%d&#39;</span><span class="ot">,</span> <span class="st">&#39;%s&#39;</span><span class="ot">,</span> <span class="st">&#39;%s&#39;</span><span class="ot">,</span> <span class="st">&#39;%s&#39;</span><span class="ot">]</span></span>
<span id="cb23-13"><a href="#cb23-13" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb23-14"><a href="#cb23-14" aria-hidden="true"></a></span>
<span id="cb23-15"><a href="#cb23-15" aria-hidden="true"></a><span class="co">// Select</span></span>
<span id="cb23-16"><a href="#cb23-16" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb23-17"><a href="#cb23-17" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb23-18"><a href="#cb23-18" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">$table_name</span><span class="st"> WHERE user_id = %d AND status = %s&quot;</span><span class="ot">,</span></span>
<span id="cb23-19"><a href="#cb23-19" aria-hidden="true"></a>        <span class="kw">$user_id</span><span class="ot">,</span></span>
<span id="cb23-20"><a href="#cb23-20" aria-hidden="true"></a>        <span class="st">&#39;approved&#39;</span></span>
<span id="cb23-21"><a href="#cb23-21" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb23-22"><a href="#cb23-22" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h2 id="testing-for-sql-injection">Testing for SQL Injection</h2>
<h3 id="manual-testing">Manual Testing</h3>
<p>Try these payloads in inputs:</p>
<pre><code>1&#39; OR &#39;1&#39;=&#39;1
1; DROP TABLE wp_posts;--
&#39; UNION SELECT NULL, NULL, NULL--
1&#39; AND 1=0 UNION ALL SELECT table_name,2,3 FROM information_schema.tables--</code></pre>
<p>If any return unexpected results or errors, you have SQL injection.</p>
<h3 id="automated-security-scanning">Automated Security Scanning</h3>
<div class="sourceCode" id="cb25">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true"></a><span class="co"># WPScan</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true"></a><span class="ex">wpscan</span> --url https://yoursite.com --enumerate vp</span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true"></a></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true"></a><span class="co"># RIPS (PHP security scanner)</span></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true"></a><span class="co"># Upload code to https://github.com/ripsscanner/rips</span></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true"></a></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true"></a><span class="co"># Psalm with security plugin</span></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true"></a><span class="ex">composer</span> require --dev vimeo/psalm psalm/plugin-wordpress</span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true"></a><span class="ex">psalm</span> --taint-analysis</span></code></pre>
</div>
<h2 id="common-vulnerable-patterns">Common Vulnerable Patterns</h2>
<h3 id="pattern-1-direct-variable-insertion">Pattern 1: Direct Variable Insertion</h3>
<div class="sourceCode" id="cb26">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true"></a><span class="co">// VULNERABLE</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true"></a><span class="kw">$id</span> = <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;id&#39;</span><span class="ot">];</span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;query<span class="ot">(</span><span class="st">&quot;DELETE FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE ID = </span><span class="kw">$id</span><span class="st">&quot;</span><span class="ot">);</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true"></a></span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true"></a><span class="co">// SECURE</span></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true"></a><span class="kw">$id</span> = absint<span class="ot">(</span><span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;id&#39;</span><span class="ot">]);</span></span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;delete<span class="ot">(</span><span class="kw">$wpdb</span>-&gt;posts<span class="ot">,</span> <span class="ot">[</span><span class="st">&#39;ID&#39;</span> =&gt; <span class="kw">$id</span><span class="ot">],</span> <span class="ot">[</span><span class="st">&#39;%d&#39;</span><span class="ot">]);</span></span></code></pre>
</div>
<h3 id="pattern-2-string-concatenation">Pattern 2: String Concatenation</h3>
<div class="sourceCode" id="cb27">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true"></a><span class="co">// VULNERABLE</span></span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true"></a><span class="kw">$search</span> = <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;search&#39;</span><span class="ot">];</span></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true"></a><span class="kw">$sql</span> = <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE post_title LIKE &#39;%&quot;</span> . <span class="kw">$search</span> . <span class="st">&quot;%&#39;&quot;</span><span class="ot">;</span></span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true"></a></span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true"></a><span class="co">// SECURE</span></span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true"></a><span class="kw">$search</span> = <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;search&#39;</span><span class="ot">];</span></span>
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb27-8"><a href="#cb27-8" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb27-9"><a href="#cb27-9" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE post_title LIKE %s&quot;</span><span class="ot">,</span></span>
<span id="cb27-10"><a href="#cb27-10" aria-hidden="true"></a>        <span class="st">&#39;%&#39;</span> . <span class="kw">$wpdb</span>-&gt;esc_like<span class="ot">(</span><span class="kw">$search</span><span class="ot">)</span> . <span class="st">&#39;%&#39;</span></span>
<span id="cb27-11"><a href="#cb27-11" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb27-12"><a href="#cb27-12" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h3 id="pattern-3-unvalidated-array-access">Pattern 3: Unvalidated Array Access</h3>
<div class="sourceCode" id="cb28">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true"></a><span class="co">// VULNERABLE</span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true"></a><span class="kw">$ids</span> = <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;ids&#39;</span><span class="ot">];</span> <span class="co">// &quot;1,2,3 OR 1=1&quot;</span></span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true"></a><span class="kw">$sql</span> = <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE ID IN (</span><span class="kw">$ids</span><span class="st">)&quot;</span><span class="ot">;</span></span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true"></a></span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true"></a><span class="co">// SECURE</span></span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true"></a><span class="kw">$ids</span> = <span class="fu">array_map</span><span class="ot">(</span><span class="st">&#39;absint&#39;</span><span class="ot">,</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;ids&#39;</span><span class="ot">]);</span></span>
<span id="cb28-7"><a href="#cb28-7" aria-hidden="true"></a><span class="kw">$placeholders</span> = <span class="fu">implode</span><span class="ot">(</span><span class="st">&#39;,&#39;</span><span class="ot">,</span> <span class="fu">array_fill</span><span class="ot">(</span><span class="dv">0</span><span class="ot">,</span> <span class="fu">count</span><span class="ot">(</span><span class="kw">$ids</span><span class="ot">),</span> <span class="st">&#39;%d&#39;</span><span class="ot">));</span></span>
<span id="cb28-8"><a href="#cb28-8" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb28-9"><a href="#cb28-9" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span><span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;posts}</span><span class="st"> WHERE ID IN (</span><span class="kw">$placeholders</span><span class="st">)&quot;</span><span class="ot">,</span> <span class="kw">$ids</span><span class="ot">)</span></span>
<span id="cb28-10"><a href="#cb28-10" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h2 id="security-checklist">Security Checklist</h2>
<p>✅ Use $wpdb-&gt;prepare() for all queries with variables ✅ Validate all user input (absint, sanitize_text_field, etc.) ✅ Use $wpdb-&gt;insert/update/delete methods ✅ Whitelist table/column names (never from user input) ✅ Use $wpdb-&gt;esc_like() for LIKE queries ✅ Prefer WordPress core functions over raw SQL ✅ Test with SQL injection payloads ✅ Regular security audits and code reviews ✅ Use static analysis tools ✅ Keep WordPress and plugins updated</p>
<p>SQL injection is preventable through disciplined coding practices. Always treat user input as hostile, validate rigorously, use prepared statements consistently, and leverage WordPress’s built-in security functions.</p>
<h2 id="external-links">External Links</h2>
<ol type="1">
<li><a href="https://owasp.org/www-community/attacks/SQL_Injection">SQL Injection (OWASP)</a></li>
<li><a href="https://developer.wordpress.org/reference/classes/wpdb/">wpdb Class Documentation</a></li>
<li><a href="https://developer.wordpress.org/plugins/security/data-validation/">Data Validation</a></li>
<li><a href="https://developer.wordpress.org/apis/security/escaping/">Prepared Statements</a></li>
<li><a href="https://developer.wordpress.org/apis/security/">WordPress Security Handbook</a></li>
</ol>
<h2 id="call-to-action">Call to Action</h2>
<p>Secure your site with bulletproof backups! <a href="https://backupcopilotplugin.com/">Backup Copilot Pro</a> offers automated security audits, malware scanning before backups, and instant recovery—try it free!</p>
<p>The post <a href="https://developryplugins.com/preventing-sql-injection-in-wordpress-security-best-practices/">Preventing SQL Injection in WordPress: Security Best Practices</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>SSL Certificate Setup for WordPress: Complete HTTPS Migration</title>
		<link>https://developryplugins.com/ssl-certificate-setup-for-wordpress-complete-https-migration/</link>
		
		<dc:creator><![CDATA[Krasen Slavov]]></dc:creator>
		<pubDate>Mon, 20 Apr 2026 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Security & Protection]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[ssl certificate]]></category>
		<category><![CDATA[ssl migration]]></category>
		<category><![CDATA[ssl setup]]></category>
		<category><![CDATA[wordpress security]]></category>
		<guid isPermaLink="false">https://developryplugins.com/?p=154</guid>

					<description><![CDATA[<p>Migrating your WordPress site from HTTP to HTTPS is no longer optional &#8211; it’s essential for security, SEO performance, and user trust. Search engines prioritize HTTPS sites in rankings, browsers...</p>
<p>The post <a href="https://developryplugins.com/ssl-certificate-setup-for-wordpress-complete-https-migration/">SSL Certificate Setup for WordPress: Complete HTTPS Migration</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- @format --></p>
<p>Migrating your WordPress site from HTTP to HTTPS is no longer optional &#8211; it’s essential for security, SEO performance, and user trust. Search engines prioritize HTTPS sites in rankings, browsers display security warnings for non-HTTPS sites, and users increasingly expect the padlock icon indicating secure connections. This comprehensive guide walks you through every step of SSL certificate installation and complete HTTPS migration.</p>
<h2 id="why-https-is-essential-for-wordpress">Why HTTPS Is Essential for WordPress</h2>
<p>Google confirmed HTTPS as a ranking signal in 2014, giving secure sites a competitive advantage in search results. While the direct ranking boost is modest, HTTPS indirectly improves SEO through reduced bounce rates and increased user engagement resulting from the trust signals browsers display.</p>
<p>Modern browsers like Chrome, Firefox, and Safari prominently mark HTTP sites as “Not Secure,” particularly on pages with form inputs. This warning discourages visitors from interacting with your site, directly impacting conversions and user experience.</p>
<p>For e-commerce sites, HTTPS is mandatory &#8211; payment processors and PCI compliance requirements demand encrypted connections protecting customer financial data. Beyond compliance, HTTPS encrypts all data transmission between browsers and servers, preventing attackers from intercepting sensitive information like passwords, personal details, and session cookies.</p>
<h2 id="understanding-ssltls-certificates">Understanding SSL/TLS Certificates</h2>
<p>SSL (Secure Sockets Layer) and its successor TLS (Transport Layer Security) certificates enable encrypted HTTPS connections. While technically distinct, the terms are often used interchangeably, with modern implementations exclusively using TLS.</p>
<p>Domain Validation (DV) certificates provide basic encryption and verification that you control the domain. These certificates issue quickly, often within minutes, and suit most WordPress sites. The validation process simply confirms domain ownership through email verification or DNS records.</p>
<p>Organization Validation (OV) certificates include business identity verification beyond domain ownership. Certificate authorities validate company information before issuance, providing additional credibility markers in certificate details.</p>
<p>Extended Validation (EV) certificates require the most rigorous verification process, validating legal entity existence, operational status, and physical location. EV certificates historically displayed company names in the browser address bar, though modern browsers have deprecated this prominent display.</p>
<p>Wildcard SSL certificates secure a domain and all its subdomains with a single certificate. For example, a wildcard certificate for *.example.com covers www.example.com, blog.example.com, shop.example.com, and any other subdomain.</p>
<h2 id="free-vs-paid-ssl-certificates">Free vs Paid SSL Certificates</h2>
<p>Let’s Encrypt revolutionized SSL accessibility by providing free, automated DV certificates trusted by all major browsers. The nonprofit certificate authority issues 90-day certificates that can be automatically renewed, eliminating cost barriers to HTTPS adoption.</p>
<p>Most modern hosting providers offer integrated Let’s Encrypt support through cPanel, Plesk, or custom control panels. These integrations handle certificate generation, installation, and renewal automatically.</p>
<p>Commercial SSL certificates from providers like DigiCert, Sectigo, and GlobalSign offer extended warranties, dedicated support, and OV/EV validation options. For most WordPress sites, free Let’s Encrypt certificates provide equivalent security to paid DV certificates.</p>
<h2 id="installing-ssl-through-cpanel">Installing SSL Through cPanel</h2>
<p>Most shared hosting providers offer cPanel with integrated SSL management. Access your cPanel, navigate to the Security section, and click “SSL/TLS Status” or “Let’s Encrypt SSL.”</p>
<p>For Let’s Encrypt certificates, select the domains to secure (typically your primary domain and www subdomain), then click “Install.” The system automatically generates the certificate, private key, and configures your web server.</p>
<p>Alternative manual installation through cPanel involves generating a Certificate Signing Request (CSR) in the SSL/TLS section, submitting this CSR to your certificate provider, then installing the received certificate files through “Manage SSL Sites.”</p>
<h2 id="manual-ssl-installation">Manual SSL Installation</h2>
<p>For VPS or dedicated servers without automation, manual installation provides full control. First, generate a private key and CSR:</p>
<div class="sourceCode" id="cb1">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="ex">openssl</span> req -new -newkey rsa:2048 -nodes -keyout yourdomain.key -out yourdomain.csr</span></code></pre>
</div>
<p>This command creates a 2048-bit RSA private key (yourdomain.key) and CSR (yourdomain.csr). Submit the CSR to your certificate authority, keeping the private key secure and never sharing it.</p>
<p>When you receive certificate files from your provider, you’ll typically get the primary certificate (yourdomain.crt) and intermediate certificate bundle (intermediate.crt). Install these in your web server configuration.</p>
<p>For Apache, edit your virtual host configuration:</p>
<div class="sourceCode" id="cb2">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="fu">&lt;VirtualHost</span><span class="at"> *:443</span><span class="fu">&gt;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>    ServerName<span class="st"> yourdomain.com</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a>    ServerAlias<span class="st"> www.yourdomain.com</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>    DocumentRoot<span class="st"> /var/www/html</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a>    <span class="ex">SSLEngine</span><span class="ch"> </span><span class="kw">on</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a>    SSLCertificateFile<span class="st"> /path/to/yourdomain.crt</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a>    SSLCertificateKeyFile<span class="st"> /path/to/yourdomain.key</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a>    SSLCertificateChainFile<span class="st"> /path/to/intermediate.crt</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true"></a></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a>    <span class="co"># Modern SSL configuration</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true"></a>    <span class="ex">SSLProtocol</span><span class="ch"> </span><span class="kw">all</span><span class="ch"> </span><span class="kw">-SSLv2</span><span class="ch"> </span><span class="kw">-SSLv3</span><span class="ch"> </span><span class="kw">-TLSv1</span><span class="ch"> </span><span class="kw">-TLSv1</span><span class="ch">.1</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true"></a>    SSLCipherSuite<span class="st"> HIGH:!aNULL:!MD5</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true"></a>    <span class="ex">SSLHonorCipherOrder</span><span class="ch"> </span><span class="kw">on</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true"></a><span class="fu">&lt;/VirtualHost&gt;</span></span></code></pre>
</div>
<p>For Nginx, configure the server block:</p>
<pre class="nginx"><code>server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/html;

    ssl_certificate /path/to/yourdomain.crt;
    ssl_certificate_key /path/to/yourdomain.key;

    # Modern SSL configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
}</code></pre>
<p>After configuration, restart your web server:</p>
<div class="sourceCode" id="cb4">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co"># Apache</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="fu">sudo</span> systemctl restart apache2</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a><span class="co"># Nginx</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a><span class="fu">sudo</span> systemctl restart nginx</span></code></pre>
</div>
<h2 id="configuring-wordpress-for-https">Configuring WordPress for HTTPS</h2>
<p>Once SSL is active, update WordPress to use HTTPS URLs. In the WordPress admin panel, navigate to Settings &gt; General and update both “WordPress Address (URL)” and “Site Address (URL)” from http:// to https://.</p>
<p>For security, force HTTPS in wp-config.php by adding these lines before “That’s all, stop editing!”:</p>
<div class="sourceCode" id="cb5">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span><span class="st">&#39;FORCE_SSL_ADMIN&#39;</span><span class="ot">,</span> <span class="kw">true</span><span class="ot">);</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span><span class="fu">strpos</span><span class="ot">(</span><span class="kw">$_SERVER</span><span class="ot">[</span><span class="st">&#39;HTTP_X_FORWARDED_PROTO&#39;</span><span class="ot">],</span> <span class="st">&#39;https&#39;</span><span class="ot">)</span> !== <span class="kw">false</span><span class="ot">)</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>    <span class="kw">$_SERVER</span><span class="ot">[</span><span class="st">&#39;HTTPS&#39;</span><span class="ot">]</span>=<span class="st">&#39;on&#39;</span><span class="ot">;</span></span></code></pre>
</div>
<p>The first line forces HTTPS for admin access. The second handles reverse proxy situations common with load balancers and CDNs.</p>
<h2 id="redirecting-http-to-https">Redirecting HTTP to HTTPS</h2>
<p>Implement 301 permanent redirects sending all HTTP traffic to HTTPS. For Apache, add this to your .htaccess file at the top, before WordPress rules:</p>
<div class="sourceCode" id="cb6">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="fu">&lt;IfModule</span><span class="at"> mod_rewrite.c</span><span class="fu">&gt;</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="ex">RewriteEngine</span><span class="ch"> </span><span class="kw">On</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>RewriteCond<span class="st"> %{HTTPS} off</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a>RewriteRule<span class="st"> ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a><span class="fu">&lt;/IfModule&gt;</span></span></code></pre>
</div>
<p>For Nginx, add this to your server configuration:</p>
<pre class="nginx"><code>server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$server_name$request_uri;
}</code></pre>
<p>Test the redirect by visiting http://yourdomain.com and confirming automatic redirection to https://yourdomain.com.</p>
<h2 id="updating-database-urls">Updating Database URLs</h2>
<p>WordPress stores URLs in the database that need updating from HTTP to HTTPS. The Better Search Replace plugin provides a safe interface for this critical operation.</p>
<p>Install Better Search Replace from the WordPress repository, navigate to Tools &gt; Better Search Replace, and configure:</p>
<ul>
<li>Search for: http://yourdomain.com</li>
<li>Replace with: https://yourdomain.com</li>
<li>Select all tables</li>
<li>Deselect “Run as dry run” only after testing</li>
<li>Click “Run Search/Replace”</li>
</ul>
<p>Run initially as a dry run to preview changes before executing the actual replacement.</p>
<p>For command-line enthusiasts, WP-CLI offers efficient search-replace:</p>
<div class="sourceCode" id="cb8">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="ex">wp</span> search-replace <span class="st">&#39;http://yourdomain.com&#39;</span> <span class="st">&#39;https://yourdomain.com&#39;</span> --all-tables --dry-run</span></code></pre>
</div>
<p>Remove –dry-run to execute after confirming the results look correct.</p>
<h2 id="fixing-mixed-content-issues">Fixing Mixed Content Issues</h2>
<p>Mixed content occurs when an HTTPS page loads resources (images, scripts, stylesheets) via HTTP, causing browser warnings and security issues. Browsers block mixed content, breaking functionality and displaying security warnings.</p>
<p>Identify mixed content using browser developer tools. Open DevTools (F12), navigate to the Console tab, and look for mixed content warnings like “Mixed Content: The page was loaded over HTTPS, but requested an insecure resource.”</p>
<p>The Really Simple SSL plugin automatically detects and fixes most mixed content issues by rewriting HTTP URLs to HTTPS. Install from the WordPress repository, activate, and the plugin handles the migration automatically.</p>
<p>For manual fixes, search your theme and plugin files for hardcoded HTTP URLs:</p>
<div class="sourceCode" id="cb9">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="co">// Bad - hardcoded HTTP</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a>&lt;img src=<span class="st">&quot;http://yourdomain.com/image.jpg&quot;</span>&gt;</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a><span class="co">// Good - protocol-relative</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a>&lt;img src=<span class="st">&quot;//yourdomain.com/image.jpg&quot;</span>&gt;</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a><span class="co">// Better - HTTPS</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a>&lt;img src=<span class="st">&quot;https://yourdomain.com/image.jpg&quot;</span>&gt;</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a><span class="co">// Best - dynamic</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true"></a>&lt;img src=<span class="st">&quot;&lt;?php echo esc_url( home_url( &#39;/image.jpg&#39; ) ); ?&gt;&quot;</span>&gt;</span></code></pre>
</div>
<p>Update external resources to HTTPS versions. Google Fonts, Analytics, and most CDNs support HTTPS. Replace HTTP CDN URLs with HTTPS equivalents.</p>
<h2 id="testing-https-implementation">Testing HTTPS Implementation</h2>
<p>SSL Labs Server Test (ssllabs.com/ssltest) provides comprehensive SSL configuration analysis. Enter your domain and wait for the scan to complete. Aim for an A+ rating by following the recommendations.</p>
<p>Common issues flagged include outdated TLS protocols (disable TLSv1.0 and TLSv1.1), weak cipher suites, and missing security headers like HSTS (HTTP Strict Transport Security).</p>
<p>Verify the padlock icon appears in all major browsers by testing in Chrome, Firefox, Safari, and Edge. Click the padlock to view certificate details and confirm validity.</p>
<p>Use online tools like WhyNoPadlock.com to identify mixed content preventing the secure padlock display.</p>
<h2 id="updating-external-services">Updating External Services</h2>
<p>Update Google Search Console by adding the HTTPS version of your site as a new property. Set up the HTTPS property, verify ownership, and submit an HTTPS sitemap. Keep the HTTP property active temporarily to monitor the migration.</p>
<p>In Google Analytics, update the property URL from HTTP to HTTPS in Admin &gt; Property Settings &gt; Default URL. This ensures accurate tracking during and after migration.</p>
<p>Update social media profiles on Facebook, Twitter, LinkedIn, and other platforms to reflect your HTTPS URLs. This maintains proper link sharing and Open Graph previews.</p>
<p>High-authority backlinks pointing to HTTP versions still pass value through 301 redirects, but consider reaching out to major referral sources to update links directly to HTTPS URLs.</p>
<h2 id="ssl-certificate-renewal">SSL Certificate Renewal</h2>
<p>Let’s Encrypt certificates expire after 90 days, requiring regular renewal. Most hosting providers with Let’s Encrypt integration handle automatic renewal through cron jobs.</p>
<p>Verify auto-renewal is configured by checking for certbot renewal cron entries:</p>
<div class="sourceCode" id="cb10">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="fu">sudo</span> crontab -l <span class="kw">|</span> <span class="fu">grep</span> certbot</span></code></pre>
</div>
<p>You should see entries like:</p>
<pre><code>0 0,12 * * * /usr/bin/certbot renew --quiet</code></pre>
<p>Test renewal manually without affecting current certificates:</p>
<div class="sourceCode" id="cb12">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="fu">sudo</span> certbot renew --dry-run</span></code></pre>
</div>
<p>Set up certificate expiration monitoring using services like SSL Checker or Uptime Robot that alert you before certificates expire.</p>
<h2 id="performance-benefits-of-https">Performance Benefits of HTTPS</h2>
<p>HTTP/2 protocol requires HTTPS and provides significant performance improvements including multiplexing (multiple requests over a single connection), header compression, and server push. These features typically improve load times by 10-30%.</p>
<p>Enable HTTP/2 in your web server configuration. Most modern servers support HTTP/2 when SSL is enabled. Verify HTTP/2 is active using browser developer tools Network tab or online checkers.</p>
<p>TLS session resumption reduces the overhead of establishing encrypted connections by caching session parameters. Modern web servers enable this by default, but verify with:</p>
<pre class="nginx"><code>ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;</code></pre>
<h2 id="troubleshooting-common-ssl-issues">Troubleshooting Common SSL Issues</h2>
<p>“ERR_SSL_PROTOCOL_ERROR” typically indicates server configuration issues. Verify your SSL certificate files are correctly installed and web server configuration is valid. Check server error logs for specific issues.</p>
<p>“NET::ERR_CERT_DATE_INVALID” means your certificate has expired or is not yet valid. Check certificate dates and renew if necessary. Ensure server time is accurate, as incorrect system time can cause validation failures.</p>
<p>Redirect loops often occur with CDN services like Cloudflare using Flexible SSL. This happens when Cloudflare connects to your origin server via HTTP while presenting HTTPS to visitors. Fix by either using Full SSL mode in Cloudflare or detecting HTTPS through X-Forwarded-Proto headers.</p>
<p>Mixed content warnings persist after migration when cached pages contain old HTTP URLs. Clear all caching layers including WordPress cache plugins, CDN cache, and browser cache. Force cache busting by incrementing version numbers on enqueued scripts and styles.</p>
<p>By following this comprehensive HTTPS migration process, your WordPress site gains the security, SEO benefits, and user trust that come with proper SSL implementation while avoiding common pitfalls that can disrupt functionality during the transition.</p>
<h2 id="external-links">External Links</h2>
<ol type="1">
<li><a href="https://letsencrypt.org/">Let’s Encrypt Free SSL</a></li>
<li><a href="https://www.ssllabs.com/ssltest/">SSL Labs Server Test</a></li>
<li><a href="https://wordpress.org/plugins/really-simple-ssl/">Really Simple SSL Plugin</a></li>
<li><a href="https://wordpress.org/plugins/better-search-replace/">Better Search Replace</a></li>
<li><a href="https://www.namecheap.com/security/ssl-certificates/">SSL Certificate Providers</a></li>
</ol>
<h2 id="call-to-action">Call to Action</h2>
<p>Secure your site with bulletproof backups! <a href="https://backupcopilotplugin.com/">Backup Copilot Pro</a> offers automated security audits, malware scanning before backups, and instant recovery—try it free!</p>
<p>The post <a href="https://developryplugins.com/ssl-certificate-setup-for-wordpress-complete-https-migration/">SSL Certificate Setup for WordPress: Complete HTTPS Migration</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>WordPress Plugin Security Best Practices: Prevent Common Vulnerabilities</title>
		<link>https://developryplugins.com/wordpress-plugin-security-best-practices-prevent-common-vulnerabilities/</link>
		
		<dc:creator><![CDATA[Krasen Slavov]]></dc:creator>
		<pubDate>Tue, 20 Jan 2026 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Plugin Development Guide]]></category>
		<category><![CDATA[plugin security]]></category>
		<category><![CDATA[security best practices]]></category>
		<category><![CDATA[sql injection]]></category>
		<category><![CDATA[wordpress security]]></category>
		<category><![CDATA[xss prevention]]></category>
		<guid isPermaLink="false">https://developryplugins.com/?p=173</guid>

					<description><![CDATA[<p>Plugin security vulnerabilities endanger millions of WordPress sites. As a plugin developer, you’re responsible for protecting user data, preventing attacks, and maintaining WordPress ecosystem trust. This guide covers essential security...</p>
<p>The post <a href="https://developryplugins.com/wordpress-plugin-security-best-practices-prevent-common-vulnerabilities/">WordPress Plugin Security Best Practices: Prevent Common Vulnerabilities</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- @format --></p>
<p>Plugin security vulnerabilities endanger millions of WordPress sites. As a plugin developer, you’re responsible for protecting user data, preventing attacks, and maintaining WordPress ecosystem trust. This guide covers essential security practices every plugin developer must follow.</p>
<h2 id="why-plugin-security-matters">Why Plugin Security Matters</h2>
<p>WordPress powers 43% of websites globally. Attackers target plugins because:</p>
<ul>
<li>Plugins often handle sensitive data</li>
<li>Security vulnerabilities affect thousands of sites using the same plugin</li>
<li>Poorly coded plugins provide entry points to otherwise secure sites</li>
<li>Plugin vulnerabilities appear in major security databases</li>
</ul>
<p>A single SQL injection vulnerability in your plugin could compromise thousands of WordPress installations. Security isn’t optional—it’s your primary responsibility as a developer.</p>
<h2 id="cross-site-scripting-xss-prevention">Cross-Site Scripting (XSS) Prevention</h2>
<p>XSS attacks inject malicious JavaScript into pages, stealing cookies, redirecting users, or modifying content.</p>
<p><strong>Always escape output:</strong></p>
<div class="sourceCode" id="cb1">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co">// HTML context</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;p&gt;&#39;</span> . esc_html<span class="ot">(</span> <span class="kw">$user_input</span> <span class="ot">)</span> . <span class="st">&#39;&lt;/p&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="co">// Attribute context</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;div class=&quot;&#39;</span> . esc_attr<span class="ot">(</span> <span class="kw">$class_name</span> <span class="ot">)</span> . <span class="st">&#39;&quot;&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a><span class="co">// URL context</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;a href=&quot;&#39;</span> . esc_url<span class="ot">(</span> <span class="kw">$link</span> <span class="ot">)</span> . <span class="st">&#39;&quot;&gt;Link&lt;/a&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true"></a><span class="co">// JavaScript context</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;script&gt;var data = &#39;</span> . esc_js<span class="ot">(</span> <span class="kw">$data</span> <span class="ot">)</span> . <span class="st">&#39;;&lt;/script&gt;&#39;</span><span class="ot">;</span></span></code></pre>
</div>
<p><strong>Allow specific HTML with wp_kses():</strong></p>
<div class="sourceCode" id="cb2">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw">$allowed_html</span> = <span class="kw">array</span><span class="ot">(</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>    <span class="st">&#39;a&#39;</span> =&gt; <span class="kw">array</span><span class="ot">(</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a>        <span class="st">&#39;href&#39;</span> =&gt; <span class="kw">array</span><span class="ot">(),</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>        <span class="st">&#39;title&#39;</span> =&gt; <span class="kw">array</span><span class="ot">()</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a>    <span class="ot">),</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a>    <span class="st">&#39;strong&#39;</span> =&gt; <span class="kw">array</span><span class="ot">(),</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a>    <span class="st">&#39;em&#39;</span> =&gt; <span class="kw">array</span><span class="ot">()</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a><span class="kw">echo</span> wp_kses<span class="ot">(</span> <span class="kw">$user_content</span><span class="ot">,</span> <span class="kw">$allowed_html</span> <span class="ot">);</span></span></code></pre>
</div>
<p>Never output unescaped user data. Ever.</p>
<h2 id="sql-injection-prevention">SQL Injection Prevention</h2>
<p>SQL injection allows attackers to execute arbitrary database queries.</p>
<p><strong>Always use prepared statements:</strong></p>
<div class="sourceCode" id="cb3">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="kw">global</span> <span class="kw">$wpdb</span><span class="ot">;</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a><span class="co">// WRONG - vulnerable to SQL injection</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a><span class="kw">$user_id</span> = <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;user_id&#39;</span><span class="ot">];</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span> <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;users}</span><span class="st"> WHERE ID = </span><span class="kw">$user_id</span><span class="st">&quot;</span> <span class="ot">);</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a><span class="co">// CORRECT - using prepare()</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a><span class="kw">$user_id</span> = <span class="fu">intval</span><span class="ot">(</span> <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;user_id&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;users}</span><span class="st"> WHERE ID = %d&quot;</span><span class="ot">,</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true"></a>        <span class="kw">$user_id</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p><strong>Placeholders for different data types:</strong></p>
<div class="sourceCode" id="cb4">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co">// %d for integers</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span> <span class="st">&quot;SELECT * FROM table WHERE id = %d&quot;</span><span class="ot">,</span> <span class="kw">$id</span> <span class="ot">);</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a><span class="co">// %s for strings</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span> <span class="st">&quot;SELECT * FROM table WHERE name = %s&quot;</span><span class="ot">,</span> <span class="kw">$name</span> <span class="ot">);</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a><span class="co">// %f for floats</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span> <span class="st">&quot;SELECT * FROM table WHERE price = %f&quot;</span><span class="ot">,</span> <span class="kw">$price</span> <span class="ot">);</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true"></a><span class="co">// Multiple values</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true"></a>    <span class="st">&quot;SELECT * FROM table WHERE id = %d AND name = %s&quot;</span><span class="ot">,</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true"></a>    <span class="kw">$id</span><span class="ot">,</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true"></a>    <span class="kw">$name</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p>Never concatenate variables into SQL queries.</p>
<h2 id="csrf-protection-with-nonces">CSRF Protection with Nonces</h2>
<p>CSRF attacks trick users into executing unwanted actions.</p>
<p><strong>Create nonces in forms:</strong></p>
<div class="sourceCode" id="cb5">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a>&lt;form method=<span class="st">&quot;post&quot;</span>&gt;</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a>    &lt;<span class="ot">?</span>php wp_nonce_field<span class="ot">(</span> <span class="st">&#39;dprt_save_settings&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_nonce&#39;</span> <span class="ot">);</span> <span class="kw">?&gt;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>    &lt;input type=<span class="st">&quot;text&quot;</span> name=<span class="st">&quot;setting_value&quot;</span>&gt;</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a>    &lt;input type=<span class="st">&quot;submit&quot;</span> value=<span class="st">&quot;Save&quot;</span>&gt;</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a>&lt;/form&gt;</span></code></pre>
</div>
<p><strong>Verify nonces before processing:</strong></p>
<div class="sourceCode" id="cb6">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> <span class="kw">isset</span><span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;submit&#39;</span><span class="ot">]</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! <span class="kw">isset</span><span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;dprt_nonce&#39;</span><span class="ot">]</span> <span class="ot">)</span> || ! wp_verify_nonce<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;dprt_nonce&#39;</span><span class="ot">],</span> <span class="st">&#39;dprt_save_settings&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>        wp_die<span class="ot">(</span> <span class="st">&#39;Security check failed&#39;</span> <span class="ot">);</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a>    }</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a>    <span class="co">// Process form data</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a>}</span></code></pre>
</div>
<p><strong>For admin pages, use check_admin_referer():</strong></p>
<div class="sourceCode" id="cb7">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a>check_admin_referer<span class="ot">(</span> <span class="st">&#39;dprt_save_settings&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_nonce&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p><strong>For AJAX:</strong></p>
<div class="sourceCode" id="cb8">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="co">// JavaScript</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a>$.post<span class="ot">(</span> ajaxurl<span class="ot">,</span> {</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a>    action: <span class="st">&#39;dprt_save&#39;</span><span class="ot">,</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a>    nonce: dprt_ajax.nonce<span class="ot">,</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>    data: formData</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a>}<span class="ot">);</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a><span class="co">// PHP</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a><span class="kw">function</span> dprt_ajax_save<span class="ot">()</span> {</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a>    check_ajax_referer<span class="ot">(</span> <span class="st">&#39;dprt_ajax_nonce&#39;</span><span class="ot">,</span> <span class="st">&#39;nonce&#39;</span> <span class="ot">);</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a>    <span class="co">// Process request</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>Every form submission, AJAX request, and state-changing action requires nonce verification.</p>
<h2 id="user-capability-checks">User Capability Checks</h2>
<p>Verify users have permission before allowing actions:</p>
<div class="sourceCode" id="cb9">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="co">// Check specific capability</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;manage_options&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a>    wp_die<span class="ot">(</span> <span class="st">&#39;Unauthorized access&#39;</span> <span class="ot">);</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a>}</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a><span class="co">// Check for specific post</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;edit_post&#39;</span><span class="ot">,</span> <span class="kw">$post_id</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a>    wp_die<span class="ot">(</span> <span class="st">&#39;You cannot edit this post&#39;</span> <span class="ot">);</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a>}</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true"></a><span class="co">// Check multiple capabilities</span></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;edit_posts&#39;</span> <span class="ot">)</span> &amp;&amp; ! current_user_can<span class="ot">(</span> <span class="st">&#39;edit_pages&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true"></a>    <span class="kw">return</span><span class="ot">;</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>Common capabilities:</p>
<ul>
<li><code>manage_options</code> &#8211; Administrators only</li>
<li><code>edit_posts</code> &#8211; Can edit posts</li>
<li><code>publish_posts</code> &#8211; Can publish posts</li>
<li><code>edit_others_posts</code> &#8211; Can edit posts by other users</li>
</ul>
<h2 id="data-sanitization">Data Sanitization</h2>
<p>Clean all user input before processing:</p>
<div class="sourceCode" id="cb10">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="co">// Text fields</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="kw">$text</span> = sanitize_text_field<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;text_field&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a><span class="co">// Textareas</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a><span class="kw">$content</span> = sanitize_textarea_field<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;content&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a><span class="co">// Email addresses</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a><span class="kw">$email</span> = sanitize_email<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;email&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a><span class="co">// URLs</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a><span class="kw">$url</span> = esc_url_raw<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;url&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true"></a><span class="co">// File names</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true"></a><span class="kw">$filename</span> = sanitize_file_name<span class="ot">(</span> <span class="kw">$_FILES</span><span class="ot">[</span><span class="st">&#39;file&#39;</span><span class="ot">][</span><span class="st">&#39;name&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true"></a></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true"></a><span class="co">// HTML class names</span></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true"></a><span class="kw">$class</span> = sanitize_html_class<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;class&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true"></a></span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true"></a><span class="co">// Keys</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true"></a><span class="kw">$key</span> = sanitize_key<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;key&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true"></a></span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true"></a><span class="co">// Integers</span></span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true"></a><span class="kw">$number</span> = absint<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;number&#39;</span><span class="ot">]</span> <span class="ot">);</span></span></code></pre>
</div>
<p>Sanitize input, escape output. This principle prevents most vulnerabilities.</p>
<h2 id="file-upload-security">File Upload Security</h2>
<p>File uploads are high-risk operations:</p>
<div class="sourceCode" id="cb11">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="kw">function</span> dprt_handle_file_upload<span class="ot">()</span> {</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! <span class="kw">isset</span><span class="ot">(</span> <span class="kw">$_FILES</span><span class="ot">[</span><span class="st">&#39;file&#39;</span><span class="ot">]</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a>        <span class="kw">return</span><span class="ot">;</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a>    }</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a>    <span class="co">// Verify nonce</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a>    check_admin_referer<span class="ot">(</span> <span class="st">&#39;dprt_upload&#39;</span><span class="ot">,</span> <span class="st">&#39;nonce&#39;</span> <span class="ot">);</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true"></a>    <span class="co">// Check capability</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;upload_files&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true"></a>        wp_die<span class="ot">(</span> <span class="st">&#39;Insufficient permissions&#39;</span> <span class="ot">);</span></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true"></a>    }</span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true"></a></span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true"></a>    <span class="co">// Validate file type</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true"></a>    <span class="kw">$allowed_types</span> = <span class="kw">array</span><span class="ot">(</span> <span class="st">&#39;image/jpeg&#39;</span><span class="ot">,</span> <span class="st">&#39;image/png&#39;</span><span class="ot">,</span> <span class="st">&#39;image/gif&#39;</span> <span class="ot">);</span></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true"></a>    <span class="kw">$file_type</span> = <span class="kw">$_FILES</span><span class="ot">[</span><span class="st">&#39;file&#39;</span><span class="ot">][</span><span class="st">&#39;type&#39;</span><span class="ot">];</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true"></a></span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! <span class="fu">in_array</span><span class="ot">(</span> <span class="kw">$file_type</span><span class="ot">,</span> <span class="kw">$allowed_types</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true"></a>        wp_die<span class="ot">(</span> <span class="st">&#39;Invalid file type&#39;</span> <span class="ot">);</span></span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true"></a>    }</span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true"></a></span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true"></a>    <span class="co">// Use WordPress upload handler</span></span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true"></a>    <span class="kw">$upload</span> = wp_handle_upload<span class="ot">(</span></span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true"></a>        <span class="kw">$_FILES</span><span class="ot">[</span><span class="st">&#39;file&#39;</span><span class="ot">],</span></span>
<span id="cb11-25"><a href="#cb11-25" aria-hidden="true"></a>        <span class="kw">array</span><span class="ot">(</span> <span class="st">&#39;test_form&#39;</span> =&gt; <span class="kw">false</span> <span class="ot">)</span></span>
<span id="cb11-26"><a href="#cb11-26" aria-hidden="true"></a>    <span class="ot">);</span></span>
<span id="cb11-27"><a href="#cb11-27" aria-hidden="true"></a></span>
<span id="cb11-28"><a href="#cb11-28" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> <span class="kw">isset</span><span class="ot">(</span> <span class="kw">$upload</span><span class="ot">[</span><span class="st">&#39;error&#39;</span><span class="ot">]</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb11-29"><a href="#cb11-29" aria-hidden="true"></a>        wp_die<span class="ot">(</span> <span class="kw">$upload</span><span class="ot">[</span><span class="st">&#39;error&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb11-30"><a href="#cb11-30" aria-hidden="true"></a>    }</span>
<span id="cb11-31"><a href="#cb11-31" aria-hidden="true"></a></span>
<span id="cb11-32"><a href="#cb11-32" aria-hidden="true"></a>    <span class="co">// File uploaded successfully</span></span>
<span id="cb11-33"><a href="#cb11-33" aria-hidden="true"></a>    <span class="kw">$file_url</span> = <span class="kw">$upload</span><span class="ot">[</span><span class="st">&#39;url&#39;</span><span class="ot">];</span></span>
<span id="cb11-34"><a href="#cb11-34" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>Never trust uploaded files. Validate type, size, and content.</p>
<h2 id="preventing-direct-file-access">Preventing Direct File Access</h2>
<p>Prevent users from accessing plugin files directly:</p>
<div class="sourceCode" id="cb12">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="kw">&lt;?php</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a><span class="co">// At the top of every PHP file</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! <span class="fu">defined</span><span class="ot">(</span> <span class="st">&#39;ABSPATH&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a>    <span class="kw">exit</span><span class="ot">;</span> <span class="co">// Exit if accessed directly</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>This prevents attackers from executing PHP files outside WordPress context.</p>
<h2 id="secure-ajax-implementation">Secure AJAX Implementation</h2>
<p>AJAX requests need the same security as form submissions:</p>
<div class="sourceCode" id="cb13">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="co">// Enqueue script with nonce</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a><span class="kw">function</span> dprt_enqueue_ajax_script<span class="ot">()</span> {</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a>    wp_enqueue_script<span class="ot">(</span> <span class="st">&#39;dprt-ajax&#39;</span><span class="ot">,</span> plugin_dir_url<span class="ot">(</span> <span class="kw">__FILE__</span> <span class="ot">)</span> . <span class="st">&#39;ajax.js&#39;</span><span class="ot">,</span> <span class="kw">array</span><span class="ot">(</span> <span class="st">&#39;jquery&#39;</span> <span class="ot">)</span> <span class="ot">);</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true"></a>    wp_localize_script<span class="ot">(</span> <span class="st">&#39;dprt-ajax&#39;</span><span class="ot">,</span> <span class="st">&#39;dprtAjax&#39;</span><span class="ot">,</span> <span class="kw">array</span><span class="ot">(</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true"></a>        <span class="st">&#39;ajaxurl&#39;</span> =&gt; admin_url<span class="ot">(</span> <span class="st">&#39;admin-ajax.php&#39;</span> <span class="ot">),</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true"></a>        <span class="st">&#39;nonce&#39;</span> =&gt; wp_create_nonce<span class="ot">(</span> <span class="st">&#39;dprt_ajax&#39;</span> <span class="ot">)</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true"></a>    <span class="ot">)</span> <span class="ot">);</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true"></a>}</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true"></a></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true"></a><span class="co">// AJAX handler</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true"></a><span class="kw">function</span> dprt_ajax_handler<span class="ot">()</span> {</span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true"></a>    <span class="co">// Verify nonce</span></span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true"></a>    check_ajax_referer<span class="ot">(</span> <span class="st">&#39;dprt_ajax&#39;</span><span class="ot">,</span> <span class="st">&#39;nonce&#39;</span> <span class="ot">);</span></span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true"></a></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true"></a>    <span class="co">// Check capabilities</span></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;manage_options&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true"></a>        wp_send_json_error<span class="ot">(</span> <span class="st">&#39;Insufficient permissions&#39;</span> <span class="ot">);</span></span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true"></a>    }</span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true"></a></span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true"></a>    <span class="co">// Sanitize input</span></span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true"></a>    <span class="kw">$data</span> = sanitize_text_field<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;data&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true"></a></span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true"></a>    <span class="co">// Process and respond</span></span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true"></a>    wp_send_json_success<span class="ot">(</span> <span class="kw">array</span><span class="ot">(</span> <span class="st">&#39;result&#39;</span> =&gt; <span class="kw">$processed_data</span> <span class="ot">)</span> <span class="ot">);</span></span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true"></a>}</span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true"></a>add_action<span class="ot">(</span> <span class="st">&#39;wp_ajax_dprt_action&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_ajax_handler&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<h2 id="secure-configuration">Secure Configuration</h2>
<p>Never hardcode sensitive information:</p>
<div class="sourceCode" id="cb14">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="co">// BAD - hardcoded API key</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="kw">$api_key</span> = <span class="st">&#39;sk_live_123456789&#39;</span><span class="ot">;</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a><span class="co">// GOOD - use constants or options</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;DPRT_API_KEY&#39;</span><span class="ot">,</span> <span class="st">&#39;sk_live_123456789&#39;</span> <span class="ot">);</span> <span class="co">// In wp-config.php</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true"></a><span class="kw">$api_key</span> = <span class="fu">defined</span><span class="ot">(</span> <span class="st">&#39;DPRT_API_KEY&#39;</span> <span class="ot">)</span> <span class="ot">?</span> <span class="kw">DPRT_API_KEY</span> <span class="ot">:</span> get_option<span class="ot">(</span> <span class="st">&#39;dprt_api_key&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p>Store sensitive data in wp-config.php or use environment variables.</p>
<h2 id="security-checklist">Security Checklist</h2>
<p>Before releasing your plugin:</p>
<ul class="task-list">
<li><input type="checkbox" disabled="" /><br />
All user input sanitized</li>
<li><input type="checkbox" disabled="" /><br />
All output escaped</li>
<li><input type="checkbox" disabled="" /><br />
Nonces on all forms and AJAX requests</li>
<li><input type="checkbox" disabled="" /><br />
Capability checks on all admin functions</li>
<li><input type="checkbox" disabled="" /><br />
Prepared statements for all database queries</li>
<li><input type="checkbox" disabled="" /><br />
File upload validation</li>
<li><input type="checkbox" disabled="" /><br />
Direct file access prevention</li>
<li><input type="checkbox" disabled="" /><br />
No hardcoded credentials</li>
<li><input type="checkbox" disabled="" /><br />
Security review by another developer</li>
<li><input type="checkbox" disabled="" /><br />
Testing with security plugins</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Security isn’t a feature—it’s a requirement. Sanitize all input, escape all output, verify nonces, check capabilities, and use prepared statements. These practices protect users and maintain WordPress ecosystem trust. Make security your default mindset, not an afterthought.</p>
<ul>
<li>Secure communication</li>
<li>Using HTTPS for API calls</li>
<li>SSL/TLS certificate validation</li>
<li>Third-party library security</li>
<li>Keeping dependencies updated</li>
<li>Vulnerability scanning tools</li>
<li>Security auditing and code review</li>
<li>Common security mistakes developers make</li>
<li>Security checklist for plugin release</li>
<li>Responsible disclosure of vulnerabilities</li>
</ul>
<p>Includes code examples, security patterns, and testing procedures for building secure, trustworthy WordPress plugins.</p>
<h2 id="external-links">External Links</h2>
<ol type="1">
<li><a href="https://developer.wordpress.org/plugins/security/">WordPress Plugin Security</a></li>
<li><a href="https://developer.wordpress.org/plugins/security/data-validation/">Data Validation Documentation</a></li>
<li><a href="https://owasp.org/www-project-top-ten/">OWASP Top 10</a></li>
<li><a href="https://wordpress.org/about/security/">WordPress Security White Paper</a></li>
<li><a href="https://wpscan.com/wordpress-security-scanner">WPScan Vulnerability Database</a></li>
</ol>
<h2 id="call-to-action">Call to Action</h2>
<p>Supercharge your development! <a href="https://acfcopilotplugin.com/">ACF Copilot Pro</a> generates ACF field groups with AI, exports to PHP, and accelerates custom field workflows—try it free!</p>
<p>The post <a href="https://developryplugins.com/wordpress-plugin-security-best-practices-prevent-common-vulnerabilities/">WordPress Plugin Security Best Practices: Prevent Common Vulnerabilities</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>WordPress Security Checklist: 50 Steps to Harden Your Website</title>
		<link>https://developryplugins.com/wordpress-security-checklist-50-steps-to-harden-your-website/</link>
		
		<dc:creator><![CDATA[Krasen Slavov]]></dc:creator>
		<pubDate>Tue, 30 Dec 2025 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Security & Protection]]></category>
		<category><![CDATA[security best practices]]></category>
		<category><![CDATA[security checklist]]></category>
		<category><![CDATA[website hardening]]></category>
		<category><![CDATA[wordpress protection]]></category>
		<category><![CDATA[wordpress security]]></category>
		<guid isPermaLink="false">https://developryplugins.com/?p=178</guid>

					<description><![CDATA[<p>WordPress security isn’t optional—it’s essential. With over 40% of the web running on WordPress, it’s a prime target for hackers, bots, and malicious actors. A single security vulnerability can lead...</p>
<p>The post <a href="https://developryplugins.com/wordpress-security-checklist-50-steps-to-harden-your-website/">WordPress Security Checklist: 50 Steps to Harden Your Website</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- @format --></p>
<p>WordPress security isn’t optional—it’s essential. With over 40% of the web running on WordPress, it’s a prime target for hackers, bots, and malicious actors. A single security vulnerability can lead to data breaches, SEO penalties, blacklisting, and complete site compromise.</p>
<p>This comprehensive checklist provides 50 actionable steps to harden your WordPress website against common threats. Whether you’re securing a personal blog or an enterprise site, these measures will significantly reduce your attack surface.</p>
<h2 id="core-wordpress-security-steps-1-10">Core WordPress Security (Steps 1-10)</h2>
<h3 id="keep-wordpress-core-updated">1. Keep WordPress Core Updated</h3>
<p>Always update to the latest WordPress version. Updates include security patches for known vulnerabilities.</p>
<div class="sourceCode" id="cb1">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co">// Enable automatic updates for minor releases in wp-config.php</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;WP_AUTO_UPDATE_CORE&#39;</span><span class="ot">,</span> <span class="st">&#39;minor&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<h3 id="update-all-plugins-regularly">2. Update All Plugins Regularly</h3>
<p>Outdated plugins are the #1 entry point for hackers. Enable auto-updates for trusted plugins.</p>
<h3 id="update-all-themes">3. Update All Themes</h3>
<p>Even inactive themes can be exploited. Update or delete them.</p>
<h3 id="remove-unused-plugins-and-themes">4. Remove Unused Plugins and Themes</h3>
<p>Delete (don’t just deactivate) any plugins and themes you’re not using.</p>
<h3 id="delete-default-wordpress-themes">5. Delete Default WordPress Themes</h3>
<p>Unless needed for testing, remove default themes like Twenty Twenty-Three.</p>
<h3 id="use-strong-admin-passwords">6. Use Strong Admin Passwords</h3>
<p>Require 20+ character passwords with uppercase, lowercase, numbers, and symbols.</p>
<h3 id="change-default-admin-username">7. Change Default “admin” Username</h3>
<p>The username “admin” is the first thing hackers try. Create a unique username.</p>
<div class="sourceCode" id="cb2">
<pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="co">-- Change username via database (backup first!)</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="kw">UPDATE</span> wp_users <span class="kw">SET</span> user_login <span class="op">=</span> <span class="st">&#39;newusername&#39;</span> <span class="kw">WHERE</span> user_login <span class="op">=</span> <span class="st">&#39;admin&#39;</span>;</span></code></pre>
</div>
<h3 id="implement-two-factor-authentication-2fa">8. Implement Two-Factor Authentication (2FA)</h3>
<p>Add an extra security layer beyond passwords. Use plugins like Wordfence Login Security or Two-Factor.</p>
<h3 id="limit-login-attempts">9. Limit Login Attempts</h3>
<p>Prevent brute force attacks by limiting failed login attempts.</p>
<div class="sourceCode" id="cb3">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="co">// Using Limit Login Attempts Reloaded plugin settings:</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="co">// - 4 attempts before lockout</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a><span class="co">// - 20-minute lockout duration</span></span></code></pre>
</div>
<h3 id="install-a-security-plugin">10. Install a Security Plugin</h3>
<p>Choose Wordfence, Sucuri, or iThemes Security for comprehensive protection.</p>
<h2 id="configuration-security-steps-11-20">Configuration Security (Steps 11-20)</h2>
<h3 id="generate-fresh-security-keys">11. Generate Fresh Security Keys</h3>
<p>Replace WordPress security keys in wp-config.php using the <a href="https://api.wordpress.org/secret-key/1.1/salt/">WordPress.org generator</a>.</p>
<div class="sourceCode" id="cb4">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span><span class="st">&#39;AUTH_KEY&#39;</span><span class="ot">,</span>         <span class="st">&#39;put your unique phrase here&#39;</span><span class="ot">);</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span><span class="st">&#39;SECURE_AUTH_KEY&#39;</span><span class="ot">,</span>  <span class="st">&#39;put your unique phrase here&#39;</span><span class="ot">);</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span><span class="st">&#39;LOGGED_IN_KEY&#39;</span><span class="ot">,</span>    <span class="st">&#39;put your unique phrase here&#39;</span><span class="ot">);</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span><span class="st">&#39;NONCE_KEY&#39;</span><span class="ot">,</span>        <span class="st">&#39;put your unique phrase here&#39;</span><span class="ot">);</span></span></code></pre>
</div>
<h3 id="change-database-table-prefix">12. Change Database Table Prefix</h3>
<p>Change from default <code>wp_</code> to something unique like <code>wp_7j2k_</code>.</p>
<div class="sourceCode" id="cb5">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="co">// In wp-config.php</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a><span class="kw">$table_prefix</span> = <span class="st">&#39;wp_7j2k_&#39;</span><span class="ot">;</span></span></code></pre>
</div>
<h3 id="disable-file-editing-in-admin">13. Disable File Editing in Admin</h3>
<p>Prevent hackers from modifying theme/plugin files via admin.</p>
<div class="sourceCode" id="cb6">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="co">// Add to wp-config.php</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;DISALLOW_FILE_EDIT&#39;</span><span class="ot">,</span> <span class="kw">true</span> <span class="ot">);</span></span></code></pre>
</div>
<h3 id="disable-file-installation-via-admin">14. Disable File Installation via Admin</h3>
<p>For maximum security, prevent all file installations.</p>
<div class="sourceCode" id="cb7">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;DISALLOW_FILE_MODS&#39;</span><span class="ot">,</span> <span class="kw">true</span> <span class="ot">);</span></span></code></pre>
</div>
<h3 id="protect-wp-config.php">15. Protect wp-config.php</h3>
<p>Move wp-config.php one directory above web root, or add .htaccess protection.</p>
<div class="sourceCode" id="cb8">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="co"># In .htaccess</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="fu">&lt;files</span><span class="at"> wp-config.php</span><span class="fu">&gt;</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a><span class="ex">order</span><span class="ch"> </span><span class="kw">allow,deny</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a>deny<span class="st"> from all</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a><span class="fu">&lt;/files&gt;</span></span></code></pre>
</div>
<h3 id="secure-.htaccess-file">16. Secure .htaccess File</h3>
<p>Protect your .htaccess from unauthorized access.</p>
<div class="sourceCode" id="cb9">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="fu">&lt;files</span><span class="at"> .htaccess</span><span class="fu">&gt;</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="ex">order</span><span class="ch"> </span><span class="kw">allow,deny</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a>deny<span class="st"> from all</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a><span class="fu">&lt;/files&gt;</span></span></code></pre>
</div>
<h3 id="disable-xml-rpc">17. Disable XML-RPC</h3>
<p>Unless needed for mobile apps or external posting, disable XML-RPC.</p>
<div class="sourceCode" id="cb10">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="co"># In .htaccess</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="fu">&lt;Files</span><span class="at"> xmlrpc.php</span><span class="fu">&gt;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a><span class="ex">order</span><span class="ch"> </span><span class="kw">deny,allow</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a>deny<span class="st"> from all</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a><span class="fu">&lt;/Files&gt;</span></span></code></pre>
</div>
<h3 id="disable-rest-api-for-unauthenticated-users">18. Disable REST API for Unauthenticated Users</h3>
<p>Prevent data harvesting via REST API.</p>
<div class="sourceCode" id="cb11">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="co">// In functions.php</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a>add_filter<span class="ot">(</span> <span class="st">&#39;rest_authentication_errors&#39;</span><span class="ot">,</span> <span class="kw">function</span><span class="ot">(</span> <span class="kw">$result</span> <span class="ot">)</span> {</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! is_user_logged_in<span class="ot">()</span> <span class="ot">)</span> {</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a>        <span class="kw">return</span> <span class="kw">new</span> WP_Error<span class="ot">(</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a>            <span class="st">&#39;rest_not_logged_in&#39;</span><span class="ot">,</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a>            <span class="st">&#39;You must be logged in to access the REST API.&#39;</span><span class="ot">,</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a>            <span class="kw">array</span><span class="ot">(</span> <span class="st">&#39;status&#39;</span> =&gt; <span class="dv">401</span> <span class="ot">)</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a>        <span class="ot">);</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true"></a>    }</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">$result</span><span class="ot">;</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true"></a>}<span class="ot">);</span></span></code></pre>
</div>
<h3 id="hide-wordpress-version">19. Hide WordPress Version</h3>
<p>Remove version info from source code.</p>
<div class="sourceCode" id="cb12">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="co">// In functions.php</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a>remove_action<span class="ot">(</span><span class="st">&#39;wp_head&#39;</span><span class="ot">,</span> <span class="st">&#39;wp_generator&#39;</span><span class="ot">);</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a><span class="co">// Remove from RSS feeds</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a>add_filter<span class="ot">(</span><span class="st">&#39;the_generator&#39;</span><span class="ot">,</span> <span class="st">&#39;__return_empty_string&#39;</span><span class="ot">);</span></span></code></pre>
</div>
<h3 id="remove-readme.html-and-license.txt">20. Remove readme.html and license.txt</h3>
<p>Delete these files that reveal WordPress installation.</p>
<h2 id="file-system-security-steps-21-30">File System Security (Steps 21-30)</h2>
<h3 id="set-proper-file-permissions">21. Set Proper File Permissions</h3>
<p>Directories: 755, Files: 644, wp-config.php: 440</p>
<div class="sourceCode" id="cb13">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="co"># Via SSH</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a><span class="fu">find</span> /path/to/wordpress/ -type d -exec chmod 755 {} <span class="dt">\;</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a><span class="fu">find</span> /path/to/wordpress/ -type f -exec chmod 644 {} <span class="dt">\;</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true"></a><span class="fu">chmod</span> 440 wp-config.php</span></code></pre>
</div>
<h3 id="disable-directory-browsing">22. Disable Directory Browsing</h3>
<p>Prevent listing of directory contents.</p>
<div class="sourceCode" id="cb14">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="co"># In .htaccess</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="ex">Options</span><span class="ch"> </span><span class="kw">-Indexes</span></span></code></pre>
</div>
<h3 id="protect-wp-includes">23. Protect wp-includes</h3>
<p>Prevent direct access to wp-includes files.</p>
<div class="sourceCode" id="cb15">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="co"># In .htaccess</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a><span class="fu">&lt;IfModule</span><span class="at"> mod_rewrite.c</span><span class="fu">&gt;</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a><span class="ex">RewriteEngine</span><span class="ch"> </span><span class="kw">On</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a>RewriteBase<span class="st"> /</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a>RewriteRule<span class="st"> ^wp-admin/includes/ - [F,L]</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a>RewriteRule<span class="st"> !^wp-includes/ - [S=3]</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a>RewriteRule<span class="st"> ^wp-includes/[^/]+\.php$ - [F,L]</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a>RewriteRule<span class="st"> ^wp-includes/js/tinymce/langs/.+\.php - [F,L]</span></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a>RewriteRule<span class="st"> ^wp-includes/theme-compat/ - [F,L]</span></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true"></a><span class="fu">&lt;/IfModule&gt;</span></span></code></pre>
</div>
<h3 id="protect-wp-content">24. Protect wp-content</h3>
<p>Block PHP execution in uploads directory.</p>
<div class="sourceCode" id="cb16">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="co"># In wp-content/uploads/.htaccess</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a><span class="fu">&lt;Files</span><span class="at"> *.php</span><span class="fu">&gt;</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a>deny<span class="st"> from all</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true"></a><span class="fu">&lt;/Files&gt;</span></span></code></pre>
</div>
<h3 id="enable-file-integrity-monitoring">25. Enable File Integrity Monitoring</h3>
<p>Use security plugins to monitor file changes.</p>
<h3 id="regular-file-scans">26. Regular File Scans</h3>
<p>Schedule daily malware scans with Wordfence or Sucuri.</p>
<h3 id="validate-file-ownership">27. Validate File Ownership</h3>
<p>Ensure files are owned by your user account, not the web server.</p>
<h3 id="remove-default-files">28. Remove Default Files</h3>
<p>Delete wp-config-sample.php, readme.html, and license.txt.</p>
<h3 id="backup-before-updates">29. Backup Before Updates</h3>
<p>Always backup before major updates.</p>
<h3 id="test-restores-monthly">30. Test Restores Monthly</h3>
<p>Verify backups work by testing restoration procedures.</p>
<h2 id="access-control-steps-31-40">Access Control (Steps 31-40)</h2>
<h3 id="use-https-site-wide">31. Use HTTPS Site-Wide</h3>
<p>Install SSL certificate and force HTTPS.</p>
<div class="sourceCode" id="cb17">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a><span class="co">// In wp-config.php</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;FORCE_SSL_ADMIN&#39;</span><span class="ot">,</span> <span class="kw">true</span> <span class="ot">);</span></span></code></pre>
</div>
<h3 id="ip-whitelist-wp-admin">32. IP Whitelist wp-admin</h3>
<p>Restrict admin access to specific IPs.</p>
<div class="sourceCode" id="cb18">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a><span class="co"># In wp-admin/.htaccess</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true"></a><span class="ex">order</span><span class="ch"> </span><span class="kw">deny,allow</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true"></a>deny<span class="st"> from all</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true"></a>allow<span class="st"> from 123.456.789.0</span></span></code></pre>
</div>
<h3 id="add-recaptcha-to-login">33. Add reCAPTCHA to Login</h3>
<p>Prevent bot attacks on wp-login.php.</p>
<h3 id="change-login-url">34. Change Login URL</h3>
<p>Use WPS Hide Login to obscure wp-login.php.</p>
<h3 id="disable-user-registration">35. Disable User Registration</h3>
<p>Unless needed, turn off user registration in Settings &gt; General.</p>
<h3 id="implement-role-based-access-control">36. Implement Role-Based Access Control</h3>
<p>Give users minimal permissions needed.</p>
<h3 id="remove-unused-user-accounts">37. Remove Unused User Accounts</h3>
<p>Delete inactive or suspicious accounts.</p>
<h3 id="audit-user-permissions-regularly">38. Audit User Permissions Regularly</h3>
<p>Review who has admin access quarterly.</p>
<h3 id="use-security-questions">39. Use Security Questions</h3>
<p>Add secondary authentication questions.</p>
<h3 id="enable-activity-logging">40. Enable Activity Logging</h3>
<p>Monitor all user actions with Simple History plugin.</p>
<h2 id="server-hosting-security-steps-41-50">Server &amp; Hosting Security (Steps 41-50)</h2>
<h3 id="choose-secure-hosting">41. Choose Secure Hosting</h3>
<p>Use hosts with server-level security (managed WordPress hosting recommended).</p>
<h3 id="enable-web-application-firewall-waf">42. Enable Web Application Firewall (WAF)</h3>
<p>Cloudflare or Sucuri firewall protection.</p>
<h3 id="add-security-headers">43. Add Security Headers</h3>
<div class="sourceCode" id="cb19">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a><span class="co"># In .htaccess</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true"></a><span class="fu">&lt;IfModule</span><span class="at"> mod_headers.c</span><span class="fu">&gt;</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true"></a>Header<span class="st"> set X-XSS-Protection &quot;1; mode=block&quot;</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true"></a>Header<span class="st"> set X-Frame-Options &quot;SAMEORIGIN&quot;</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true"></a>Header<span class="st"> set X-Content-Type-Options &quot;nosniff&quot;</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true"></a>Header<span class="st"> set Referrer-Policy &quot;strict-origin-when-cross-origin&quot;</span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true"></a>Header<span class="st"> set Permissions-Policy &quot;geolocation=(), microphone=(), camera=()&quot;</span></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true"></a><span class="fu">&lt;/IfModule&gt;</span></span></code></pre>
</div>
<h3 id="disable-php-error-reporting">44. Disable PHP Error Reporting</h3>
<p>Hide errors in production.</p>
<div class="sourceCode" id="cb20">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true"></a><span class="co">// In wp-config.php</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;WP_DEBUG&#39;</span><span class="ot">,</span> <span class="kw">false</span> <span class="ot">);</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;WP_DEBUG_DISPLAY&#39;</span><span class="ot">,</span> <span class="kw">false</span> <span class="ot">);</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;WP_DEBUG_LOG&#39;</span><span class="ot">,</span> <span class="kw">true</span> <span class="ot">);</span></span></code></pre>
</div>
<h3 id="implement-content-security-policy">45. Implement Content Security Policy</h3>
<p>Add CSP headers to prevent XSS.</p>
<h3 id="enable-hsts">46. Enable HSTS</h3>
<p>Force HTTPS at browser level.</p>
<div class="sourceCode" id="cb21">
<pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true"></a>Header<span class="st"> set Strict-Transport-Security &quot;max-age=31536000; includeSubDomains; preload&quot;</span></span></code></pre>
</div>
<h3 id="configure-fail2ban">47. Configure Fail2Ban</h3>
<p>Block IPs with repeated failed attempts (VPS/dedicated servers).</p>
<h3 id="ddos-protection">48. DDoS Protection</h3>
<p>Use Cloudflare or similar CDN with DDoS mitigation.</p>
<h3 id="regular-security-audits">49. Regular Security Audits</h3>
<p>Quarterly professional security audits.</p>
<h3 id="stay-informed">50. Stay Informed</h3>
<p>Subscribe to WordPress security bulletins and CVE databases.</p>
<h2 id="implementation-priority">Implementation Priority</h2>
<p><strong>High Priority (Do Immediately):</strong> Steps 1-10, 13, 15, 21, 31</p>
<p><strong>Medium Priority (This Week):</strong> Steps 11-12, 14, 16-20, 22-30, 32-40</p>
<p><strong>Low Priority (This Month):</strong> Steps 41-50</p>
<p>Security is an ongoing process, not a one-time task. Implement these steps systematically, test thoroughly, and maintain vigilance through regular monitoring and updates.</p>
<h2 id="external-links">External Links</h2>
<ol type="1">
<li><a href="https://wordpress.org/support/article/hardening-wordpress/">WordPress Security Documentation</a></li>
<li><a href="https://wordpress.org/plugins/wordfence/">Wordfence Security Plugin</a></li>
<li><a href="https://wordpress.org/plugins/sucuri-scanner/">Sucuri Security</a></li>
<li><a href="https://owasp.org/www-project-top-ten/">OWASP Top 10</a></li>
<li><a href="https://wpscan.com/">WPScan Vulnerability Database</a></li>
</ol>
<h2 id="call-to-action">Call to Action</h2>
<p>Secure your site with bulletproof backups! <a href="https://backupcopilotplugin.com/">Backup Copilot Pro</a> offers automated security audits, malware scanning before backups, and instant recovery—try it free!</p>
<p>The post <a href="https://developryplugins.com/wordpress-security-checklist-50-steps-to-harden-your-website/">WordPress Security Checklist: 50 Steps to Harden Your Website</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
