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

XSS in One Minute

Cross-site scripting (XSS) happens when attacker-controlled content is interpreted as code in someone else's browser.

Dangerous idea:

html
<!-- 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:

text
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:

html
<img src=x onerror=alert(1)>

Safe output should display it as text:

html
&lt;img src=x onerror=alert(1)&gt;

In templates and frameworks, use their default escaping features. Avoid APIs and template options with names like:

  • innerHTML
  • dangerouslySetInnerHTML
  • raw HTML filters
  • unescaped template output

Use them only with trusted, sanitized content.

Dangerous Inline Event Attributes

Avoid inline handlers for real apps:

html
<!-- Avoid -->
<button onclick="deleteAccount()">Delete account</button>

Prefer external JavaScript:

html
<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

If you open a third-party site in a new tab:

html
<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.

Common safe schemes:

html
<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:

text
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:

html
<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:

AllowanceEnables
allow-scriptsScripts inside iframe
allow-formsForm submission
allow-same-originTreat content as same origin
allow-popupsPopup 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:

html
<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:

html
<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:

html
<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:

html
<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:

html
<!-- 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:

html
<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 _blank links
  • Validate user-provided URLs before rendering
  • Use sandbox for untrusted iframes
  • Submit sensitive forms over HTTPS
  • Remember accept on file inputs is not security
  • Keep secrets out of HTML
  • Consider CSP for production sites

Mini Exercise

Review one practice page and answer:

  1. Does any link use target="_blank" without rel?
  2. Do any comments contain information that should not be public?
  3. Are file inputs labeled and restricted with accept?
  4. Are external iframes sandboxed where possible?
  5. 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.