乐闻世界logo
搜索文章和话题

What are the security considerations for Service Worker?

3月7日 12:06

Service Worker Security Considerations Explained

Service Worker, as a proxy server running in the browser background, has powerful capabilities while also bringing some security risks. Understanding these security issues is crucial for developing secure web applications.

1. HTTPS Requirement

Why HTTPS is Mandatory

javascript
// Service Worker can only be registered in HTTPS environment // Exception: localhost allows HTTP if ('serviceWorker' in navigator) { // Check if it's a secure context if (window.isSecureContext) { navigator.serviceWorker.register('/sw.js'); } else { console.error('Service Worker requires HTTPS environment'); } }

Security Risks:

  • Service Worker in HTTP environment may be tampered by man-in-the-middle attacks
  • Attackers can inject malicious Service Workers to intercept all network requests
  • Users' sensitive data may be stolen

Detect Secure Context

javascript
// Check if current environment is secure function checkSecureContext() { if (!window.isSecureContext) { console.warn('Not in secure context, Service Worker functionality limited'); return false; } return true; } // Or check protocol function isSecureProtocol() { return location.protocol === 'https:' || location.hostname === 'localhost'; }

2. Scope Limitations

Scope Security

javascript
// Service Worker can only control pages within its scope // Specify scope during registration navigator.serviceWorker.register('/sw.js', { scope: '/app/' // Can only control pages under /app/ }); // Attempting to access resources outside scope will fail // /app/page.html ✅ Can control // /other/page.html ❌ Cannot control

Path Traversal Protection

javascript
// ❌ Dangerous: Not validating paths may cause security issues self.addEventListener('fetch', event => { const url = new URL(event.request.url); // Malicious requests may contain path traversal characters like ../ caches.match(url.pathname); // Dangerous! }); // ✅ Safe: Validate and sanitize paths self.addEventListener('fetch', event => { const url = new URL(event.request.url); const pathname = url.pathname; // Validate path doesn't contain traversal characters if (pathname.includes('..') || pathname.includes('//')) { event.respondWith(new Response('Invalid path', { status: 400 })); return; } // Only allow access to whitelist paths const allowedPaths = ['/api/', '/assets/', '/static/']; const isAllowed = allowedPaths.some(path => pathname.startsWith(path)); if (!isAllowed) { event.respondWith(new Response('Forbidden', { status: 403 })); return; } event.respondWith(caches.match(event.request)); });

3. Content Security Policy (CSP)

CSP Impact on Service Worker

javascript
// Set CSP to prevent XSS attacks // Set in HTTP response header: // Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' // Service Worker script itself needs to comply with CSP // Inline scripts may be blocked

Safe Service Worker Script Loading

javascript
// ✅ Recommended: Load external script navigator.serviceWorker.register('/sw.js'); // ❌ Avoid: Use inline Service Worker const swCode = ` self.addEventListener('fetch', ...); `; const blob = new Blob([swCode], { type: 'application/javascript' }); const url = URL.createObjectURL(blob); navigator.serviceWorker.register(url); // May violate CSP

4. Cache Security

Sensitive Data Caching

javascript
// ❌ Dangerous: Caching sensitive information self.addEventListener('fetch', event => { if (event.request.url.includes('/api/user/profile')) { event.respondWith( fetch(event.request).then(response => { // Cache response containing personal information caches.open('api-cache').then(cache => { cache.put(event.request, response.clone()); // Dangerous! }); return response; }) ); } }); // ✅ Safe: Don't cache sensitive data const SENSITIVE_PATHS = [ '/api/auth/', '/api/user/profile', '/api/payment/' ]; self.addEventListener('fetch', event => { const url = new URL(event.request.url); // Check if it's a sensitive path const isSensitive = SENSITIVE_PATHS.some(path => url.pathname.includes(path) ); if (isSensitive) { // Sensitive requests go directly to network, no caching event.respondWith(fetch(event.request)); return; } // Non-sensitive requests can use cache event.respondWith( caches.match(event.request).then(response => { return response || fetch(event.request); }) ); });

Safe Cache Cleanup

javascript
// ✅ Safe cache cleanup self.addEventListener('activate', event => { event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { // Only delete caches created by current Service Worker // Avoid deleting other apps' caches if (cacheName.startsWith('my-app-')) { return caches.delete(cacheName); } }) ); }) ); });

5. Cross-Site Scripting (XSS) Protection

Prevent Malicious Script Injection

javascript
// ❌ Dangerous: Directly using user input self.addEventListener('message', event => { const userInput = event.data.message; // Directly executing user input may cause XSS eval(userInput); // Extremely dangerous! }); // ✅ Safe: Validate and sanitize input self.addEventListener('message', event => { const data = event.data; // Verify message origin if (event.origin !== 'https://trusted-domain.com') { console.error('Untrusted origin:', event.origin); return; } // Verify data type if (typeof data.action !== 'string') { return; } // Use whitelist to validate action const allowedActions = ['skipWaiting', 'claimClients']; if (!allowedActions.includes(data.action)) { console.error('Invalid action:', data.action); return; } // Safe execution switch (data.action) { case 'skipWaiting': self.skipWaiting(); break; case 'claimClients': self.clients.claim(); break; } });

