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

How is PWA security ensured? What are the key security measures?

2月18日 21:58

PWA security is very important because PWA can be installed on devices like native apps and can access more device features. Here are the key aspects and best practices for PWA security:

1. Necessity of HTTPS

Why PWA Must Use HTTPS

  1. Service Worker Requirement: Service Worker can only run in HTTPS environment (localhost excepted)
  2. Data Security: Protect user data during transmission
  3. Trust: HTTPS provides identity verification, prevents man-in-the-middle attacks
  4. Browser Requirements: Modern browsers require PWA to use HTTPS
  5. Push Notifications: Web Push API requires HTTPS

Configuring HTTPS

nginx
# Nginx configuration example server { listen 443 ssl http2; server_name example.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; # SSL configuration ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; # HSTS add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; } # HTTP redirect to HTTPS server { listen 80; server_name example.com; return 301 https://$server_name$request_uri; }

2. Content Security Policy (CSP)

Role of CSP

CSP helps prevent cross-site scripting (XSS), clickjacking, and other security threats.

Configuring CSP

html
<!-- Configure via HTTP header --> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; font-src 'self' https://fonts.gstatic.com; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none';"> <!-- Via server configuration -->
nginx
# Nginx configuration add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; font-src 'self' https://fonts.gstatic.com; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none';";

CSP Directives Explanation

  • default-src: Default policy
  • script-src: Script sources
  • style-src: Style sources
  • img-src: Image sources
  • connect-src: Network request sources
  • font-src: Font sources
  • object-src: Plugin sources
  • frame-ancestors: Allowed parent pages for embedding
  • form-action: Form submission targets

3. Service Worker Security

Limit Service Worker Scope

javascript
// Place Service Worker in root directory to control entire app navigator.serviceWorker.register('/sw.js'); // Or place in specific directory to control only that directory navigator.serviceWorker.register('/app/sw.js', { scope: '/app/' });

Verify Service Worker Updates

javascript
// sw.js self.addEventListener('install', event => { event.waitUntil( caches.open('my-cache').then(cache => { return cache.addAll([ '/', '/styles/main.css', '/scripts/app.js' ]); }) ); }); self.addEventListener('activate', event => { event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { // Only delete caches belonging to current app if (cacheName.startsWith('my-app-')) { return caches.delete(cacheName); } }) ); }) ); });

Prevent Cache Pollution

