---
title: "Dominar el Arte de la Seguridad en Aplicaciones Web Progresivas"
---

# Dominar el Arte de la Seguridad en Aplicaciones Web Progresivas

Las Progressive Web Apps (PWAs) se han convertido en el estándar de facto para ofrecer experiencias similares a las de una aplicación en la web abierta. Sus fortalezas principales —funcionalidad offline, notificaciones push e instalabilidad— también introducen nuevas superficies de ataque que los sitios web tradicionales rara vez enfrentan. Este artículo profundiza en el **ciclo de vida de la seguridad de una PWA**, combinando endurecimiento a nivel de red, protecciones en tiempo de ejecución y mejores prácticas operativas. Al final, tendrás una lista de verificación que podrás aplicar a cualquier proyecto, ya sea un lector de noticias sencillo o una plataforma de comercio electrónico compleja.

> **TL;DR**: Asegura tu PWA con HTTPS, impone una CSP estricta, aísla los service workers, valida todas las entradas y adopta una rutina de monitoreo continuo.

---

## 1. Capa de Transporte – La Primera Línea de Defensa

### 1.1 Imponer HTTPS en Todas Partes

Todos los navegadores modernos exigen **HTTPS** para el registro de service workers. Sin embargo, algunos recursos (p. ej., análisis de terceros) pueden seguir cargándose mediante HTTP, generando advertencias de contenido mixto y abriendo la puerta a ataques de hombre‑en‑el‑medio (MITM).

**Pasos clave:**

1. Obtén un certificado TLS confiable (Let’s Encrypt, Cloudflare, etc.).
2. Redirige todas las peticiones `http://` a `https://` mediante la configuración del servidor.
3. Habilita **HSTS** (HTTP Strict Transport Security) para obligar a los navegadores a usar HTTPS durante un período predefinido.

```nginx
# Example NGINX snippet
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl http2;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    # TLS settings ...
}
```

> **Nota**: HSTS elimina los ataques de degradación, pero requiere planificación cuidadosa porque, una vez activado, los navegadores rechazarán conexiones HTTP durante el tiempo especificado.

### 1.2 Anclado de Certificado (Opcional)

Para PWAs altamente sensibles —banca, historiales médicos— considera **anclado de certificado** mediante la **Extensión de Anclado de Clave Pública para HTTP (HPKP)**. Aunque HPKP está siendo descontinuado por los riesgos de despliegue, puedes lograr una seguridad similar con los encabezados **Expect‑CT**, que obligan a la Transparencia de Certificados.

```http
Expect-CT: max-age=86400, enforce, report-uri="https://example.com/report"
```

---

## 2. Endurecimiento de Service Workers

Los service workers son el corazón de las capacidades offline de una PWA. Se ejecutan en un sub‑hilo con privilegios elevados, lo que los convierte en un objetivo privilegiado para la explotación.

### 2.1 Limitación del Alcance (Scope)

Define el `scope` más pequeño posible al registrar un service worker. Esto evita que el worker intercepte peticiones fuera de su área prevista.

```javascript
navigator.serviceWorker.register('/sw.js', { scope: '/app/' })
  .then(reg => console.log('SW registered with scope:', reg.scope));
```

### 2.2 Verificar Integridad con Subresource Integrity (SRI)

Si cargas el script de un service worker desde una CDN, protégelo con **SRI** para asegurarte de que el código recibido coincide con el hash esperado.

```html
<script src="https://cdn.example.com/sw.js"
        integrity="sha384-abc123..."
        crossorigin="anonymous"></script>
```

### 2.3 Política de Seguridad de Contenidos para Service Workers

Una **CSP** estricta puede impedir que el worker cargue scripts maliciosos o se comunique con orígenes no deseados.

```http
Content-Security-Policy: script-src 'self' https://trusted.cdn.com; 
                         connect-src 'self' https://api.example.com;
```

> **Consejo**: Usa la directiva `worker-src` (compatible en navegadores más recientes) para permitir explícitamente solo los orígenes de scripts de workers.

### 2.4 Gestión del Estado y Validación de Caché

Nunca confíes ciegamente en las entradas de caché. Siempre valida la frescura de las respuestas almacenadas, sobre todo para tokens de autenticación o datos personales.

```javascript
self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);
  if (url.pathname.startsWith('/api/')) {
    // Bypass cache for dynamic data
    event.respondWith(fetch(event.request));
    return;
  }
  // Default cache‑first strategy for static assets
  event.respondWith(
    caches.match(event.request).then(cached => cached || fetch(event.request))
  );
});
```

