← Back to Learning Hub

Lesson 8: Secure Software Architecture II

The Trust Boundary — Client-Side vs Server-Side Security

65 minutes Year 12 Content High Exam Priority

Part 1: Homework Review — Your Password Implementation 10 min

What You Did Well

Walk me through what you built for Volcanic Pantheon. Before we look at any problems, let's acknowledge the good decisions:

  • SHA-256 hashing — You remembered from Lesson 7 that passwords should never be stored in plaintext. You used crypto.subtle.digest('SHA-256', ...) to hash the input before comparing it.
  • Protection on every page — You created a protect.js file that runs on each protected page. That's good modular thinking.
  • Redirect mechanism — Unauthorized visitors get sent back to the login page automatically.
  • GitHub repo made private — You recognised the risk after your friend found the site, and you acted on it.

Let's Look at Your Code

Here's the core of your authentication system:

auth.js — Your Login Logic
const PASSWORD_HASH = "299957fa31b8e7784f4f7972436d9d4db8667f1bb7859faa4ec64d45c98861fb";

async function checkPassword() {
    const input = document.getElementById("password").value;
    const data = new TextEncoder().encode(input);
    const buffer = await crypto.subtle.digest("SHA-256", data);

    const hash = [...new Uint8Array(buffer)]
        .map(b => b.toString(16).padStart(2, "0"))
        .join("");

    if (hash === PASSWORD_HASH) {
        localStorage.setItem("authenticated", "true");
        window.location.href = "Main/HomePage.html";
    } else {
        // Show error and shake animation
    }
}
protect.js — On Every Protected Page
(function() {
    if (localStorage.getItem("authenticated") !== "true") {
        window.location.href = "../login.html";
    }
})();

Can you spot the problem?

Take a moment. Look at protect.js. What does it check? Where does that value come from? Who controls it?

Live Demo: Breaking In (5 seconds)

Let's do this together on your actual site right now:

1. Open Volcanic Pantheon in the browser

2. Try to visit a protected page — get redirected to login (expected)

3. Open DevTools (F12) → Console

4. Type: localStorage.setItem("authenticated", "true")

5. Press Enter. Refresh the page.

6. Full access granted. No password needed.

The Second Attack Vector

Even if you somehow fixed the localStorage bypass, there's another problem:

  • Right-click → View Page Source (or DevTools → Sources tab)
  • Find the SHA-256 hash 299957fa... sitting right there in auth.js
  • Take that hash to an online SHA-256 lookup tool — if the password is common, it's cracked in seconds
  • Or skip the hash entirely: just delete the protect.js script tag from the DOM, or set a breakpoint and skip the check

Key Takeaway

This is not a flaw in your coding. Your code is actually well-written. The problem is where the code runs. This is a fundamental architectural problem — and that's what we're here to understand today.

Part 2: AAA Scenario Analysis — Lesson 7 Review 10 min

Let's Analyse Volcanic Pantheon with the AAA Model

Remember the AAA Model from last lesson? Let's apply it to your website. I want you to lead this — fill in the table together:

AAA Component What Volcanic Pantheon Does What It Should Do
Authentication
"Who are you?"
??? ???
Authorisation
"What can you do?"
??? ???
Accountability
"What did you do?"
??? ???

Discussion Prompts

  • Authentication: Right now, if your friend and your mum both have the password, can you tell who logged in?
  • Authorisation: If you wanted to let friends see some pages but keep the Frauds tab private, could your current system handle that?
  • Accountability: When your friend found your site through shoulder surfing, how did you find out? Was it the system that told you, or the friend?

Analysis (Click to Reveal)

Authentication

Current: SHA-256 hash comparison in JavaScript. Single shared password for everyone. No usernames — there is no identity, just a shared secret. No MFA.

Should be: Server verifies identity. Each user has their own credentials. Password is never sent to or stored on the client.

Authorisation

Current: Binary — you're either "authenticated" (see everything) or not (see nothing). No roles, no permissions.

Should be: Different users see different content. Role-based access control (RBAC) — some pages public, some require login, some admin-only.

Accountability

Current: Nothing. Zero logging. No record of who accessed what, when, or from where.

Should be: Login events logged with timestamps and IP addresses. Page access tracked. Failed login attempts recorded.

Summary: Volcanic Pantheon has weak authentication (shared secret, client-side), no authorisation (all-or-nothing), and zero accountability (no logs). This AAA analysis is exactly what the HSC wants you to do in exam scenarios.

Part 3: Client-Side vs Server-Side Security 15 min

The Browser is the Attacker's Playground

The fundamental principle: Everything the browser receives, the user controls.

Let's look at every tool an attacker has, using your site as the example:

1. Source Code Visibility

Everything in HTML, CSS, and JavaScript is delivered to the browser in full. "View Source" shows it all. Minification (making code smaller) just makes it harder to read, not impossible.

Your SHA-256 hash, your protect.js logic, your page structure — all visible to anyone.

Think About This

When you made your GitHub repo private, did that stop people from seeing your source code in the browser?

  • The repo being private only affects GitHub. Once deployed, Vercel serves everything publicly.