javascript
self.addEventListener('fetch', event => { const url = new URL(event.request.url); // Only cache same-origin resources if (url.origin !== self.location.origin) { event.respondWith(fetch(event.request)); return; } // Handle same-origin requests event.respondWith( caches.match(event.request).then(response => { return response || fetch(event.request); }) ); });

4. Data Security

Encrypt Sensitive Data

javascript
// Use Web Crypto API to encrypt data async function encryptData(data, key) { const encoder = new TextEncoder(); const encodedData = encoder.encode(data); const iv = crypto.getRandomValues(new Uint8Array(12)); const encryptedData = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv }, key, encodedData ); return { iv: Array.from(iv), data: Array.from(new Uint8Array(encryptedData)) }; } async function decryptData(encryptedData, key) { const iv = new Uint8Array(encryptedData.iv); const data = new Uint8Array(encryptedData.data); const decryptedData = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: iv }, key, data ); const decoder = new TextDecoder(); return decoder.decode(decryptedData); }

Secure Storage

javascript
// Use IndexedDB to store sensitive data const dbPromise = idb.open('secure-db', 1, upgradeDB => { upgradeDB.createObjectStore('secure-data', { keyPath: 'id' }); }); async function storeSecureData(id, data) { const db = await dbPromise; await db.put('secure-data', { id: id, data: data, timestamp: Date.now() }); } // Avoid storing sensitive information in localStorage // ❌ Not secure localStorage.setItem('token', 'sensitive-token'); // ✅ Use IndexedDB or store encrypted

5. Push Notification Security

VAPID Key Management

javascript
// Securely store VAPID keys on server const vapidKeys = { publicKey: process.env.VAPID_PUBLIC_KEY, privateKey: process.env.VAPID_PRIVATE_KEY }; // Don't expose private key on frontend // ❌ Wrong const privateKey = 'my-private-key'; // Don't do this // ✅ Correct: Private key only used on server

Validate Push Subscriptions

javascript
// Server-side validate subscription info async function validateSubscription(subscription) { // Check required fields if (!subscription.endpoint || !subscription.keys) { throw new Error('Invalid subscription'); } // Check key format if (!subscription.keys.p256dh || !subscription.keys.auth) { throw new Error('Invalid keys'); } // Validate endpoint format try { new URL(subscription.endpoint); } catch (error) { throw new Error('Invalid endpoint'); } return true; }

6. Cross-Origin Security

CORS Configuration

nginx
# Nginx configure CORS location /api/ { add_header 'Access-Control-Allow-Origin' 'https://your-pwa.com'; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization'; add_header 'Access-Control-Allow-Credentials' 'true'; if ($request_method = 'OPTIONS') { return 204; } }
javascript
// Frontend request configuration fetch('https://api.example.com/data', { method: 'GET', credentials: 'include', // Include cookies headers: { 'Content-Type': 'application/json' } });

7. Authentication and Authorization

Use JWT for Authentication

javascript
// Get JWT after login async function login(username, password) { const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const data = await response.json(); // Securely store JWT if (data.token) { await storeSecureToken(data.token); } return data; } // Carry JWT in requests async function fetchData() { const token = await getSecureToken(); const response = await fetch('/api/data', { headers: { 'Authorization': `Bearer ${token}` } }); return response.json(); }

Token Refresh Mechanism

javascript
// Auto-refresh expired token async function fetchWithAuth(url, options = {}) { let token = await getSecureToken(); // Check if token is expiring soon if (isTokenExpiringSoon(token)) { token = await refreshToken(); } const response = await fetch(url, { ...options, headers: { ...options.headers, 'Authorization': `Bearer ${token}` } }); // If token is invalid, try to refresh if (response.status === 401) { token = await refreshToken(); return fetch(url, { ...options, headers: { ...options.headers, 'Authorization': `Bearer ${token}` } }); } return response; }

8. Security Best Practices

1. Input Validation

javascript
// Validate user input function validateInput(input) { // Prevent XSS const sanitized = input.replace(/[<>]/g, ''); // Validate format if (!/^[a-zA-Z0-9]+$/.test(sanitized)) { throw new Error('Invalid input'); } return sanitized; }

2. Prevent CSRF

javascript
// Use CSRF token async function submitForm(data) { const csrfToken = await getCsrfToken(); const response = await fetch('/api/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify(data) }); return response.json(); }

3. Secure Service Worker Updates

javascript
// Check for Service Worker updates navigator.serviceWorker.addEventListener('controllerchange', () => { // Notify user that app has been updated showUpdateNotification(); }); // Prompt user to refresh page function showUpdateNotification() { const notification = document.createElement('div'); notification.textContent = 'New version available, click to refresh'; notification.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: #007bff; color: white; padding: 15px 20px; border-radius: 4px; cursor: pointer; z-index: 9999; `; notification.addEventListener('click', () => { window.location.reload(); }); document.body.appendChild(notification); }

9. Security Audit and Monitoring

Use Lighthouse for Security Audit

bash
# Run Lighthouse security audit lighthouse https://your-pwa.com --view --only-categories=security

Monitor Security Events

javascript
// Monitor suspicious activities function logSecurityEvent(event) { fetch('/api/security-log', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ event: event.type, timestamp: Date.now(), userAgent: navigator.userAgent, url: window.location.href }) }); } // Monitor Service Worker errors navigator.serviceWorker.addEventListener('error', (event) => { logSecurityEvent({ type: 'service-worker-error', error: event.error }); });

Summary

Key points for PWA security:

  1. Must Use HTTPS: Service Worker and push notifications require HTTPS
  2. Configure CSP: Prevent XSS and other injection attacks
  3. Limit Service Worker Scope: Prevent cache pollution
  4. Securely Store Sensitive Data: Use IndexedDB and encryption
  5. Validate Push Subscriptions: Prevent malicious pushes
  6. Properly Configure CORS: Prevent cross-origin attacks
  7. Use Secure Authentication: JWT + Token refresh
  8. Input Validation and Output Encoding: Prevent injection attacks
  9. Regular Security Audits: Use tools like Lighthouse
  10. Monitor Security Events: Detect and respond to security threats in time
标签:PWA