Codeskill

Learn to code, step by step

Security – XSS CSRF awareness safe DOM APIs

XSS, CSRF awareness, and DOM APIs that do not paint you into a corner. Server security matters too, but client code is often where mistakes become visible exploits.

Cross-site scripting (XSS)

XSS means running attacker-controlled script in your page’s origin. That can steal cookies, call your API as the user, or rewrite the UI. It usually arrives via unsanitised HTML, URLs, or JSON rendered into the DOM.

Dangerous APIs

// avoid - parses HTML and runs script in many cases
element.innerHTML = userComment;

// avoid - same problem
document.write(markup);

// avoid - javascript: URLs
link.href = userSuppliedUrl;

Prefer text nodes and safe setters when displaying user content.

// safe for plain text
element.textContent = userComment;

// safe when you control the attribute semantics
img.src = sanitisedUrl;
img.alt = userLabel;

When you need HTML from users

Rich text is hard. Options: sanitise on the server with a maintained allowlist library, use Markdown rendered to safe HTML server-side, or restrict input to a small subset of tags. Client-only sanitisation is a last resort – attackers can bypass it by hitting your API directly.

// if you must insert structured nodes you built yourself
const li = document.createElement('li');
li.textContent = item.title;
list.appendChild(li);

// DocumentFragment for batches - still no raw HTML strings
const frag = document.createDocumentFragment();
items.forEach((item) => {
  const row = document.createElement('tr');
  const cell = document.createElement('td');
  cell.textContent = item.name;
  row.appendChild(cell);
  frag.appendChild(row);
});
tbody.appendChild(frag);

Content Security Policy (CSP)

CSP is a HTTP header that limits where script and resources may load from. It is the best defence-in-depth against XSS – even if you slip up once, inline script may be blocked.

// example policy (set by server, not in JS)
// Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'

// avoid inline handlers in HTML when CSP is strict
// bad: <button onclick="deleteItem()">
// good: addEventListener in your module

Hash or nonce-based CSP allows specific inline scripts if you must. Plan CSP when the project starts – retrofitting is painful.

CSRF awareness on the client

CSRF tricks a logged-in user’s browser into making unwanted requests to your site. The fix is mostly server-side: SameSite cookies, CSRF tokens, checking Origin/Referer. The client still has jobs to do.

  • Send CSRF tokens in headers or form fields when your backend expects them.
  • Use fetch with credentials: 'same-origin' deliberately – know when cookies go with the request.
  • Never put secrets in URLs where they leak via Referer headers.
  • For state-changing requests, prefer POST/PUT/PATCH/DELETE with proper headers – do not change data with GET.
const token = document.querySelector('meta[name="csrf-token"]')?.content;

await fetch('/api/tasks', {
  method: 'POST',
  credentials: 'same-origin',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': token,
  },
  body: JSON.stringify({ text: 'Buy milk' }),
});

Open redirects and URL handling

function safeRedirect(path) {
  // only allow relative paths on your origin
  if (!path.startsWith('/') || path.startsWith('//')) {
    throw new Error('Invalid redirect');
  }
  location.assign(path);
}

Passing ?next=https://evil.example into location.href is a classic foot-gun.

Third-party script and supply chain

Every analytics snippet, chat widget, or ad tag runs with your page’s privileges. Load fewer third-party scripts. Use Subresource Integrity (SRI) on CDN assets when you can. Audit what you embed.

localStorage is not for secrets

Anything in localStorage or sessionStorage is readable by script on your origin – including XSS payloads. Do not store refresh tokens or personal data there without understanding the risk. HttpOnly cookies are for secrets the JS layer should not touch.

The next tutorial covers performance profiling and tracking down memory leaks.

PreviousStreams and large data in the browser