6. Man-in-the-Middle Attack Protection

Certificate Pinning

javascript
// Although Service Worker cannot directly implement certificate pinning // Can enhance security by checking response headers self.addEventListener('fetch', event => { event.respondWith( fetch(event.request).then(response => { // Check security response headers const headers = response.headers; // Check HSTS header if (!headers.get('Strict-Transport-Security')) { console.warn('Missing HSTS header'); } // Check CSP header if (!headers.get('Content-Security-Policy')) { console.warn('Missing CSP header'); } return response; }) ); });

7. Permission Control

Principle of Least Privilege

javascript
// ✅ Only request necessary permissions // Push notification permission async function requestPushPermission() { const permission = await Notification.requestPermission(); return permission === 'granted'; } // Background sync permission async function requestSyncPermission() { const registration = await navigator.serviceWorker.ready; if ('sync' in registration) { // Check permission status const status = await navigator.permissions.query({ name: 'periodic-background-sync' }); if (status.state === 'granted') { return true; } } return false; }

8. Update Security

Safe Update Mechanism

javascript
// ✅ Verify Service Worker updates navigator.serviceWorker.register('/sw.js').then(registration => { registration.addEventListener('updatefound', () => { const newWorker = registration.installing; newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed') { // Verify new version integrity // Can verify by computing hash value verifyServiceWorkerIntegrity(newWorker).then(isValid => { if (isValid) { // Prompt user to update showUpdateNotification(newWorker); } else { console.error('Service Worker integrity verification failed'); } }); } }); }); }); // Verify Service Worker integrity (example) async function verifyServiceWorkerIntegrity(worker) { try { const response = await fetch('/sw.js'); const scriptContent = await response.text(); // Can add hash verification logic here // For example, compare with server-returned hash value return true; } catch (error) { console.error('Verification failed:', error); return false; } }

9. Data Leak Prevention

Prevent Cache Leaks

javascript
// ✅ Safe caching strategy const PUBLIC_RESOURCES = [ '/', '/index.html', '/styles.css', '/app.js', '/images/' ]; const PRIVATE_RESOURCES = [ '/api/user/', '/api/orders/', '/dashboard/' ]; self.addEventListener('fetch', event => { const url = new URL(event.request.url); // Check if it's a public resource const isPublic = PUBLIC_RESOURCES.some(path => url.pathname.startsWith(path) ); // Check if it's a private resource const isPrivate = PRIVATE_RESOURCES.some(path => url.pathname.startsWith(path) ); if (isPrivate) { // Private resources: network first, no caching event.respondWith( fetch(event.request).catch(() => { return new Response('Network error', { status: 503 }); }) ); } else if (isPublic) { // Public resources: can use cache event.respondWith( caches.match(event.request).then(response => { return response || fetch(event.request); }) ); } else { // Unknown resources: default network request event.respondWith(fetch(event.request)); } });

10. Security Best Practices Checklist

Development Stage

  • Ensure all environments use HTTPS (except localhost)
  • Set reasonable Service Worker scope
  • Don't cache sensitive data (user info, payment data, etc.)
  • Validate all user input
  • Implement CSP policy
  • Regularly update Service Worker

Production Environment

  • Monitor Service Worker abnormal behavior
  • Implement Subresource Integrity (SRI)
  • Configure HSTS headers
  • Regularly audit cache contents
  • Implement access control
  • Record security logs

Security Testing Recommendations

javascript
// Security testing checklist const securityTests = { // 1. Check HTTPS checkHTTPS: () => location.protocol === 'https:', // 2. Check scope checkScope: async () => { const registration = await navigator.serviceWorker.ready; console.log('Service Worker scope:', registration.scope); return registration.scope; }, // 3. Check cache contents checkCacheContents: async () => { const cacheNames = await caches.keys(); for (const name of cacheNames) { const cache = await caches.open(name); const requests = await cache.keys(); console.log(`Cache "${name}" contains ${requests.length} items`); } }, // 4. Check permissions checkPermissions: async () => { const permissions = await navigator.permissions.query({ name: 'notifications' }); console.log('Notification permission:', permissions.state); } };

Summary

Service Worker security key points:

  1. Must use HTTPS: Prevent man-in-the-middle attacks
  2. Set reasonable scope: Limit control capabilities
  3. Don't cache sensitive data: Prevent data leakage
  4. Validate user input: Prevent XSS attacks
  5. Implement CSP: Enhance content security
  6. Regular updates: Fix security vulnerabilities
  7. Least privilege: Only request necessary permissions
标签:Service Worker