2. DevTools Manipulation

Every browser ships with powerful developer tools that give the user full control:

  • Console: Execute any JavaScript (localStorage.setItem(...))
  • Elements: Modify the DOM live (delete scripts, unhide hidden elements)
  • Network: See every request, every response, every cookie
  • Sources: Set breakpoints, step through code, modify variables mid-execution
  • Application: Read/write localStorage, sessionStorage, cookies
What an attacker can do in DevTools Console on Volcanic Pantheon
// Attack 1: Bypass authentication
localStorage.setItem("authenticated", "true");

// Attack 2: If a tab was hidden with CSS
document.querySelector('.hidden-tab').style.display = 'block';

// Attack 3: If a JS variable controlled access
window.isAdmin = true;

// Attack 4: Remove the protection script entirely
document.querySelector('script[src="protect.js"]').remove();

3. Network Request Interception

Even if you added an API call to "check" the password, an attacker could intercept the response and change it. Browser extensions can modify request and response headers. This is why the check must happen server-side where the user cannot interfere.

What Server-Side Auth Looks Like

Let's contrast your approach with how real authentication works:

CLIENT-SIDE AUTH (Your current approach):
=========================================
Browser has: password hash, check logic, ALL page content
Attacker can: see hash, skip check, access content directly

[Browser] ──── loads protect.js ──── checks localStorage ──── shows/hides page
    ▲                                       ▲
    │                                       │
    Everything happens HERE.          Can be bypassed HERE.
    The user controls all of this.


SERVER-SIDE AUTH (Proper approach):
====================================
Browser has: login form. That's it.
Attacker can: submit guesses. That's it.

[Browser] ──── sends password ────▶ [SERVER checks hash] ──── sends page content
                                          │
                                     Attacker CANNOT
                                     reach this code.
                                     It runs on YOUR
                                     server, not their
                                     browser.

The key insight: With server-side auth, the protected page content literally does not exist in the browser until the server decides to send it. There is nothing to bypass because there is nothing there yet.

Why Your Specific Improvements Didn't Help

What You Did Why It Feels Secure Why It Isn't
Used SHA-256 hash Hash can't be reversed Attacker doesn't need to reverse it — just bypass the check entirely
Made GitHub repo private Source code hidden on GitHub Source code still fully visible in the browser via View Source
Changed hosting platform (Netlify → Vercel) Different hosting, new URL Same client-side architecture, same vulnerability
Created protect.js on every page Consistent protection All pages load full content first, then check — content can be intercepted

The Pattern

Every fix you tried was still on the client side. It's like putting a stronger lock on a glass door — the lock is great, but the attacker can see through the glass and go around it.

Part 4: How to Actually Secure a Static Site 15 min

Practical Solutions for Volcanic Pantheon

You're already on Vercel, so you have real options. Here are three approaches, from simplest to most powerful:

Option 1: Vercel Edge Middleware (Simplest)

Create a middleware.js file in your project root:

middleware.js — server-side password protection on Vercel
export default function middleware(request) {
  const authHeader = request.headers.get('authorization');

  if (authHeader && authHeader.startsWith('Basic ')) {
    const encoded = authHeader.slice(6);
    const decoded = atob(encoded);
    const [username, password] = decoded.split(':');

    if (username === 'blake' && password === 'securepassword123') {
      return; // credentials correct — let the request through
    }
  }

  return new Response('Authentication Required', {
    status: 401,
    headers: {
      'WWW-Authenticate': 'Basic realm="Volcanic Pantheon"',
    },
  });
}

export const config = {
  matcher: ['/main/:path*'], // only protect /main/ pages
};

This is server-side. Vercel runs this middleware before serving any page. The page content never reaches the browser without authentication. No JavaScript, no localStorage, no bypass.

Option 2: StatiCrypt (Encryption-Based)

Remember encryption from Lesson 7? StatiCrypt uses it to protect static pages:

  1. You run StatiCrypt on your HTML file with a password
  2. It encrypts the entire page content with AES-256
  3. The deployed page contains only a password prompt and an encrypted blob
  4. Correct password = page decrypts in the browser
  5. Wrong password = content is genuinely unreadable gibberish

Why StatiCrypt Is Different From Your Approach

  • Your current system: All page content is loaded and then "hidden" by JavaScript. The content is there — just behind a flimsy gate.
  • StatiCrypt: The content is genuinely encrypted. Without the password, it's mathematical gibberish. There is nothing to bypass.

Option 3: Vercel Password Protection (Most Powerful)

  • Built-in site-wide password protection managed through the Vercel dashboard
  • One click to enable — Vercel enforces it at the edge before any content is served
  • No code to write; no bypass possible from the browser
  • Note: requires the Vercel Pro plan (not free tier)

Environment Variables on Vercel

For when you eventually build anything with an API or server:

  • Vercel has an environment variables section in its project dashboard
  • You can store API keys and secrets there — they're injected at build time or runtime
  • They never appear in your source code or in the browser

