Course outline

React / Performance and trustworthy boundaries / Lesson 17

Security and trusted boundaries

Use React’s text rendering safely while keeping authorization, URL policy, raw HTML, and dependency trust under explicit control.

About 4 min readContent reviewed
On this page

Lesson goals

  • Start with what an input can control
  • Verify ordinary strings remain text
  • Protect the real operation and supply chain

Start with what an input can control

List untrusted inputs such as form text, route parameters, API responses, rich content, and dependency updates. Trace each to its output or capability: text, URL, raw HTML, storage, or a server operation. React’s ordinary string rendering prevents that string from being interpreted as markup, but this useful default does not validate all downstream contexts. A text value placed into a URL prop requires a URL policy, and a field sent to a server action requires server validation and authorization. Avoid a universal sanitize function whose meaning is unclear across these different boundaries. Each context needs its own narrow contract.

Keep formatted content structured where possible

Prefer components for known formatting instead of concatenating HTML strings. dangerouslySetInnerHTML bypasses normal text rendering and requires a trustworthy sanitization and content policy suitable for the source and deployment. A regular expression is not a general HTML sanitizer. Content Security Policy can provide defense in depth but does not replace correct handling or authorization. Do not copy raw API content into the escape hatch merely because it was returned by your own endpoint; the endpoint may be carrying user-authored content. Keep the origin, transformations, and permitted markup contract reviewable, including how updates to the sanitizer or parser are maintained.

Verify ordinary strings remain text

The first example displays a string containing harmless markup-looking characters. The visible result includes the angle brackets, and the browser should not contain a strong element created from that string. This is an isolated demonstration of text rendering, not a comprehensive security test. Keeping content structured also helps accessibility because actual headings, labels, and links can be chosen deliberately rather than accepting arbitrary markup. Do not suppress meaningful validation errors or expose sensitive records in console logs while debugging. Error feedback should give the user a correction path without disclosing tokens, private queries, or internal implementation details unnecessarily.

jsx Component module — requires the course React host
const text = "<strong>draft</strong>";
export default function App() { return <main><h1>Text boundary</h1><p>{text}</p></main>; }
Expected output
Visible paragraph: <strong>draft</strong>. No strong element is created from the string.

The string is a text child. Its bracketed content is visible rather than becoming a strong element.

Constrain navigation destinations deliberately

The second example accepts only HTTPS URLs on one synthetic approved origin before rendering a link. It uses the URL parser rather than a prefix check that can confuse host boundaries. This policy suits the fixture, not every application: relative links, allowed external destinations, redirects, and download behavior require a documented product policy. A safe browser link policy does not establish SSRF protection for a server that fetches a URL, where DNS resolution, internal addresses, redirects, and capabilities introduce additional concerns. Never assume React validates those semantics merely because a URL is passed through a JSX expression.

jsx Component module — requires the course React host
export function approvedHref(value) { try { const url = new URL(value); return url.protocol === "https:" && url.origin === "https://docs.example.test" ? url.href : null; } catch { return null; } }
export default function App() { const href = approvedHref("https://docs.example.test/guide"); return <main><h1>Approved destination</h1>{href ? <a href={href}>Read guide</a> : <p>Destination unavailable</p>}</main>; }
Expected output
Read guide links to https://docs.example.test/guide.

The fixture only computes and renders a link; it makes no request. The exact parsed origin and protocol must satisfy its explicit policy.

Protect the real operation and supply chain

Client UI permissions are guidance, not enforcement. Authenticate and authorize the resource operation on the trusted server, validate inputs, and design CSRF and session behavior according to the chosen framework and deployment. Keep secrets out of delivered bundles and serialized props. Review dependency identity, lockfile changes, supported versions, and official advisories; installing a package can execute code and a clean audit report is not proof of safety. Test negative cases only in authorized isolated fixtures. A secure component API should make dangerous choices explicit and avoid spreading raw HTML or unrestricted URL capabilities through otherwise ordinary presentation props.

Common errors and fixes

Raw content becomes executable markup

Why it happens
An untrusted value entered the raw HTML escape hatch.
How to fix it
Use text or a reviewed content sanitization boundary.
Prevent it next time
Keep raw HTML capabilities explicit and limited.

A hidden admin button is treated as access control

Why it happens
Only client presentation checks exist.
How to fix it
Authorize the server operation independently.
Prevent it next time
Test direct resource access with isolated roles.

A URL prefix check accepts the wrong host

Why it happens
String prefixes were mistaken for parsed origin identity.
How to fix it
Parse the URL and enforce an explicit policy.
Prevent it next time
Include confusing host and protocol cases.

Check your understanding

1. Does rendering a user’s text safely prove that a server action receiving it is safe?

Show worked solution

No. Text rendering addresses a browser markup context. The server must independently validate type and domain constraints, authenticate and authorize the operation, and control its database or other capabilities. One boundary’s guarantee cannot be transferred to another context without analysis.

2. Why avoid a single unrestricted html prop in a broadly reused card component?

Show worked solution

It distributes an escape hatch to many callers and makes the content trust contract easy to overlook. Prefer structured children for normal content and isolate any justified rich-HTML renderer behind a clearly named, reviewed API with an enforced sanitization policy.

Key takeaways

  • React’s text default is useful but context-specific.
  • Authorize at the real server boundary.
  • Make raw HTML and URL capabilities explicit.

Official references