freezr Security Model

freezr apps can be created by 3rd parties, and running third-party code on a server naturally sets off alarm bells! Here is the mechanism freezr uses to isolate different apps and allow them to run simultaneously on the same freezr.

Note: This is a technical document for developers who want to understand how freezr's security model works before trusting it with their data or code. It specifically addresses how client-side JavaScript can be run by different 3rd party apps on freezr, securely. Server-side JavaScript can only be run with server admin's permission or via bacvkground jobs (i.e. serverless instances.)

The Core Problem

When multiple applications run on the same origin (example.com), the browser's same-origin policy provides no isolation between them:

  • Apps can read each other's cookies
  • Apps can fetch each other's pages
  • Apps can make requests using each other's credentials
  • Apps share localStorage, sessionStorage, and the DOM

freezr uses path-based credential scoping combined with token-based API authentication to provide app isolation (without needing them to run on separate domains or sub domains).

Architecture Overview

Path structure

/account/home → "Account" functions (treated as an app even with trusted code, but is given access to 3rd party app data)
/admin/home   → "Admin" functions (treated as an app even with trusted code)
/apps/com.example.todo/dashboard.html        → Todo app (third-party code)
/apps/com.anotherdeveloper.calendar/app.html → Calendar app (third-party code)
/apps/com.bigbank.freezrbanking/accounts.html → Banking app (third-party code)

Each app gets its own path namespace. Credentials are scoped to these paths.

Note: Note: Public pages do not follow this security model - they are public, and don't even leave any coookies for unauthenticated users.

Authentication Flow for App Pages

User navigates to /apps/com.example.todo/dashboard.html ↓ Server validates session and user (global httpOnly cookie) ↓ Server generates short-lived token (days) ↓ Server sets token cookie: Path=/apps/com.example.todo ↓ Page loads, JS reads token from cookie ↓ API calls include token in Authorization header ↓ Server validates: token matches requested resource

Technical Implementation

1. Path-Scoped Cookies

Each app receives a token in a path-scoped cookie:

// Todo app at /apps/com.example.todo
Set-Cookie: todo_token=<random_string>;
            Path=/apps/com.example.todo;
            Secure;
            SameSite=strict;
            Max-Age=300

Properties:

  • Path=/apps/com.example.todo — Browser only sends cookie for requests to /apps/com.example.todo
  • httpOnly=false — JavaScript can read it (necessary for Authorization headers)
  • Token is opaque (random 32-byte string, not JWT)
  • Token stored server-side with user ID and app name

3. API Token Validation

API endpoints validate tokens against the requested resource:

// API request structure
POST /feps/write/com.example.todo.items/item_123
Authorization: Bearer <token>

// Server-side validation
1. Extract token from Authorization header
2. Lookup token in cache → get userId and appName
3. Verify appName has permission to access 'com.example.todo.items' collection
4. Verify session userId matches token userId
5. Process request

Each API call validates:

  • Token is valid and not expired
  • Token with combination of both appName and userId has permission to the requested resource
  • Token's userId matches current session. (This is disabled for offline apps like mobile apps.)

4. Server-Side Rendered Data Protection

For pages with server-side data, an additional check prevents malicious fetching of the web page:

// Banking app tries to fetch Todo dashboard:
fetch('/apps/com.example.todo/dashboard')

// Request headers:
Sec-Fetch-Mode: cors          // Browser-generated, cannot be forged
Sec-Fetch-Site: same-origin
Sec-Fetch-Dest: empty

// Server checks:
if (page has sensitive data && sec-fetch-mode !== 'navigate') {
  return 403; // Block JavaScript fetch
}

// Legitimate navigation:
// User clicks link or types URL
Sec-Fetch-Mode: navigate      // Only set by real navigation
// Server returns page with data

Sec-Fetch-Mode is a forbidden header — JavaScript cannot set or modify it. This provides cryptographic proof of legitimate navigation.

Browser support: Chrome/Edge/Opera since 2019 · Safari 17.4+ (March 2024) · Firefox 2023.
For browsers without Sec-Fetch-Mode, freezr doesn't allow data to be injected in HTML on the server side. A nonce-based algorithm can allow this, but as major browsers are supported, this is not a priority.

Content Security Policy (CSP)

Every app page served by freezr includes a strict Content Security Policy that limits what client-side code can do.

Default CSP for App Pages

script-src 'self' 'nonce-<random>'  — Only server-loaded scripts with matching nonce
style-src 'self' 'unsafe-inline'    — Styles from self, inline styles allowed
connect-src 'self'                  — Fetch/XHR restricted to same origin
img-src * data:                     — Images from any source
form-action 'self'                  — Forms can only submit to freezr server
default-src 'self'                  — Everything else restricted to same origin
frame-src 'none'                    — No iframes
child-src 'none'                    — No workers/nested browsing contexts
object-src 'none'                   — No plugins (Flash, Java, etc.)