The Golden Rule

 ╔══════════════════════════════════════════════════════════╗
 ║                                                          ║
 ║              "NEVER TRUST THE CLIENT."                   ║
 ║                                                          ║
 ╚══════════════════════════════════════════════════════════╝

 TRUST BOUNDARY
 ══════════════════════════════════════════════════════════

   Client (Browser)           │  Server
   ─────────────────          │  ──────────────────
   • User controls            │  • You control
   • Can be inspected         │  • Hidden from user
   • Can be modified          │  • Tamper-proof
   • NEVER trust              │  • ALWAYS verify here
                              │
   ← Attacker's territory →   │  ← Your territory →

Memorise This

  • Never store secrets in client-side code
  • Never rely on client-side checks for security
  • Always validate on the server, even if you also validate on the client
  • Assume the user has full control over everything in their browser

Part 5: Knowledge Check 15 min

HSC-Style Questions

  • Q1 (2 marks): Blake's website uses localStorage.setItem("authenticated", "true") to track login status. Explain TWO reasons why this is an insecure approach.
  • Q2 (3 marks): Explain why client-side input validation alone is insufficient for web security. In your answer, reference the relationship between the client and server.
  • Q3 (3 marks): A developer is choosing between two approaches to protect a page on their Vercel static site: (A) a JavaScript function that checks a password hash stored in the source code, or (B) a middleware.js file that checks credentials server-side before serving the page. Explain why approach B provides stronger security than approach A.

Model Answers (Click to Reveal)

Q1: localStorage Insecurity

Reason 1: localStorage is accessible from the browser's DevTools console, meaning any user can set the value to "true" without knowing the password, completely bypassing the authentication system.

Reason 2: localStorage data persists indefinitely and is not encrypted, so the authentication state can be read or modified by any JavaScript running on the same origin, including potentially malicious scripts.

Q2: Client-Side Validation Insufficiency

Client-side validation runs in the user's browser, which the user has full control over. They can disable JavaScript, modify the validation code via DevTools, or send requests directly to the server bypassing the browser entirely.

An attacker can intercept and modify network requests between the browser and server, sending malicious data even if client-side validation would normally block it.

Therefore, server-side validation is essential because the server is the only component the developer controls. Client-side validation improves user experience (fast feedback) but must always be complemented by server-side validation for security.

Q3: Client-Side vs Server-Side Protection

Approach A (JavaScript hash check) runs entirely in the browser. The protected page content is delivered to the browser before the password is checked, and the check itself can be bypassed by manipulating localStorage, removing the script tag via DevTools, or setting a breakpoint to skip the redirect. The password hash is also visible in the source code.

Approach B (Vercel middleware) is enforced server-side. Vercel runs the middleware before sending any page content. If the credentials are incorrect, the page is never transmitted to the browser. There is nothing on the client to bypass because the protected content literally does not exist in the browser until the server authorises the request.

The key principle is "never trust the client" — any security check that runs in the browser can be circumvented by the user, whereas server-side checks run in an environment the attacker cannot access.

What You've Learned Today

  • Your password system — Well-coded but architecturally flawed because it runs client-side
  • AAA Analysis — How to evaluate a real system's Authentication, Authorisation, and Accountability
  • Client vs Server — The browser is the attacker's playground. Everything client-side can be inspected, modified, and bypassed
  • Static site solutions — Vercel Edge Middleware, StatiCrypt, Vercel Password Protection — why each is genuinely more secure than JavaScript checks
  • The golden rule — "Never trust the client." Security checks must happen server-side, in code the user cannot reach

Curriculum Progress

  • Year 12 Secure Software Architecture module — Part 2 complete
  • HSC Outcomes: SE-12-04 (full), SE-12-07 (full)
  • Your own website was the case study — this is the kind of applied knowledge that makes HSC answers stand out

Next lesson (Lesson 9): Web Vulnerabilities — a deep dive into specific attack types: hardcoded secrets, security through obscurity, shoulder surfing and social engineering, and XSS (Cross-Site Scripting). We'll also cover Kerckhoffs's principle and do five HSC-style exam questions together.

Homework Before Next Lesson

Security Improvement Tasks

Task 1: Fix the Server-Side Protection

Add a middleware.js file to your Volcanic Pantheon project and deploy it to Vercel. Your middleware should:

  • Intercept requests to your protected pages before any content is served
  • Return a 401 response with a WWW-Authenticate header if credentials are missing or wrong
  • Allow the request through only when the correct username and password are provided

The code from today's lesson is your starting point — adapt it to match your site's folder structure.

Task 2: Add Authorisation

Right now your site is all-or-nothing. Add a second layer so that different pages require different credentials (or at minimum, restrict access differently based on the URL path). For example:

  • Public pages stay fully accessible
  • One set of protected pages requires a standard password
  • A more restricted area (like an admin or secret page) requires a different, stronger password

Use the matcher config in your middleware to target the right paths.

Hint

You can have multiple if checks in your middleware based on request.url, or use separate matchers. The key is that the logic runs on Vercel's servers, not in the browser.

← Back to Blake's Learning Hub