---
title: "Освоение искусства безопасности прогрессивных веб‑приложений"
---

# Освоение искусства безопасности прогрессивных веб‑приложений

Прогрессивные веб‑приложения (PWA) стали де‑факто стандартом для предоставления приложений‑подобного опыта в открытом вебе. Их ключевые преимущества — возможность работы офлайн, push‑уведомления и устанавливаемость — одновременно открывают новые поверхности атаки, с которыми традиционные сайты обычно не сталкиваются. В этой статье мы подробно рассматриваем **жизненный цикл безопасности PWA**, объединяя жёсткую настройку сети, защиту в runtime и лучшие практики эксплуатации. К концу вы получите чек‑лист, который можно применить к любому проекту, будь то простой новостной читатель или сложная платформa электронной коммерции.

> **TL;DR**: Защитите своё PWA с помощью HTTPS, строгой CSP, изолированных сервис‑воркеров, проверяйте все входные данные и внедряйте непрерывный мониторинг.

---

## 1. Транспортный уровень – первая линия обороны

### 1.1 Принудительное использование HTTPS везде

Все современные браузеры требуют **HTTPS** для регистрации сервис‑воркера. Однако некоторые ресурсы (например, сторонняя аналитика) могут всё ещё загружаться по HTTP, вызывая предупреждения о смешанном контенте и открывая дверь атакам типа «человек посередине» (MITM).

**Ключевые шаги:**

1. Получите доверенный TLS‑сертификат (Let’s Encrypt, Cloudflare и пр.).
2. Перенаправьте все запросы `http://` на `https://` через конфигурацию сервера.
3. Включите **HSTS** (HTTP Strict Transport Security), чтобы заставить браузеры использовать HTTPS в течение заданного периода.

```nginx
# Пример конфигурации NGINX
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‑настройки …
}
```

> **Примечание**: HSTS устраняет атаки с понижением уровня защиты, но требует тщательного планирования, потому что после установки браузеры откажутся подключаться по HTTP на указанный срок.

### 1.2 Привязка сертификата (по желанию)

Для особо чувствительных PWA — банковских приложений, систем хранения медицинских данных — рассмотрите **привязку сертификата** с помощью **Public Key Pinning Extension for HTTP (HPKP)**. Несмотря на то, что HPKP устарел из‑за рисков при развёртывании, аналогичный уровень безопасности можно получить через заголовок **Expect‑CT**, который заставляет использовать Certificate Transparency.

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

---

## 2. Жёсткая настройка сервис‑воркеров

Сервис‑воркеры – сердце офлайн‑возможностей PWA. Они работают в фоновом потоке с повышенными привилегиями, что делает их прекрасной целью для эксплуатации.

### 2.1 Ограничение области действия (scope)

Указывайте максимально узкий `scope` при регистрации сервис‑воркера. Это предотвратит перехват запросов за пределами предназначенной области.

```javascript
navigator.serviceWorker.register('/sw.js', { scope: '/app/' })
  .then(reg => console.log('SW зарегистрирован с областью:', reg.scope));
```

### 2.2 Проверка целостности с помощью Subresource Integrity (SRI)

Если вы загружаете скрипт сервис‑воркера с CDN, защитите его с помощью **SRI**, чтобы убедиться, что полученный код совпадает с ожидаемым хешем.

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

### 2.3 Политика безопасности контента (CSP) для сервис‑воркеров

Строгий **CSP** может помешать воркеру загружать вредоносные скрипты или подключаться к непредвиденным источникам.

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

> **Совет**: Используйте директиву `worker-src` (поддерживается в новых браузерах), чтобы явно указать разрешённые источники скриптов воркеров.

### 2.4 Управление состоянием и проверка кеша

Никогда не доверяйте записям кеша слепо. Всегда проверяйте актуальность закешированных ответов, особенно для токенов аутентификации или персональных данных.

```javascript
self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);
  if (url.pathname.startsWith('/api/')) {
    // Обход кеша для динамических данных
    event.respondWith(fetch(event.request));
    return;
  }
  // Стратегия «cache‑first» для статических ресурсов
  event.respondWith(
    caches.match(event.request).then(cached => cached || fetch(event.request))
  );
});
```

---

## 3. Защита в runtime – CSP, Permissions Policy и sandbox

### 3.1 Полноценный CSP

Хорошо продуманный CSP снижает риск **Cross‑Site Scripting (XSS)** и **Data Injection** атак. Ниже пример политики, адаптированный под типичное PWA:

```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` содержит хеш для встроенных скриптов, избавляясь от `unsafe-inline`.
- `frame-ancestors 'none'` отключает кликджеккинг.
- `connect-src` разрешает только нужные API и WebSocket‑конечные точки.

### 3.2 Permissions Policy (ранее Feature‑Policy)

Ограничьте мощные возможности браузера, которые ваше PWA может случайно унаследовать.

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

### 3.3 Атрибут sandbox для встроенного контента

Если ваше PWA встраивает сторонние iframe, изолируйте их с помощью sandbox, чтобы запретить исполнение скриптов и навигацию.

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

---

## 4. Аутентификация и авторизация

### 4.1 Токен‑базированная аутентификация с JWT

Для без­состоя­тного доступа используйте **JSON Web Tokens (JWT)**, но **не храните** их в `localStorage` — используйте **Secure, HttpOnly cookies**, чтобы уменьшить риск кражи через XSS.

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

### 4.2 Ротация refresh‑токенов

Реализуйте вращающиеся refresh‑токены, чтобы ограничить последствия компрометации токена.

