Browser Security Best Practices

Chapter 1 · Practice 2 of 4

Use text sinks and validate URLs before they reach href or src

From Cross-site scripting and Trusted Types

Why it matters

OWASP's advice is to refactor away from unsafe sinks: "use textContent or value" instead of innerHTML. Text sinks never parse HTML, so there is nothing to escape.

Instead ofUse
el.innerHTML = nameel.textContent = name
building markup stringsdocument.createElement and properties
dangerouslySetInnerHTML for textnormal JSX children
setTimeout('fn()', 100)setTimeout(fn, 100)

URLs need their own check. Escaping does not stop javascript:alert(1) in an href, because it contains no special HTML characters. OWASP says to allowlist http and https URLs, and notes that React 19 blocking javascript: URLs does not replace application-specific URL validation. Parse the URL with the URL constructor and check its protocol, rather than testing the string with a regular expression.

When the content genuinely needs HTML (a comment with formatting), sanitize it with a maintained library. OWASP recommends DOMPurify, kept up to date, with its output used as-is.

Example

TSX
export function safeHref(input: string, base = window.location.origin) {
  try {
    const url = new URL(input, base);
    return url.protocol === 'https:' || url.protocol === 'http:' ? url.href : '#';
  } catch {
    return '#';
  }
}

// React: plain children are escaped, the URL is checked
<a href={safeHref(profile.website)} rel="noopener noreferrer">{profile.name}</a>