---

## 3. Protecciones en Tiempo de Ejecución – CSP, Permisos y Sandbox

### 3.1 CSP Completa

Una CSP bien diseñada reduce el riesgo de **Cross‑Site Scripting (XSS)** y de **Inyección de Datos**. A continuación, un ejemplo de política adaptada a una PWA típica:

```http
Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'sha256-XYZ' https://analytics.example.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;
  font-src 'self' https://fonts.gstatic.com;
  connect-src 'self' https://api.example.com wss://socket.example.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';
```

- `script-src` incluye un hash para scripts inline y evita `unsafe-inline`.
- `frame-ancestors 'none'` desactiva el clickjacking.
- `connect-src` permite solo APIs y puntos de WebSocket autorizados.

### 3.2 Permissions Policy (antes Feature‑Policy)

Restringe características poderosas del navegador que la PWA podría heredar sin necesidad.

```http
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=()
```

### 3.3 Atributo Sandbox para Contenido Embebido

Si tu PWA incorpora iframes de terceros, aplícales sandbox para impedir la ejecución de scripts y la navegación no deseada.

```html
<iframe src="https://maps.example.com"
        sandbox="allow-scripts allow-same-origin"
        loading="lazy"></iframe>
```

---

## 4. Autenticación y Autorización

### 4.1 Autenticación basada en Tokens con JWT

Utiliza **JSON Web Tokens (JWT)** para autenticación sin estado, pero nunca los almacenes en `localStorage`; emplea **cookies seguras, HttpOnly** para mitigar el robo mediante XSS.

```http
Set-Cookie: token=eyJhbGciOi...; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600
```

### 4.2 Rotación de Refresh Tokens

Implementa refresh tokens rotativos para limitar el daño de un token comprometido.

1. El cliente envía el refresh token.
2. El servidor lo valida y emite un nuevo access token **y** un nuevo refresh token.
3. El refresh token anterior se invalida en la base de datos.

### 4.3 Mitigación de CSRF

Aun con cookies SameSite, usa **tokens anti‑CSRF** para peticiones POST que cambian el estado.

```html
<input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
```

En el servidor, verifica que el token coincida con la sesión.

---

## 5. Almacenamiento Seguro de Datos

Las PWAs pueden almacenar datos mediante **IndexedDB**, **Cache API** y **Web Storage**. Cada uno posee sus propias consideraciones de seguridad.

| Almacenamiento | Idoneidad | Consejos de Seguridad |
|----------------|-----------|-----------------------|
| **Cache API** | Recursos estáticos, fallback offline | Cachea solo recursos inmutables. Usa nombres de caché versionados para purgar datos obsoletos. |
| **IndexedDB** | Datos estructurados, preferencias de usuario | Encripta campos sensibles del lado del cliente con la Web Crypto API. |
| **LocalStorage / SessionStorage** | Datos pequeños y no sensibles | Evita guardar secretos. Los datos son accesibles a cualquier script en la página. |

### 5.1 Ejemplo: Encriptar Datos en IndexedDB

```javascript
async function encryptAndStore(key, value) {
  const cryptoKey = await crypto.subtle.generateKey(
    { name: "AES-GCM", length: 256 },
    true,
    ["encrypt", "decrypt"]
  );
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const encoded = new TextEncoder().encode(value);
  const ciphertext = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv },
    cryptoKey,
    encoded
  );
  const db = await openDB('secure-store', 1, {
    upgrade(db) { db.createObjectStore('secrets'); }
  });
  await db.put('secrets', { iv, ciphertext }, key);
}
```

---

## 6. Monitoreo, Auditoría y Respuesta a Incidentes

La seguridad no es una configuración única; es un ciclo continuo.

### 6.1 Escaneo Automatizado

- Usa **OWASP ZAP** o **Burp Suite** para buscar XSS, CSRF y encabezados inseguros.
- Integra auditorías de **Lighthouse** en tus pipelines CI/CD para imponer el cumplimiento de PWA.

### 6.2 Registro en Tiempo de Ejecución

Captura errores del service worker y envíalos a un endpoint seguro para su análisis.

```javascript
self.addEventListener('error', event => {
  fetch('https://logs.example.com/collect', {
    method: 'POST',
    body: JSON.stringify({ message: event.message, stack: event.error.stack })
  });
});
```

### 6.3 Playbook de Incidentes

