A revolutionary approach to session security that makes stolen session tokens cryptographically worthless.
Partitioned Authority Sessions (PAN) is a novel authentication architecture that eliminates session hijacking as an attack vector. By splitting session authority across isolated browser contexts and binding all actions to non-transferable cryptographic proofs, PAN makes session tokens completely useless to attackersโeven with full XSS access.
Authority = Token + Isolated Signature + Interaction Proof
An attacker who steals any single component gains nothing. All three must be present, and two of them are non-transferable by design.
| Layer | Technology | Description |
|---|---|---|
Main Application (example.com) |
TypeScript | Primary application layer with session management and interaction tracking |
Signing Iframe (sign.example.com) |
TypeScript (Vanilla) | Isolated cryptographic signing context with no framework dependencies |
| API / Verification | Go | Backend API with signature verification and session validation |
| Cryptography | Browser WebCrypto + Go crypto/* |
Client-side non-extractable keys with server-side ECDSA verification |
| Storage | Redis | Distributed session management, nonce storage, and public key registry |
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ USER'S BROWSER โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ MAIN APPLICATION (TypeScript) โ โ
โ โ (example.com) โ โ
โ โ โโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ Session Cookie โ โ Interaction Tracker โ โ โ
โ โ โ (identifier) โ โ โข Mouse trajectory โ โ โ
โ โ โ โ โ โข Timing analysis โ โ โ
โ โ โโโโโโโโโโโโโโโโโโ โ โข Context binding โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ
โ โ โ postMessage (validated) โ โ
โ โ โผ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ SIGNING ORIGIN IFRAME (Vanilla TypeScript) โ โ โ
โ โ โ (sign.example.com) โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ ISOLATED CRYPTO CONTEXT โ โ โ โ
โ โ โ โ โข WebCrypto API (non-extractable keys) โ โ โ โ
โ โ โ โ โข Origin-locked IndexedDB storage โ โ โ โ
โ โ โ โ โข Same-Origin Policy protection โ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ HTTPS + Signed Request
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโ
โ API SERVER (Go) โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ Signature Verifyโ โ
โ โ (crypto/ecdsa) โ โ
โ โโโโโโโโโโโโโโโโโโโค โ
โ โ Interaction โ โ
โ โ Validation โ โ
โ โโโโโโโโโโโโโโโโโโโค โ
โ โ Nonce Manager โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโ
โ REDIS CLUSTER โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ Session Store โ โ
โ โ โข Public keys โ โ
โ โ โข Session meta โ โ
โ โโโโโโโโโโโโโโโโโโโค โ
โ โ Nonce Registry โ โ
โ โ โข Single-use โ โ
โ โ โข TTL-managed โ โ
โ โโโโโโโโโโโโโโโโโโโค โ
โ โ Rate Limiting โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโ
-
Session Token (Identifier Only)
- HttpOnly, Secure, SameSite cookie
- Grants zero authority on its own
- Only identifies which public key to use for verification
-
Isolated Signature (Non-Transferable)
- Generated in cross-origin iframe (
sign.example.com) - Private key is non-extractable (WebCrypto enforcement)
- Cannot be accessed by XSS (Same-Origin Policy)
- Stored in origin-locked IndexedDB
- Generated in cross-origin iframe (
-
Interaction Proof (Non-Fabricable)
- Mouse trajectory analysis
- Timing pattern validation
- Action-context binding
- Freshness verification (< 5 seconds)
| Attack Vector | Traditional Sessions | PAN |
|---|---|---|
| Cookie Theft (Network) | โ Full compromise | โ Token useless without signature |
| XSS Token Exfiltration | โ Full compromise | โ Token alone grants no authority |
| XSS Direct Key Access | โ N/A | โ Blocked by Same-Origin Policy |
| XSS postMessage Attack | โ N/A | โ Requires valid interaction proof |
| Fabricated Interactions | โ N/A | โ Trajectory/timing cannot be faked |
| Browser Extensions | โ Cross-origin isolation | |
| Memory Scraping | โ Token exposed | โ Non-extractable keys |
| Replay Attacks | โ Single-use nonces | |
| Session Fixation | โ Fresh key pair per session |
npm install partitioned-authority-sessions
# or
bun add partitioned-authority-sessionsSee IMPLEMENTATION.md for detailed integration guide.
Prerequisites:
- Node.js 18+ or Bun (recommended)
- Redis 7.0+ (optional - falls back to in-memory)
- Modern Browser (Chrome 60+, Firefox 55+, Safari 11+, Edge 79+)
# Clone the repository
git clone https://github.com/AaryanBansal-dev/Partitioned-Authority-Sessions.git
cd Partitioned-Authority-Sessions
# Install dependencies
bun install
# Start all services
./dev.sh
# Or manually:
cd api-server && bun run dev & # Port 8080
cd signing-iframe && bun run dev & # Port 3001
cd main-app && bun run dev & # Port 3000# Start Redis with persistence
redis-server --appendonly yes --requirepass your-secure-passwordCreate .env file in the API server directory:
# Server Configuration
PORT=8080
ENVIRONMENT=production
# Redis Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your-secure-password
REDIS_DB=0
# Session Configuration
SESSION_TTL=86400 # 24 hours
NONCE_TTL=300 # 5 minutes
# CORS Configuration
ALLOWED_ORIGINS=https://example.com,https://sign.example.com
# Cryptography
ECDSA_CURVE=P-256
# Rate Limiting
MAX_REQUESTS_PER_MINUTE=60# DNS Records
example.com A your-server-ip
sign.example.com A your-server-ip
# Nginx Configuration
server {
server_name example.com;
listen 443 ssl http2;
ssl_certificate /path/to/example.com.crt;
ssl_certificate_key /path/to/example.com.key;
location / {
proxy_pass http://localhost:3000;
}
location /api {
proxy_pass http://localhost:8080;
}
}
server {
server_name sign.example.com;
listen 443 ssl http2;
ssl_certificate /path/to/sign.example.com.crt;
ssl_certificate_key /path/to/sign.example.com.key;
location / {
proxy_pass http://localhost:3001;
}
# Security headers
add_header X-Frame-Options "ALLOW-FROM https://example.com";
add_header Content-Security-Policy "frame-ancestors https://example.com";
}# Terminal 1: Start Redis
redis-server
# Terminal 2: Start API Server
cd api-server
./pas-server
# Terminal 3: Start Main Application
cd main-app
npm run dev
# Terminal 4: Start Signing Iframe
cd signing-iframe
npm run dev// interaction-tracker.ts
export class InteractionTracker {
private interactions: InteractionProof[] = [];
private trajectoryPoints: Point[] = [];
constructor() {
this.setupListeners();
}
private setupListeners(): void {
// Track mouse movement for trajectory analysis
document.addEventListener('mousemove', (e) => {
this.trajectoryPoints.push({
x: e.clientX,
y: e.clientY,
timestamp: performance.now()
});
// Keep only last 100 points
if (this.trajectoryPoints.length > 100) {
this.trajectoryPoints.shift();
}
});
// Capture genuine user interactions
document.addEventListener('click', (e) => {
this.recordInteraction({
type: 'click',
timestamp: Date.now(),
target: this.hashElement(e.target as HTMLElement),
position: { x: e.clientX, y: e.clientY },
trajectory: this.getMouseTrajectory(),
actionContext: this.getActionContext(e.target as HTMLElement),
velocity: this.calculateVelocity(),
acceleration: this.calculateAcceleration()
});
}, { capture: true });
}
getInteractionProof(action: Action): InteractionProof | null {
const recent = this.findMatchingInteraction(action);
if (!recent) return null;
return {
...recent,
actionHash: this.hashAction(action),
freshness: Date.now(),
signature: null // Will be filled by signing iframe
};
}
private hashAction(action: Action): string {
const canonical = JSON.stringify({
type: action.type,
context: action.context
});
return crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonical))
.then(buf => this.bufferToHex(buf));
}
}// signing-iframe.ts
class SigningContext {
private privateKey: CryptoKey | null = null;
async initialize(): Promise<JsonWebKey> {
// Generate non-extractable ECDSA key pair
const keyPair = await crypto.subtle.generateKey(
{
name: 'ECDSA',
namedCurve: 'P-256'
},
false, // Non-extractable - CRITICAL
['sign']
);
this.privateKey = keyPair.privateKey;
// Store in origin-isolated IndexedDB
await this.storePrivateKey(keyPair.privateKey);
// Export public key for server registration
return await crypto.subtle.exportKey('jwk', keyPair.publicKey);
}
setupMessageHandler(): void {
window.addEventListener('message', async (event) => {
// CRITICAL: Validate origin
if (event.origin !== 'https://example.com') {
console.error('Rejected message from unauthorized origin:', event.origin);
return;
}
const { action, proof, nonce, requestId } = event.data;
try {
// Validate interaction proof
if (!this.validateProof(proof, action)) {
throw new Error('Invalid interaction proof');
}
// Check freshness (must be < 5 seconds old)
if (Date.now() - proof.freshness > 5000) {
throw new Error('Interaction proof expired');
}
// Verify action matches interaction context
if (proof.actionContext !== action.displayName) {
throw new Error('Action-context mismatch');
}
// Sign the request
const signature = await this.sign(action, proof, nonce);
// Send signature back to main application
event.source?.postMessage({
requestId,
signature
}, event.origin);
} catch (error) {
event.source?.postMessage({
requestId,
error: error.message
}, event.origin);
}
});
}
private async sign(action: Action, proof: InteractionProof, nonce: string): Promise<string> {
if (!this.privateKey) {
throw new Error('Private key not initialized');
}
// Create canonical message
const message = this.createCanonicalMessage(action, proof, nonce);
const encoder = new TextEncoder();
const data = encoder.encode(message);
// Sign with ECDSA
const signature = await crypto.subtle.sign(
{
name: 'ECDSA',
hash: 'SHA-256'
},
this.privateKey,
data
);
return this.arrayBufferToBase64(signature);
}
}// server/verification/middleware.go
package verification
import (
"crypto/ecdsa"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"math/big"
"net/http"
"time"
)
type VerificationMiddleware struct {
sessionStore *redis.Client
nonceManager *NonceManager
}
func (vm *VerificationMiddleware) VerifyRequest(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Extract headers
sessionID := r.Header.Get("X-Session-ID")
signature := r.Header.Get("X-Signature")
proofJSON := r.Header.Get("X-Interaction-Proof")
if sessionID == "" || signature == "" || proofJSON == "" {
http.Error(w, "Missing authentication headers", http.StatusUnauthorized)
return
}
// Get session data from Redis
session, err := vm.getSession(sessionID)
if err != nil {
http.Error(w, "Invalid session", http.StatusUnauthorized)
return
}
// Parse interaction proof
var proof InteractionProof
if err := json.Unmarshal([]byte(proofJSON), &proof); err != nil {
http.Error(w, "Invalid interaction proof", http.StatusBadRequest)
return
}
// Validate proof freshness
if time.Now().Unix() - proof.Freshness > 5 {
http.Error(w, "Stale interaction proof", http.StatusUnauthorized)
return
}
// Validate nonce (single-use)
if !vm.nonceManager.ValidateAndConsume(proof.Nonce) {
http.Error(w, "Invalid or reused nonce", http.StatusUnauthorized)
return
}
// Verify cryptographic signature
if err := vm.verifySignature(session.PublicKey, signature, r.Body, &proof); err != nil {
http.Error(w, "Signature verification failed", http.StatusUnauthorized)
return
}
// Validate interaction patterns (anomaly detection)
if err := vm.validateInteractionPattern(&proof, sessionID); err != nil {
http.Error(w, "Suspicious interaction pattern", http.StatusForbidden)
return
}
// Request is authenticated
next.ServeHTTP(w, r)
})
}
func (vm *VerificationMiddleware) verifySignature(
publicKeyJWK string,
signatureB64 string,
body []byte,
proof *InteractionProof,
) error {
// Parse public key
publicKey, err := parsePublicKey(publicKeyJWK)
if err != nil {
return err
}
// Decode signature
sigBytes, err := base64.StdEncoding.DecodeString(signatureB64)
if err != nil {
return err
}
// Parse ECDSA signature (R and S values)
r := new(big.Int).SetBytes(sigBytes[:len(sigBytes)/2])
s := new(big.Int).SetBytes(sigBytes[len(sigBytes)/2:])
// Create canonical message
canonicalMsg := createCanonicalMessage(body, proof)
hash := sha256.Sum256([]byte(canonicalMsg))
// Verify signature
if !ecdsa.Verify(publicKey, hash[:], r, s) {
return errors.New("signature verification failed")
}
return nil
}
func (vm *VerificationMiddleware) validateInteractionPattern(
proof *InteractionProof,
sessionID string,
) error {
// Analyze trajectory for human-like patterns
if !vm.isHumanTrajectory(proof.Trajectory) {
return errors.New("non-human trajectory detected")
}
// Check timing patterns
if !vm.isRealisticTiming(proof) {
return errors.New("unrealistic timing pattern")
}
// Validate against session history
if !vm.matchesSessionBehavior(proof, sessionID) {
return errors.New("anomalous behavior for session")
}
return nil
}// storage/redis_schema.go
package storage
// Session storage schema
type Session struct {
SessionID string `redis:"session_id"`
UserID string `redis:"user_id"`
PublicKey string `redis:"public_key"` // JWK format
CreatedAt int64 `redis:"created_at"`
LastAccess int64 `redis:"last_access"`
IPAddress string `redis:"ip_address"`
UserAgent string `redis:"user_agent"`
ExpiresAt int64 `redis:"expires_at"`
}
// Redis key patterns
const (
SessionKeyPattern = "session:%s" // session:abc123
NonceKeyPattern = "nonce:%s" // nonce:xyz789
RateLimitPattern = "ratelimit:%s:%s" // ratelimit:session:abc123
PublicKeyPattern = "pubkey:%s" // pubkey:user123
)
// TTL values
const (
SessionTTL = 86400 // 24 hours
NonceTTL = 300 // 5 minutes
RateLimitTTL = 60 // 1 minute window
)
type RedisStore struct {
client *redis.Client
}
func (rs *RedisStore) StoreSession(session *Session) error {
key := fmt.Sprintf(SessionKeyPattern, session.SessionID)
pipe := rs.client.Pipeline()
pipe.HSet(ctx, key, session)
pipe.Expire(ctx, key, SessionTTL*time.Second)
_, err := pipe.Exec(ctx)
return err
}
func (rs *RedisStore) StoreNonce(nonce string) error {
key := fmt.Sprintf(NonceKeyPattern, nonce)
return rs.client.Set(ctx, key, "1", NonceTTL*time.Second).Err()
}
func (rs *RedisStore) ValidateNonce(nonce string) (bool, error) {
key := fmt.Sprintf(NonceKeyPattern, nonce)
// Use GETDEL to atomically get and delete (prevents reuse)
result, err := rs.client.GetDel(ctx, key).Result()
if err == redis.Nil {
return false, nil // Nonce doesn't exist or already used
}
if err != nil {
return false, err
}
return result == "1", nil
}| Operation | Average Latency | P95 | P99 | Notes |
|---|---|---|---|---|
| Key Generation (Login) | 45ms | 62ms | 89ms | One-time cost per session |
| Signature Generation | 3.2ms | 4.5ms | 6.8ms | Per sensitive action |
| postMessage Round-trip | 0.8ms | 1.2ms | 2.1ms | Browser-internal |
| Server Signature Verification | 1.1ms | 1.8ms | 3.2ms | ECDSA P-256 |
| Redis Session Lookup | 0.3ms | 0.5ms | 0.9ms | With connection pooling |
| Total Overhead | 5.4ms | 8.0ms | 13.0ms | User-imperceptible |
| Component | CPU | Memory | Network |
|---|---|---|---|
| Main App | +2% | +8MB | Negligible |
| Signing Iframe | +1% | +4MB | Negligible |
| API Server | +5% | +20MB | +1-2KB per request |
| Redis | Minimal | +10MB | Fast |
- Concurrent Sessions: Tested up to 100,000 concurrent sessions
- Requests/Second: 15,000+ on modest hardware (4 CPU, 8GB RAM)
- Redis Operations/Second: 50,000+ (session lookups + nonce validation)
In Scope:
- Session hijacking via token theft
- XSS-based session exploitation
- Replay attacks
- Session fixation
- CSRF with stolen sessions
- Browser extension attacks
- Network interception
Out of Scope:
- Social engineering (user intentionally performs malicious action)
- Credential phishing (pre-authentication)
- Physical device compromise with keylogger
- Nation-state browser exploitation (0-days)
- Always use HTTPS for both main app and signing iframe
- Implement CSP headers to restrict script sources
- Enable SameSite=Strict on session cookies
- Rotate nonces with short TTLs (5 minutes)
- Monitor interaction patterns for anomaly detection
- Rate limit signature requests per session
- Log all signature failures for security analysis
- Implement session concurrency limits (e.g., max 3 devices)
- GDPR: No PII in interaction metadata; can be anonymized
- PCI-DSS: Suitable for payment applications (eliminates session hijacking risk)
- HIPAA: Acceptable for healthcare (strong authentication continuity)
- NIST: Aligns with AAL3 (multi-factor authentication)
- Problem Statement - Understanding the session hijacking challenge
- Architecture Plan - Detailed design and security analysis
- Study Guide - Learning path for implementation
- API Reference - Complete API documentation (coming soon)
- Deployment Guide - Production deployment checklist (coming soon)
# Test main application
cd main-app
npm test
# Test signing iframe
cd signing-iframe
npm test
# Test Go API server
cd api-server
go test ./...# Run full integration test suite
cd tests
npm run test:integration
# Test specific scenarios
npm run test:xss-resistance
npm run test:replay-attack
npm run test:interaction-validation# Penetration testing checklist
./scripts/security-audit.sh
# XSS injection simulation
./scripts/test-xss-resistance.sh
# Replay attack simulation
./scripts/test-replay-attack.shWe welcome contributions! Please see CONTRIBUTING.md for guidelines.
# Fork and clone the repository
git clone https://github.com/yourusername/partitioned-authority-sessions.git
cd partitioned-authority-sessions
# Create a feature branch
git checkout -b feature/your-feature-name
# Make changes and test
npm test
# Submit a pull requestThis project is licensed under the MIT License - see the LICENSE file for details.
- WebCrypto API specification authors
- OWASP for session security research
- Browser vendors for Same-Origin Policy enforcement
- Redis team for high-performance storage
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Security: Report security vulnerabilities to security@example.com
- โ Core architecture implementation
- โ TypeScript client libraries
- โ Go server verification
- โ Redis storage backend
- ๐ WebAuthn integration for hardware keys
- ๐ Mobile SDK (iOS/Android)
- ๐ Multi-datacenter Redis clustering
- ๐ Advanced anomaly detection with ML
- ๐ Zero-trust architecture mode
- ๐ Blockchain-based audit logging
- ๐ Quantum-resistant cryptography option
- ๐ Decentralized key management
Built with โค๏ธ by the PAN Security Team