This means by default, third-party app code cannot:

  • Execute injected inline scripts or event handlers (blocked by nonce)
  • Send data to external servers (blocked by connect-src)
  • Load scripts from external domains (blocked by script-src)
  • Use eval() or dynamic code generation (blocked by default)
  • Submit forms to external servers (blocked by form-action)
  • Embed iframes (blocked by frame-src)

Permission-Based CSP Relaxation

Some apps legitimately need capabilities that the default CSP blocks. freezr handles this through its permission system — the app declares what it needs in its manifest, and the user must explicitly grant each capability:

PermissionCSP ChangeUse Case
external_fetch connect-src relaxed to * App needs to call external APIs
external_scripts script-src adds https: App loads JS libraries from CDNs
unsafe_eval script-src adds 'unsafe-eval' App uses eval() or templating engines
allow_self_frames frame-src relaxed to 'self' blob: App needs same-origin iframes (e.g. preview panes)

Each relaxation is shown to the user with a risk warning before they grant it. Permissions can be revoked at any time.

Public Page CSP

Public pages (content shared without login) use a different, simpler CSP:

script-src 'self' 'nonce-<random>'  — Scripts locked to nonce
form-action 'self'                  — Forms restricted to freezr server
object-src 'none'                   — No plugins

Public pages intentionally allow iframes, images, and styles from any source — this is public content that may embed external media like YouTube videos. The key protections are: no arbitrary script execution (nonce), no form submission to external sites, and no cookie exposure.

Server-Side Protections

Session Cookie

The session cookie that identifies logged-in users is configured with:

  • httpOnly: true — JavaScript cannot read it, even within apps
  • sameSite: 'strict' — Not sent on cross-site requests
  • secure: true — Only sent over HTTPS (in production)
  • path: / (default) — Sent to all routes on the server

Since the session cookie is httpOnly, no app — malicious or otherwise — can read or exfiltrate it via JavaScript.

Public Page Cookie Protection

Public pages run middleware that intercepts and blocks any attempt by the server to set cookies on the response. The session cookie is still sent by the browser (it has path /), but since it is httpOnly, JavaScript on the public page cannot read it.

Input Validation

  • App names: Validated against a strict character set — blocks < > " / { } \ and other special characters
  • Permission names: Must match /^[a-zA-Z0-9._-]+$/
  • Table IDs: Must match /^[a-zA-Z0-9._-]+$/
  • Public page IDs: Cannot start with reserved route prefixes (/app, /admin, /account, /login, etc.) to prevent path confusion attacks

What Is Protected

✅ Token Theft Between Apps

Attack: Calendar app tries to steal Banking app token.

// Calendar app at /apps/calendar/
document.cookie
// Returns: "calendar_token=abc123"
// Banking token (Path=/apps/banking) not visible

fetch('/apps/banking/accounts')
// Banking token not sent (path mismatch)
// Calendar token sent instead (wrong token for banking)

Path isolation prevents token access.

✅ Unauthorized API Access

Attack: Calendar app makes API call with stolen token.

POST /feps/write/banking_accounts/account_789
Authorization: Bearer <calendar_token>

// Server validation:
// Token appName: 'calendar'
// Requested collection: 'banking_accounts'
// Permission: calendar has NO access to banking_accounts
// Result: 403 Forbidden

Permission system enforces app boundaries.

✅ Server-Side Data Theft

Attack: Calendar app fetches Banking dashboard HTML.

fetch('/apps/banking/accounts')
// Sec-Fetch-Mode: cors (JavaScript fetch)

// Server sees:
// - Not a real navigation (sec-fetch-mode !== 'navigate')
// - Banking page has server-rendered sensitive data
// Result: 403 Forbidden

Sec-Fetch-Mode detection blocks malicious fetches.

✅ Cross-Site Request Forgery (CSRF)

// API calls require Authorization header (token)
// Cookies alone are insufficient
// External sites cannot get token value
// SameSite=strict prevents cookie sending from external sites

Token-based auth prevents CSRF.

What Is NOT Protected

⚠️ XSS Within an App

freezr's CSP uses nonce-based script protection, which blocks all inline scripts — including injected <script> tags and inline event handlers. However, if the app developer's own JavaScript code has vulnerabilities (e.g., using innerHTML with unsanitised user input), an attacker could still manipulate the DOM within that app's page.

