Think like an attacker
Security starts with a question: *what if the input is hostile?* A café review that says <img src=x onerror="steal(document.cookie)"> becomes code in every visitor's browser if you paste it into HTML — cross-site scripting (XSS). The fix is to treat data as data: escape it for the place it's going.
- HTML text: escape & < > " '
- URLs in href: only allow http: and https: (block javascript:)
- Frameworks like React escape by default — dangerouslySetInnerHTML is named that for a reason
Escaping by default
const ESCAPES = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
function escapeHtml(value) {
return String(value).replace(/[&<>"']/g, (c) => ESCAPES[c]);
}
// A tagged template: html`<p>${name}</p>` escapes every interpolated value.
function html(strings, ...values) {
return strings.reduce((out, s, i) => out + s + (i < values.length ? escapeHtml(values[i]) : ""), "");
}
function safeUrl(url) {
try {
const u = new URL(url, "https://sikau.com");
return u.protocol === "https:" || u.protocol === "http:" ? u.href : "#";
} catch {
return "#";
}
}