1. Клиент отправляет refresh‑токен.
2. Сервер проверяет и выдаёт новый access‑токен **и** новый refresh‑токен.
3. Старый refresh‑токен помечается как недействительный в базе.

### 4.3 Защита от CSRF

Несмотря на использование same‑site куки, применяйте **анти‑CSRF токены** для запросов, изменяющих состояние.

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

На сервере проверяйте соответствие токена сессии.

---

## 5. Безопасное хранилище данных

PWA могут хранить данные через **IndexedDB**, **Cache API** и **Web Storage**. У каждого из этих механизмов свои особенности безопасности.

| Хранилище | Подходящее использование | Советы по безопасности |
|-----------|--------------------------|------------------------|
| **Cache API** | Статические ресурсы, офлайн‑fallback | Кешировать только неизменяемые файлы. Использовать версии кешей для очистки устаревших данных. |
| **IndexedDB** | Структурированные данные, пользовательские настройки | Шифровать чувствительные поля на клиенте с помощью Web Crypto API. |
| **LocalStorage / SessionStorage** | Небольшие, не‑чувствительные данные | Не хранить секреты. Доступен всем скриптам страницы. |

### 5.1 Пример: шифрование данных в 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. Мониторинг, аудит и реагирование на инциденты

Безопасность — это не разовый набор настроек, а непрерывный цикл.

### 6.1 Автоматическое сканирование

- Используйте **OWASP ZAP** или **Burp Suite** для поиска XSS, CSRF и некорректных заголовков.
- Интегрируйте **Lighthouse** в CI/CD, чтобы проверять соответствие PWA‑требованиям.

### 6.2 Логирование в runtime

Перехватывайте ошибки сервис‑воркера и отправляйте их в безопасный эндпоинт для анализа.

```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 План реагирования на инциденты

1. **Обнаружение**: Тревога от аномального трафика или CSP‑отчётов.
2. **Сдерживание**: Аннулируйте скомпрометированные JWT, отзовите API‑ключи.
3. **Устранение**: Исправьте уязвимый код, обновите кеш сервис‑воркера.
4. **Восстановление**: Разверните новую версию, продолжайте мониторинг.
5. **Пост‑мортем**: Зафиксируйте коренную причину, обновите чек‑лист безопасности.

---

## 7. Визуальный обзор – уровни защиты в PWA

```mermaid
flowchart TB
    subgraph "Транспортный уровень"
        H["\"HTTPS\""]
        S["\"HSTS\""]
    end
    subgraph "Сервис‑воркер"
        SW["\"Ограничение области\""]
        CS["\"Проверка кеша\""]
    end
    subgraph "Runtime"
        CSP["\"Content Security Policy\""]
        PP["\"Permissions Policy\""]
        SAN["\"Sandboxed iFrames\""]
    end
    subgraph "Аутентификация и данные"
        JWT["\"Secure Cookies\""]
        REF["\"Refresh Rotation\""]
        ENC["\"IndexedDB Encryption\""]
    end
    subgraph "Мониторинг"
        SCAN["\"OWASP ZAP\""]
        LOG["\"Runtime Logging\""]
    end

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

Диаграмма демонстрирует, как каждый контроль безопасности усиливает предыдущий, формируя архитектуру «защита в глубину».

---

## 8. Чек‑лист – быстрый справочник по защите PWA

- [ ] Обслуживание всего контента по HTTPS с включённым HSTS.
- [ ] Регистрация сервис‑воркеров с максимально узким `scope`.
- [ ] Применение Subresource Integrity для всех внешних скриптов.
- [ ] Настройка строгой CSP, включая `worker-src` и хеши скриптов.
- [ ] Отключение ненужных функций через Permissions‑Policy.
- [ ] Хранение токенов аутентификации в Secure, HttpOnly cookie; не в localStorage.
- [ ] Ротация refresh‑токенов и проверка CSRF‑токенов в запросах, изменяющих состояние.
- [ ] Шифрование конфиденциальных данных в IndexedDB.
- [ ] Автоматическое сканирование OWASP ZAP на каждом PR.
- [ ] Организация реального времени логирования ошибок сервис‑воркера.
- [ ] Наличие плана реагирования на инциденты.

---

## 9. Часто задаваемые вопросы

**В: Нужно ли сервис‑воркер для обеспечения безопасности PWA?**  
**О:** Нет. Такие меры, как HTTPS, CSP и защищённые cookie, работают независимо. Однако большинство атак в runtime происходят именно через сервис‑воркер, поэтому ему требуется дополнительное укрепление.

**В: Можно ли использовать `unsafe-inline` в CSP для стилей?**  
**О:** По возможности избегайте. Лучше добавить хеш или nonce к встроенным стилям или перенести их во внешние CSS‑файлы.

**В: Как часто менять TLS‑сертификаты?**  
**О:** Let’s Encrypt выдаёт сертификаты на 90 дней — автоматизируйте их обновление. Для самоподписных или коммерческих сертификатов старайтесь не превышать 1 год срока действия.

---

## 10. Заключение

Прогрессивные веб‑приложения стирают границу между нативными и веб‑приложениями, предоставляя мощные возможности, но одновременно расширяя поверхность атаки. Слоями транспортной безопасности, ужесточением областей сервис‑воркеров, строгой CSP и надёжными практиками аутентификации вы сможете защитить своё PWA без потери производительности.

Помните: безопасность — это путь, а не цель. Обновляйте зависимости, регулярно проводите аудит кода и следите за новыми угрозами. Применив изложенные здесь рекомендации, ваше PWA будет устойчиво против большинства типичных веб‑угроз.

---

## <span class='highlight-content'>Смотрите также</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)