1. **Detectar**: Alerta disparada por tráfico anómalo o informes de violaciones CSP.
2. **Contener**: Revocar JWT comprometidos, rotar claves de API.
3. **Erradicar**: Parchear código vulnerable, actualizar caché del service worker.
4. **Recuperar**: Desplegar nueva versión, monitorizar para recurrencias.
5. **Post‑mortem**: Documentar la causa raíz y actualizar la lista de verificación de seguridad.

---

## 7. Visión General – Capas de Seguridad en una PWA

```mermaid
flowchart TB
    subgraph "Capa de Transporte"
        H["\"HTTPS\""]
        S["\"HSTS\""]
    end
    subgraph "Service Worker"
        SW["\"Scope Limitation\""]
        CS["\"Cache Validation\""]
    end
    subgraph "Tiempo de Ejecución"
        CSP["\"Content Security Policy\""]
        PP["\"Permissions Policy\""]
        SAN["\"Sandboxed iFrames\""]
    end
    subgraph "Auth & Data"
        JWT["\"Secure Cookies\""]
        REF["\"Refresh Rotation\""]
        ENC["\"IndexedDB Encryption\""]
    end
    subgraph "Monitoreo"
        SCAN["\"OWASP ZAP\""]
        LOG["\"Runtime Logging\""]
    end

    H --> SW --> CSP --> JWT --> SCAN
    S --> SW
    CS --> PP
    PP --> ENC
    LOG --> SCAN
```

El diagrama muestra cómo cada control de seguridad se apoya en el anterior, formando una arquitectura de defensa en profundidad.

---

## 8. Lista de Verificación – Referencia Rápida para una PWA Segura

- [ ] Servir todo el contenido mediante HTTPS con HSTS habilitado.  
- [ ] Registrar service workers con el `scope` más restrictivo posible.  
- [ ] Aplicar Subresource Integrity a cualquier script cargado remotamente.  
- [ ] Imponer una CSP estricta, incluyendo `worker-src` y hashes de scripts.  
- [ ] Utilizar Permissions‑Policy para desactivar funcionalidades de navegador no usadas.  
- [ ] Almacenar tokens de autenticación en cookies Secure, HttpOnly; nunca en `localStorage`.  
- [ ] Rotar refresh tokens y validar tokens anti‑CSRF en peticiones que modifican el estado.  
- [ ] Encriptar datos sensibles en IndexedDB.  
- [ ] Ejecutar escaneos OWASP ZAP automáticos en cada Pull Request.  
- [ ] Configurar registro en tiempo real de errores del service worker.  
- [ ] Mantener un playbook de respuesta a incidentes actualizado.  

---

## 9. Preguntas Frecuentes

**P1: ¿Una PWA necesita un Service Worker para ser segura?**  
No. Medidas como HTTPS, CSP y cookies seguras funcionan de forma independiente. Sin embargo, el Service Worker es donde aparecen la mayoría de los vectores de ataque en tiempo de ejecución, por lo que requiere un endurecimiento adicional.

**P2: ¿Puedo usar `unsafe-inline` en CSP para estilos?**  
Lo ideal es evitarlo. En su lugar, emplea hashes o nonces para los estilos inline, o trasládalos a archivos CSS externos.

**P3: ¿Con qué frecuencia debo rotar los certificados TLS?**  
Let’s Encrypt emite certificados de 90 días; automatiza su renovación. Para certificados auto‑firmados o comerciales, no excedas un año de validez.

---

## 10. Conclusión

Las Progressive Web Apps borran la línea entre experiencias nativas y web, ofreciendo capacidades poderosas que también amplían la superficie de ataque. Al combinar seguridad en el transporte, restringir los alcances de los service workers, aplicar una política de seguridad de contenidos rigurosa y adoptar prácticas robustas de autenticación, puedes proteger tu PWA sin sacrificar rendimiento.

Recuerda: la seguridad es un viaje, no un destino. Mantén tus dependencias actualizadas, audita tu código con regularidad y permanece al tanto de las amenazas emergentes. Con las prácticas descritas aquí, tu PWA permanecerá firme frente a los ataques web más comunes.

---

## <span class='highlight-content'>Ver También</span>

- [MDN Web Docs – Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
- [OWASP Top Ten Project](https://owasp.org/www-project-top-ten/)
- [Mozilla – Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
- [Web.dev – HTTPS and HSTS](https://web.dev/transport-layer-security/)
- [RFC 6797 – HTTP Strict Transport Security (HSTS)](https://tools.ietf.org/html/rfc6797)