Codeskill

Learn to code, step by step

Security in markup – XSS, rel, sandboxed iframes

Security concerns that start in markup: cross-site scripting (XSS) vectors, safe linking with rel, and sandboxed iframes. HTML is not executable logic on its own, but it is the delivery mechanism for scripts, URLs, and embedded content. Treat untrusted input as hostile.

XSS and untrusted HTML

XSS happens when attacker-controlled strings become live HTML or JavaScript in the browser. Classic vectors include unescaped user comments, search query reflection, and CMS fields rendered raw. The fix is not ‘be careful’ – it is never inserting untrusted data into HTML without encoding.

In PHP or templating engines, escape on output according to context:

  • Text content: HTML-encode <, >, &, quotes
  • Attribute values: encode for attributes (including quotes)
  • URLs: validate scheme (block javascript:) and encode
  • JavaScript strings: never build JS by concatenating user input
<!-- Bad: raw user input -->
<p>Hello, <?php echo $_GET['name']; ?></p>

<!-- Good: escaped text -->
<p>Hello, <?php echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8'); ?></p>

Content Security Policy (CSP) is set in HTTP headers, not HTML, but markup habits must align with it. Inline onclick handlers and inline <script> blocks break strict CSP unless you nonce them. Prefer external scripts and event listeners added from a trusted file.

Dangerous patterns to avoid

  • innerHTML with unsanitised strings
  • <script> or event handler attributes from CMS ‘custom HTML’ fields
  • javascript: URLs in href or src
  • Third-party widgets pasted into templates without review

Link security with rel

When a link opens a new browsing context or points off-site, rel attributes reduce abuse:

<a href="https://partner.example/docs"
   target="_blank"
   rel="noopener noreferrer">
  Partner documentation
</a>

<a href="/user/123/profile"
   rel="nofollow ugc">
  User profile
</a>

noopener stops the new page from reaching window.opener – a tab-nabbing risk. noreferrer also omits the Referer header (with side effects for analytics). nofollow and ugc signal to search engines that you do not vouch for the target; they are not security controls, but they belong in markup for user-generated links.

Sandboxed iframes

Embedding untrusted HTML (ads, user uploads, third-party widgets) belongs in a sandboxed iframe with the minimum permissions required:

<iframe
  src="https://embed.example/widget"
  title="Booking calendar"
  sandbox="allow-scripts allow-same-origin"
  referrerpolicy="strict-origin-when-cross-origin"
  loading="lazy"
  width="400" height="300">
</iframe>

An empty sandbox attribute applies maximum restriction. Each allow-* token relaxes one capability. allow-scripts plus allow-same-origin together can allow escape from the sandbox if the embedded document is same-origin – treat that combination as trusted code only. Prefer srcdoc with escaped static HTML for tiny embeds when you control the content entirely.

Trust boundaries in templates

Mark in your codebase which variables are trusted HTML (rare) versus plain text (almost everything). WordPress functions like esc_html(), esc_attr(), and esc_url() exist for a reason – mirror that discipline in any stack. Security in markup is mostly discipline at the boundary where data becomes tags.

PreviousPerformance-minded markup