// Example: app code with innerHTML vulnerability
element.innerHTML = userProvidedData;
// Attacker could inject HTML that changes visible content,
// creates fake UI elements, or adds misleading links.
// However, injected <script> tags and event handlers
// are blocked by CSP nonce enforcement.
Mitigation: CSP nonce enforcement blocks all injected scripts. By default, connect-src 'self' prevents data exfiltration to external domains. All app code is open source so malicious patterns can be detected by review or automated scanning.

⚠️ Social Engineering

A malicious app could create fake UI elements that mimic login screens or other trusted interfaces.

<div style="position:fixed; top:0; left:0; width:100%;
    height:100%; background:white; z-index:9999">
  <h1>Your session expired. Please log in again:</h1>
  <form action="/apps/malicious.app/steal">
    <input type="text" name="username">
    <input type="password" name="password">
    <button>Login</button>
  </form>
</div>
Protection: CSP's form-action 'self' prevents forms from submitting data to external domains — the form above can only post to the freezr server itself.
Mitigation: User awareness (legitimate freezr login only happens at /account/login), trusted app ecosystem and open source review.

⚠️ Browser Extensions

Malicious browser extensions can read everything on the page, including tokens. Users must be cautious about installed extensions.

⚠️ Compromised Devices

If the user's device has malware, all bets are off.

⚠️ App Identity Is Install-Time Trust

freezr verifies which installed app is calling — every API token is minted for a specific (user, app) pair, and permission grants attach to the app's name for that user. It does not verify that the code running under that name is the code the user believes it is. App identity is established at install time: whoever can install apps on an account (the user — or an attacker holding their session) can install different code under an existing app's name and inherit that name's grants.

In practice this adds little to a full account compromise (an attacker with your session can simply grant themselves new permissions). The realistic risk is update and re-install hijacking: a user refreshes an app from a URL that now serves different code, and the new code silently inherits every grant made to the old one.

Mitigation: None enforced today — app identity is install-time trust. Partial mitigations under consideration are described in Known Limitations §6.

⚠️ Cross-Server Sharing Trusts the Recipient's Server

When you share data with a user on another freezr server, the unit of trust is that person and their server — not the app named in the grant. Your server delivers a copy; from that point, only the recipient's server decides which of its apps can read it, and a modified or malicious server can ignore those boundaries entirely. More fundamentally, no protocol can stop a recipient from copying delivered data onward.

Mitigation: User awareness — share data cross-server only with people (and server operators) you trust with that data. See Known Limitations §7.

Attack Scenarios and Mitigations

Scenario 1: Token Reuse Attack

Attack: Attacker captures token via network sniffer, tries to reuse it.

  • HTTPS required (encrypted transport)
  • Short token lifetime
  • Session validation (token must match active session)
  • Token tied to specific user and app

Result: Attack window is small; token only works for intended app.

Scenario 2: Path Traversal

Attack: Malicious app tries to access parent path cookies.

// Banking app at /apps/com.bigbank.freezrbanking
document.cookie
// Tries to access tokens at /apps/ or /

Protection: Browser enforces cookie path semantics. Cookies are visible only to paths matching or beneath the cookie path. Result: Attack impossible (browser-level protection).

Scenario 3: API Permission Bypass

Attack: App modifies request to access unauthorized collection.

POST /feps/write/admin_users/user_123
Authorization: Bearer <regular_app_token>

Protection: Server validates app permissions before processing any request. Result: 403 Forbidden.

Scenario 4: Session Fixation

Attack: Attacker sets a known token in victim's browser.

  • Tokens are random, not predictable
  • Tokens stored server-side with validation
  • Session cookie is httpOnly (cannot be set by JavaScript)
  • Tokens expire quickly

Result: Attack difficult to execute, limited impact.

Scenario 5: Clickjacking

Attack: Malicious app embeds legitimate app in invisible iframe, tricks user into clicking.

  • frame-src 'none' and child-src 'none' in CSP — apps cannot embed iframes by default
  • Apps that need iframes must request allow_self_frames, which only allows same-origin and blob iframes

Result: Attack blocked by CSP.

Scenario 6: Supply Chain Attack

Attack: Node modules may be compromised

Protection: freezr purposefully uses as few dependencies as possible. see dependencies.

Known Limitations

1. Trust in Browser Implementation

Path-based cookie isolation depends on correct browser implementation of cookie scoping. If a browser has a bug in cookie path handling, isolation could break.

2. HttpOnly=false Requirement

Tokens must be readable by JavaScript (to send in Authorization headers). This makes them vulnerable to XSS within the app's scope.

3. Sec-Fetch-Mode Dependency

Server-side data protection relies on Sec-Fetch-Mode headers. Older browsers without support require fallback validation. .

4. User Granted Permissions

