# Compatibilidade de Navegadores e Acesso de Rede Privada (CCSP/1) Este documento detalha o funcionamento, as garantias de segurança e os requisitos de compatibilidade entre aplicações web (servidas sob **HTTPS**) e o agente desktop **CyberCore Signer** em loopback local (`http://127.0.0.1:21325`). --- ## 1. Contexto Arquitetural: HTTPS para Loopback HTTP Tradicionalmente, navegadores modernos impedem o carregamento de recursos inseguros (`http://`) a partir de contextos seguros (`https://`), mecanismo conhecido como **Mixed Content**. No entanto, para agentes de assinatura digital locais e periféricos de hardware, o consórcio **W3C (Secure Contexts Specification)** e os motores de renderização modernos tratam o loopback IPv4 (`127.0.0.1`) e `localhost` como **Origens Potencialmente Confiáveis (*Potentially Trustworthy Origins*)**. ```mermaid sequenceDiagram autonumber participant Web as Aplicação Web (HTTPS) participant Browser as Navegador (Chrome/Firefox/Safari) participant Agent as CyberCore Signer (127.0.0.1:21325) Note over Web,Agent: 1. Preflight CORS + Private Network Access (PNA) Web->>Browser: fetch("http://127.0.0.1:21325/ccsp/v1/sign", { method: "POST" }) Browser->>Agent: OPTIONS /ccsp/v1/sign
Origin: https://meusistema.com.br
Access-Control-Request-Private-Network: true Agent-->>Browser: HTTP 204 No Content
Access-Control-Allow-Origin: *
Access-Control-Allow-Private-Network: true
Access-Control-Allow-Methods: GET, POST, OPTIONS Note over Browser,Agent: 2. Requisição Real CCSP/1 Browser->>Agent: POST /ccsp/v1/sign (JSON Digest) Agent-->>Browser: HTTP 200 OK (Assinatura Base64) Browser-->>Web: Retorno Promise Resolvida ``` --- ## 2. Private Network Access (PNA / W3C) O **Private Network Access (PNA)** é uma especificação de segurança do W3C (adotada inicialmente pelo motor Chromium a partir do Chrome 98+) com o objetivo de proteger a rede local contra ataques de Cross-Site Request Forgery (CSRF). ### Como a especificação classifica as redes: 1. **Pública (*public*):** A internet pública (ex: `https://meusistema.com.br`). 2. **Privada (*private*):** Redes locais RFC 1918 (ex: `192.168.x.x`, `10.x.x.x`). 3. **Local (*local*):** A máquina do próprio usuário (`127.0.0.1`, `localhost`). Quando uma página em um espaço de IP mais público solicita um recurso em um espaço mais restrito (como `public` $\rightarrow$ `local`), o navegador dispara uma requisição preliminar **Preflight PNA**. ### Headers Enviados pelo Navegador: ```http OPTIONS /ccsp/v1/sign HTTP/1.1 Host: 127.0.0.1:21325 Origin: https://meusistema.com.br Access-Control-Request-Method: POST Access-Control-Request-Headers: Content-Type, X-CyberCore-AppId Access-Control-Request-Private-Network: true ``` ### Resposta Padrão do CyberCore Signer (`CcspServer`): O servidor do agente desktop intercepta requisições `OPTIONS` e responde imediatamente com: ```http HTTP/1.1 204 No Content Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization, X-CyberCore-AppId, Origin, Accept Access-Control-Allow-Private-Network: true Access-Control-Max-Age: 86400 Connection: close ``` > [!NOTE] > O cabeçalho `Access-Control-Allow-Private-Network: true` é fundamental para autorizar o navegador a enviar requisições privadas originadas de sites HTTPS públicos. --- ## 3. Matriz de Compatibilidade de Navegadores | Navegador | Versão Mínima | Suporte HTTPS $\rightarrow$ 127.0.0.1 | Comportamento PNA | Requer Ação do Usuário? | | :--- | :---: | :---: | :---: | :---: | | **Google Chrome** | 88+ | ✅ Suportado | PNA ativo com preflight | Não (automático) | | **Microsoft Edge** | 88+ | ✅ Suportado | PNA ativo com preflight | Não (automático) | | **Mozilla Firefox** | 84+ | ✅ Suportado | Trata 127.0.0.1 como Secure Context | Não (automático) | | **Opera / Brave** | Recentes | ✅ Suportado | Idêntico ao Chromium (ver nota Brave) | Pode exigir ajuste no Shields | | **Apple Safari** | 15.4+ (macOS) | ✅ Suportado | Trata loopback como seguro | Não | --- ## 4. Casos de Borda e Soluções (Troubleshooting) ### 4.1. Navegador Brave (Brave Shields) O Brave possui um mecanismo agressivo de proteção que pode bloquear preflights para `127.0.0.1`. - **Sintoma:** O SDK lança `AgentNotRunningError`, mesmo com o agente em execução. - **Resolução:** O usuário deve clicar no ícone do escudo (Shields) na barra de endereços da aplicação e desativar temporariamente o bloqueio para a origem ou permitir conexões locais nas configurações (`brave://settings/shields`). ### 4.2. Extensões de Privacidade e AdBlockers Extensões como *uBlock Origin*, *Privacy Badger* ou *NoScript* podem conter regras genéricas de bloqueio para portas locais. - **Resolução:** Adicionar a URL da sua aplicação web à lista de permissões (*allowlist*) da extensão. ### 4.3. Softwares de Antivírus e Endpoint Protection Alguns antivírus com módulo de "Proteção Web" ou "Inspeção HTTPS" tentam atuar como proxy transparente no loopback local: - **Resolução:** Configurar o antivírus para excluir a porta `21325` da inspeção de tráfego local. ### 4.4. Políticas Corporativas de Grupo (GPO / Chromium Enterprise) Em ambientes corporativos restritos, administradores de rede podem bloquear chamadas a redes privadas via política: - Política Chromium: `InsecurePrivateNetworkRequestsAllowed` ou `InsecurePrivateNetworkRequestsAllowedForUrls`. - **Resolução:** Adicionar a URL do portal web corporativo na política `InsecurePrivateNetworkRequestsAllowedForUrls`. --- ## 5. Boas Práticas para Desenvolvedores Web 1. **Use sempre o SDK `@cybercore/signer-sdk`:** Ele gerencia timeouts, fallbacks de preflight e detecção de conectividade. 2. **Utilize `isInstalled()` antes de iniciar fluxos de assinatura:** ```javascript import { CyberCoreSigner } from '@cybercore/signer-sdk'; const signer = new CyberCoreSigner(); if (!(await signer.isInstalled())) { mostrarModalInstalacaoAgente(); } ``` 3. **Capture `AgentNotRunningError` e exiba o guia de ajuda:** ```javascript import { AgentNotRunningError } from '@cybercore/signer-sdk'; try { await signer.connect(); } catch (err) { if (err instanceof AgentNotRunningError) { console.info(err.actionableGuidance); exibirAjudaAoUsuario(err.actionableGuidance); } } ```