HTML Security Basics
Introduction
HTML is not executable code in the same way JavaScript is, but unsafe markup can still create serious security problems. Cross-site scripting (XSS), unsafe links, untrusted iframes, insecure form submission, and careless user-generated HTML can expose users and data. This chapter gives you a practical HTML-level security checklist.
Prerequisites
- HTML performance basics
- Links and navigation
- Audio, video, and iframe
- Basic awareness that JavaScript can manipulate the DOM
XSS in One Minute
Cross-site scripting (XSS) happens when attacker-controlled content is interpreted as code in someone else's browser.
Dangerous idea:
<!-- Imagine this came from a user comment -->
<script>stealCookies()</script>If your app inserts that as real HTML, the browser may run it.
HTML authors rarely write the backend sanitizer, but you must understand the rule:
User input is text until a trusted sanitizer proves otherwise.Warning
Never Trust User HTML
Do not directly render user-submitted HTML. Escape it, sanitize it with a trusted library, or store it as plain text.
Escaping User Content
If a user types:
<img src=x onerror=alert(1)>Safe output should display it as text:
<img src=x onerror=alert(1)>In templates and frameworks, use their default escaping features. Avoid APIs and template options with names like:
innerHTMLdangerouslySetInnerHTML- raw HTML filters
- unescaped template output
Use them only with trusted, sanitized content.
Dangerous Inline Event Attributes
Avoid inline handlers for real apps:
<!-- Avoid -->
<button onclick="deleteAccount()">Delete account</button>Prefer external JavaScript:
<button type="button" id="delete-account">Delete account</button>
<script src="js/account.js" defer></script>Reasons:
- easier to audit
- easier to test
- works better with Content Security Policy
- avoids mixing behavior into markup
Safe External Links
If you open a third-party site in a new tab:
<a
href="https://example.com"
target="_blank"
rel="noopener noreferrer"
>
Visit Example
</a>noopener prevents the opened page from controlling the original page through window.opener.
noreferrer may hide the referring URL. Use both for unknown external links opened with _blank.
Link Schemes to Treat Carefully
Common safe schemes:
<a href="https://example.com">Website</a>
<a href="mailto:support@example.com">Email support</a>
<a href="tel:+15551234567">Call support</a>Avoid letting users provide arbitrary href values. Dangerous or suspicious schemes may include:
javascript:
data:
vbscript:Modern browsers restrict many cases, but validation should happen before rendering user-provided links.
iframes and sandbox
Embedding third-party content creates risk. Use sandbox when possible:
<iframe
src="https://third-party.example/widget"
title="Third-party widget"
sandbox="allow-scripts allow-same-origin"
width="600"
height="400"
></iframe>Without allowances, sandbox heavily restricts the iframe. Add permissions only when needed:
| Allowance | Enables |
|---|---|
allow-scripts | Scripts inside iframe |
allow-forms | Form submission |
allow-same-origin | Treat content as same origin |
allow-popups | Popup windows |
Be careful combining allow-scripts and allow-same-origin for content you do not control; it can weaken isolation.
Forms and HTTPS
Sensitive forms must submit over HTTPS:
<form action="https://example.com/login" method="post">
<label for="password">Password</label>
<input id="password" name="password" type="password" autocomplete="current-password">
<button type="submit">Log in</button>
</form>Avoid:
<form action="http://example.com/login" method="post">HTTP sends data without transport encryption. Anyone on the network path may inspect or modify it.
Also remember: frontend validation is not security. Servers must validate and authorize every request.
File Upload Forms
File inputs are common attack surfaces:
<form action="/upload" method="post" enctype="multipart/form-data">
<label for="avatar">Profile image</label>
<input id="avatar" name="avatar" type="file" accept="image/png,image/jpeg">
<button type="submit">Upload</button>
</form>accept is only a hint. The server must verify file type, size, extension, content, and storage location.
Content Security Policy (Awareness)
Content Security Policy (CSP) limits where scripts, styles, images, and frames can load from. It is usually sent as an HTTP header, but can also be expressed in HTML for simple cases:
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src 'self' https:; script-src 'self'; style-src 'self'"
>Production CSP is usually configured on the server, not handwritten in every page.
Benefits:
- reduces XSS impact
- blocks unexpected third-party scripts
- documents trusted origins
CSP is powerful and easy to misconfigure. Introduce it gradually and test carefully.
Avoid Leaking Secrets in HTML
Anything in HTML is public:
<!-- Never do this -->
<script>
const apiKey = "secret-production-key";
</script>Do not put secrets in:
- comments
- hidden inputs
data-*attributes- inline scripts
- meta tags
Public identifiers are fine. Secret credentials belong on the server or in secure runtime configuration.
User-Generated Content
If your site displays user content:
- escape plain text by default
- sanitize allowed HTML with a vetted sanitizer
- strip event attributes like
onclick - block dangerous URL schemes
- avoid allowing arbitrary iframes or scripts
- set
rel="ugc nofollow"on user-submitted external links when appropriate
Example user link:
<a href="https://user-example.com" rel="ugc nofollow">User shared link</a>ugc means user-generated content. nofollow tells search engines not to treat it as an endorsement.
Security Checklist for HTML Authors
- Escape user input by default
- Do not render raw user HTML without sanitization
- Avoid inline event handlers
- Use
rel="noopener noreferrer"with external_blanklinks - Validate user-provided URLs before rendering
- Use
sandboxfor untrusted iframes - Submit sensitive forms over HTTPS
- Remember
accepton file inputs is not security - Keep secrets out of HTML
- Consider CSP for production sites
Mini Exercise
Review one practice page and answer:
- Does any link use
target="_blank"withoutrel? - Do any comments contain information that should not be public?
- Are file inputs labeled and restricted with
accept? - Are external iframes sandboxed where possible?
- Could any visible content come from a user? If yes, should it be escaped?
FAQ
Is HTML itself dangerous?
HTML is mostly declarative, but it can load scripts, embed third-party pages, submit forms, and interpret unsafe markup. Context matters.
Is type="hidden" secure?
No. Hidden inputs are visible and editable in DevTools. Treat them as convenience, not security.
Does HTTPS prevent XSS?
No. HTTPS protects transport. XSS is about unsafe content executing in the browser. You need escaping, sanitization, and CSP.
Can CSP replace sanitization?
No. CSP reduces impact but does not make unsafe HTML safe. Sanitize and escape first.
What comes next?
Project: personal profile page — apply the core HTML skills in a small real page.