If a user grants a malicious app broad permissions, that app can access sensitive data.

  • Clear UI for permission grants
  • Granular permissions (specific collections, read vs write)
  • Easy permission revocation
  • Audit log of cross-app access

5. Insider Threats

A malicious app can abuse permissions granted by users or exploit bugs in freezr itself. An app can mount a Denial of Service attack, or use the person's login to start mining crypto.

  • Open source code review
  • App vetting process
  • User ratings and reviews
  • Rate limiting and anomaly detection

6. App Code Authenticity

Permission grants bind to an app's name (per user), not to its code. Installing different code under the same name — typically via a hijacked update or re-install source — inherits the name's existing grants. Partial mitigations under consideration (none implemented yet):

  • Bundle-hash continuity — record a hash of the app files at install; surface "this app's code has changed since you granted these permissions", with re-approval required only for sensitive permission types (forcing re-grants on every update would make automated updates as cumbersome as manual ones)
  • Developer signing keys (trust-on-first-use) — the manifest carries the developer's public key; updates must be signed by the same key, so same-name code from a different publisher is treated as a different app with no inherited grants
  • Domain-bound app ids — app ids are already reverse-domain (com.example.app); the server could verify domain control at install (e.g. a /.well-known file or DNS record), tying the name to its real publisher
Caveat: these protect grant continuity across updates and re-installs. They do not protect an already-compromised account, and they cannot be enforced across servers (see §7).

7. Cross-Server Trust Boundary

Within one server, freezr enforces which user and which app can read shared data. Across servers it cannot: the receiving server is the only party in a position to enforce anything on its side, so a cross-server grant is ultimately a grant to the (user, server) pair. App names in cross-server grants are labels the remote server may honour, not boundaries your server can enforce. Server-to-server attestation (a server signing claims about what its apps are) could raise the bar but still reduces to trusting the remote server's word.

Note: this is an inherent property of decentralised sharing, not an implementation gap — the mitigation is user awareness: sharing data with someone is trusting them and whoever runs their server.

Implementation Requirements

To maintain security guarantees, freezr implementations must:

  1. Enforce HTTPS — All cookies must have Secure flag
  2. Set proper cookie attributes — SameSite=strict, correct path scoping
  3. Validate on every request — Never trust client-supplied app names
  4. Store tokens server-side — Use cache with TTL
  5. Check Sec-Fetch-Mode — For pages with server-side data rendering
  6. Implement permission system — Default deny, explicit grants only
  7. Rate limit — Prevent token generation DoS
  8. Log security events — Token validation failures, permission violations
  9. Regular security updates — Stay current with browser changes

Testing Your Implementation

Test 1: Cross-App Token Access

// At /apps/app1/:
const app1Token = document.cookie.match(/app1_token=([^;]+)/)[1];

// At /apps/app2/:
const app2Cookie = document.cookie;
// Should NOT contain app1_token

fetch('/apps/app1/somepage');
// Should NOT send app1_token (path mismatch)

Test 2: API Permission Enforcement

// Get app1 token
const token = getCookie('app1_token');

// Try to access app2 data
fetch('/ceps/read/app2_data/item', {
  headers: { 'Authorization': `Bearer ${token}` }
});
// Should return 403 Forbidden (no permission)

Test 3: Sec-Fetch-Mode Validation

// From /apps/app1/, try to fetch /apps/app2/ with server-side data
fetch('/apps/app2/dashboard');
// Should return 403 if dashboard has initial_queries
// (sec-fetch-mode will be 'cors', not 'navigate')

// Click a real link to /apps/app2/dashboard
// Should work (sec-fetch-mode is 'navigate')

Test 4: Token Expiration

// Get token
const token = getCookie('app_token');

// Wait 6 minutes
setTimeout(() => {
  fetch('/ceps/read/data/item', {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  // Should return 401 Unauthorized (token expired)
}, 360000);

Conclusion

freezr's security model uses path-based credential scoping and token-based API authentication to isolate apps on a single domain. The approach provides strong isolation while maintaining good UX. The summary below focuses on freezr-specific issues, rather than general risks of cloud impementations and software.

Key mechanisms:
  • Path-scoped cookies prevent token theft between apps
  • Token validation prevents unauthorized API access
  • Sec-Fetch-Mode detection prevents data theft via fetch
  • Permission system enables controlled cross-app access
Limitations:
  • XSS within an app can compromise that app's data
  • Social engineering attacks are possible
  • Depends on correct browser implementation
  • User-granted permissions can be abused
  • App identity is install-time trust — grants bind to an app's name, not its code
  • Cross-server sharing trusts the recipient and their server; app boundaries are not enforceable remotely

Last updated: April 2026
This document describes freezr's security model. Implementation details may vary. Always review the actual source code and conduct security audits before deploying.