Monitorar seu aplicativo da Web com a API Reporting

Use a API Reporting para monitorar violações de segurança, chamadas de API descontinuadas e muito mais.

Maud Nalpas
Maud Nalpas

Alguns erros ocorrem apenas na produção. Você não os verá localmente ou durante desenvolvimento porque usuários reais, redes reais e dispositivos reais mudar o jogo. A API de relatórios ajuda a detectar alguns desses erros, como violações de segurança ou APIs descontinuadas e que vão ser descontinuadas em breve chamadas no seu site e as transmite para um ponto de extremidade que você especificado.

Ele permite que você declare o que gostaria de monitorar por meio de cabeçalhos HTTP e é operado pelo navegador.

Ao configurar a API Reporting, você tem a tranquilidade de saber que, quando os usuários esses tipos de erros, para que possa corrigi-los.

Nesta postagem, explicamos o que essa API pode fazer e como usá-la. Vamos ao que interessa!

Demonstração e código

Veja a API Reporting em ação Chrome 96 e mais recentes (Chrome Beta ou Canary, em outubro de 2021).

Visão geral

Diagrama com um resumo das etapas abaixo, desde a geração até o acesso do desenvolvedor a eles
Como os relatórios são gerados e enviados.

Vamos supor que seu site, site.example, tenha uma Política de segurança de conteúdo e uma Política de documentos. Você não sabe o que eles fazem? Tudo bem, você ainda será entender este exemplo.

Você decide monitorar seu site para saber quando essas políticas são violadas, mas também porque é recomendável ficar de olho nas APIs descontinuadas ou que vão ser descontinuadas pela sua base de código.

Para fazer isso, configure um cabeçalho Reporting-Endpoints e mapeie esses endpoints por meio da diretiva report-to nas políticas, quando necessário.

Reporting-Endpoints: main-endpoint="https://reports.example/main", default="https://reports.example/default"
# Content-Security-Policy violations and Document-Policy violations
# will be sent to main-endpoint
Content-Security-Policy: script-src 'self'; object-src 'none'; report-to main-endpoint;
Document-Policy: document-write=?0; report-to=main-endpoint;
# Deprecation reports don't need an explicit endpoint because
# these reports are always sent to the `default` endpoint

Ocorreu um imprevisto, e essas políticas serão violadas em alguns de seus usuários.

Exemplos de violações

index.html

<script src="script.js"></script>
<!-- CSP VIOLATION: Try to load a script that's forbidden as per the Content-Security-Policy -->
<script src="https://example.com/script.js"></script>

script.js, carregado por index.html.

// DOCUMENT-POLICY VIOLATION: Attempt to use document.write despite the document policy
try {
  document.write('<h1>hi</h1>');
} catch (e) {
  console.log(e);
}
// DEPRECATION: Call a deprecated API
const webkitStorageInfo = window.webkitStorageInfo;

O navegador gera um relatório de violação da CSP, um relatório de violação da política de documentos e uma descontinuação que capturam esses problemas.

Com um pequeno atraso de até um minuto, o navegador envia os relatórios ao endpoint configurado para esse tipo de violação. Os relatórios são enviados fora de banda das navegador (não pelo seu servidor nem pelo seu site).

Os endpoints recebem esses relatórios.

Agora você pode acessar os relatórios sobre esses endpoints e monitorar o que deu errado. Você já pode começar a resolver o problema que está afetando os usuários.

Exemplo de relatório

{
  "age": 2,
  "body": {
    "blockedURL": "https://site2.example/script.js",
    "disposition": "enforce",
    "documentURL": "https://site.example",
    "effectiveDirective": "script-src-elem",
    "originalPolicy": "script-src 'self'; object-src 'none'; report-to main-endpoint;",
    "referrer": "https://site.example",
    "sample": "",
    "statusCode": 200
  },
  "type": "csp-violation",
  "url": "https://site.example",
  "user_agent": "Mozilla/5.0... Chrome/92.0.4504.0"
}

Casos de uso e tipos de relatório

A API Reporting pode ser configurada para ajudar você a monitorar vários tipos de avisos ou problemas interessantes que acontecem em todo o site:

Tipo de relatório Exemplo de uma situação em que um relatório seria gerado
Violação da CSP (apenas nível 3) Você definiu uma Content-Security-Policy (CSP) em uma das suas páginas, mas a página está tentando carregar um script que não é permitido pela CSP.
Violação de COOP Você definiu uma Cross-Origin-Opener-Policy em uma página, mas uma janela de origem cruzada está tentando interagir diretamente com o documento.
Violação de COEP Você definiu um Cross-Origin-Embedder-Policy em uma página, mas o documento inclui um iframe de origem cruzada que não ativou o carregamento por documentos de origem cruzada.
Violação da política do documento A página tem uma política de documento que impede o uso de document.write, mas um script tenta chamar document.write.
Violação da política de permissões A página tem uma política de permissões que impede o uso do microfone e um script que solicita a entrada de áudio.
Aviso de descontinuação A página usa uma API que foi descontinuada ou será descontinuada. ou usando um script de terceiros de alto nível.
Intervenção A página está tentando fazer algo que o navegador decide não respeitar, por motivos de segurança, desempenho ou experiência do usuário. Exemplo no Chrome: a página usa document.write em redes lentas ou chama navigator.vibrate em um frame de origem cruzada com o qual o usuário ainda não interagiu.
Acidente O navegador falha enquanto seu site está aberto.

Relatórios

Como são os relatórios?

O navegador envia relatórios ao endpoint que você configurou. Ele envia solicitações parecidas com estas:

POST
Content-Type: application/reports+json

O payload dessas solicitações é uma lista de relatórios.

Exemplo de lista de relatórios

[
  {
    "age": 420,
    "body": {
      "columnNumber": 12,
      "disposition": "enforce",
      "lineNumber": 11,
      "message": "Document policy violation: document-write is not allowed in this document.",
      "policyId": "document-write",
      "sourceFile": "https://site.example/script.js"
    },
    "type": "document-policy-violation",
    "url": "https://site.example/",
    "user_agent": "Mozilla/5.0... Chrome/92.0.4504.0"
  },
  {
    "age": 510,
    "body": {
      "blockedURL": "https://site.example/img.jpg",
      "destination": "image",
      "disposition": "enforce",
      "type": "corp"
    },
    "type": "coep",
    "url": "https://dummy.example/",
    "user_agent": "Mozilla/5.0... Chrome/92.0.4504.0"
  }
]

Veja a seguir os dados que você encontra em cada um desses relatórios:

Campo Descrição
age O número de milissegundos entre o carimbo de data/hora do relatório e a hora atual.
body Os dados reais do relatório, serializados em uma string JSON. Os campos contidos no body de um relatório são determinados pelo type do relatório. ⚠️ Denúncias de tipos diferentes têm corpos diferentes. Para conferir o corpo exato de cada tipo de relatório, confira o endpoint de relatórios de demonstração e siga as instruções para gerar exemplos de relatórios.
type Um tipo de relatório, por exemplo, csp-violation ou coep.
url O endereço do documento ou worker a partir do qual o relatório foi gerado. Dados sensíveis, como nome de usuário, senha e fragmento, são retirados deste URL.
user_agent O cabeçalho User-Agent da solicitação a partir da qual o relatório foi gerado.

Relatórios credenciados

Os endpoints de relatórios que têm a mesma origem da página que gera o relatório recebem as credenciais. (cookies) nas solicitações que contêm os relatórios.

As credenciais podem oferecer mais contexto útil sobre o relatório. para por exemplo, se a conta de um determinado usuário está acionando erros de forma consistente ou se uma determinada sequência de ações realizadas em outras páginas está acionando um relatório nesta página.

Quando e como o navegador envia relatórios?

Os relatórios são exibidos fora de banda no seu site: o navegador controla quando eles são enviados aos endpoints configurados. Também não há como controlar quando o navegador envia relatórios; ele captura, enfileira e envia automaticamente momento adequado.

Isso significa que há pouca ou nenhuma preocupação de desempenho ao usar a API Reporting.

Os relatórios são enviados com um atraso de até um minuto para aumentar as chances de envio em lotes. Isso economiza largura de banda para respeitar a conexão de rede do usuário, especialmente muito importante no celular. O navegador também pode atrasar a entrega se estiver ocupado processando prioridade mais alta funcionar ou se o usuário estiver em uma rede lenta e/ou congestionada no momento.

Problemas próprios ou de terceiros

Serão enviados relatórios gerados devido a violações ou descontinuações na sua página. aos endpoints que você configurou. Isso inclui violações cometidas por scripts de terceiros. em execução na sua página.

As violações ou descontinuações que aconteceram em um iframe de origem cruzada incorporado à sua página vão resultar em: não serão relatados aos seus endpoints (pelo menos não por padrão). Um iframe pode configurar sua própria relatórios e até mesmo reportar ao serviço de geração de relatórios do seu site; mas aí está ao site com frames. A maioria dos relatórios só será gerada se a política de uma página for violada. e que as políticas da sua página e as do iframe são diferentes.

Exemplo com descontinuações

Se o cabeçalho Reporting-Endpoints estiver configurado na sua página: a API descontinuada chamada por scripts de terceiros em execução na sua página será informada ao endpoint. Uma API descontinuada chamada por um iframe incorporado na sua página não será informada ao endpoint. Um relatório de descontinuação será gerado somente se o servidor de iframe tiver configurado os Relatórios-Endpoints, e ele será enviado para qualquer endpoint configurado pelo servidor do iframe.
Se o cabeçalho Reporting-Endpoints estiver configurado na sua página: a API descontinuada chamada por scripts de terceiros em execução na sua página será informada ao endpoint. Uma API descontinuada chamada por um iframe incorporado na sua página não será informada ao endpoint. Um relatório de descontinuação será gerado somente se o servidor de iframe tiver configurado os Relatórios-Endpoints, e ele será enviado para qualquer endpoint configurado pelo servidor do iframe.

Suporte ao navegador

A tabela abaixo resume o suporte do navegador para a API Reporting v1, que é Cabeçalho Reporting-Endpoints. O suporte do navegador para a API Reporting v0 (cabeçalho Report-To) é o A única exceção é um tipo de relatório: o registro de erros de rede não é compatível com a nova API Reporting. Para mais detalhes, leia o guia de migração.

Tipo de relatório Chrome Chrome para iOS Safari Firefox Edge
Violação da CSP (apenas nível 3)* ✔ Sim ✔ Sim ✔ Sim ✘ Não ✔ Sim
Registro de erros de rede ✘ Não ✘ Não ✘ Não ✘ Não ✘ Não
Violação de COOP/COEP ✔ Sim ✘ Não ✔ Sim ✘ Não ✔ Sim
Todos os outros tipos: violação da política de documentos, descontinuação, intervenção, falha ✔ Sim ✘ Não ✘ Não ✘ Não ✔ Sim

Esta tabela resume apenas o suporte para report-to com o novo cabeçalho Reporting-Endpoints. Leia as dicas de migração de relatórios da CSP se quiser migrar para o Reporting-Endpoints.

Como usar a API Reporting

Decidir para onde os relatórios devem ser enviados

Você tem duas opções:

  • Envie relatórios para um serviço de coletor de relatórios atual.
  • Envie relatórios para um coletor de relatórios que você mesmo cria e opera.

Opção 1: use um serviço de coletor de relatórios

Alguns exemplos de serviços de coletores de relatórios são:

Caso você conheça outras soluções, abra um problema e atualizaremos esta postagem.

Além do preço, considere os seguintes pontos ao escolher um coletor de relatórios: 🧐

  • Este coletor é compatível com todos os tipos de relatório? Por exemplo, nem todas as soluções de endpoint de relatórios oferecem suporte a relatórios COOP/COEP.
  • Você se sente confortável em compartilhar algum dos URLs do seu aplicativo com um coletor de relatórios de terceiros? Mesmo que o navegador remova informações sensíveis desses URLs, essas informações podem ser vazadas dessa forma. Se isso parece muito arriscado para seu aplicativo, opere seu próprio endpoint de relatórios.

Opção 2: criar e operar seu próprio coletor de relatórios

Criar seu próprio servidor que recebe relatórios não é tão simples. Para começar, você pode bifurcar nossa boilerplate leve. Ele é integrado ao Express e pode receber e exibir relatórios.

  1. Acesse o coletor de relatórios padrão.

  2. Clique em Remixar para editar para tornar o projeto editável.

  3. Agora você tem seu clone. Você pode personalizá-lo para suas próprias finalidades.

Se você não estiver usando o código boilerplate e estiver criando seu próprio servidor do zero:

  • Verifique se há solicitações POST com um Content-Type de application/reports+json para reconhecer relatórios solicitações enviadas pelo navegador para o endpoint.
  • Se o endpoint estiver em uma origem diferente do site, verifique se ele oferece suporte às solicitações de simulação do CORS.
.

Opção 3: combinar as opções 1 e 2

Talvez você queira deixar que um provedor específico cuide de alguns tipos de relatórios, mas tenha um serviço interno solução para os outros.

Nesse caso, defina vários endpoints da seguinte maneira:

Reporting-Endpoints: endpoint-1="https://reports-collector.example", endpoint-2="https://my-custom-endpoint.example"

Configurar o cabeçalho Reporting-Endpoints

Defina um cabeçalho de resposta Reporting-Endpoints. Seu valor deve ser um ou uma série de valores separados por vírgula pares de chave-valor:

Reporting-Endpoints: main-endpoint="https://reports.example/main", default="https://reports.example/default"

Se você estiver migrando da API Reporting legada para a nova API Reporting, talvez seja útil defina ambos Reporting-Endpoints e Report-To. Confira mais detalhes no guia de migração. Especificamente, se você estiver usando relatórios para Content-Security-Policy violações somente pela diretiva report-uri. Verifique as etapas de migração para a geração de relatórios da CSP.

Reporting-Endpoints: main-endpoint="https://reports.example/main", default="https://reports.example/default"
Report-To: ...

Chaves (nomes de endpoints)

Cada chave pode ser um nome de sua escolha, como main-endpoint ou endpoint-1. Você pode definir diferentes endpoints nomeados para diferentes relatórios tipos, por exemplo, my-coop-endpoint, my-csp-endpoint. Com isso, você podem rotear relatórios para endpoints diferentes, dependendo do tipo.

Se você quiser receber intervenção, suspensão de uso e/ou falha relatórios, defina um endpoint chamado default.

Se o cabeçalho Reporting-Endpoints não definir um endpoint default, os relatórios desse tipo não serão enviados, embora sejam gerados.

Valores (URLs)

Cada valor é um URL de sua escolha, para onde os relatórios serão enviados. O URL a ser definido aqui depende do que você decidiu na Etapa 1.

Um URL de endpoint:

Exemplos

Reporting-Endpoints: my-coop-endpoint="https://reports.example/coop", my-csp-endpoint="https://reports.example/csp", default="https://reports.example/default"

É possível usar cada endpoint nomeado na política apropriada ou usar um um único endpoint em todas as políticas.

Onde definir o cabeçalho?

Na nova API Reporting, aquela que é abordada neste post: os relatórios têm o escopo de documentos. Isso significa que, para um origem, documentos diferentes, como site.example/page1 e site.example/page2, pode enviar relatórios para endpoints diferentes.

Para receber denúncias de violações ou descontinuações em qualquer página da sua site, defina o cabeçalho como um middleware em todas as respostas.

Veja um exemplo no Express:

const REPORTING_ENDPOINT_BASE = 'https://report.example';
const REPORTING_ENDPOINT_MAIN = `${REPORTING_ENDPOINT_BASE}/main`;
const REPORTING_ENDPOINT_DEFAULT = `${REPORTING_ENDPOINT_BASE}/default`;

app.use(function (request, response, next) {
  // Set up the Reporting API
  response.set(
    'Reporting-Endpoints',
    `main-endpoint="${REPORTING_ENDPOINT_MAIN}", default="${REPORTING_ENDPOINT_DEFAULT}"`,
  );
  next();
});

Editar suas políticas

Agora que o cabeçalho Reporting-Endpoints está configurado, adicione um report-to diretiva a cada cabeçalho da política que você quer que esteja com problemas e detecção de ameaças. O valor de report-to precisa ser um dos endpoints nomeados que você configurada.

É possível usar o endpoint de várias políticas ou utilizar diferentes endpoints entre as políticas.

Para cada política, o valor report-to precisa ser um dos endpoints nomeados que você configurou.

report-to não é necessário para descontinuação, intervenção e falhas. e detecção de ameaças. Essas denúncias não estão vinculadas a nenhuma política. Elas são geradas um endpoint default está configurado e é enviado para esse endpoint default.

Exemplo

# Content-Security-Policy violations and Document-Policy violations
# will be sent to main-endpoint
Content-Security-Policy: script-src 'self'; object-src 'none'; report-to main-endpoint;
Document-Policy: document-write=?0;report-to=main-endpoint;
# Deprecation reports don't need an explicit endpoint because
# these reports are always sent to the default endpoint

Exemplo de código

Para conferir tudo isso no contexto, veja abaixo um exemplo de servidor de nó que usa o Express e reúne todas as peças discutidas neste artigo. Ele mostra como configurar relatórios para vários tipos de relatório diferentes e exibir os resultados.

Depurar a configuração dos relatórios

Gerar relatórios intencionalmente

Ao configurar a API Reporting, você provavelmente terá que violar intencionalmente suas políticas para verificar se os relatórios são gerados e enviados conforme esperado. Para ver um exemplo de código que viola as políticas e faz outras coisas ruins que gerar relatórios de todos os tipos, confira a demonstração.

Poupe tempo

As denúncias podem ser enviadas com um atraso, cerca de um minuto, que é um tempo longo. durante a depuração. Frequentemente, ao depurar no Chrome, você pode usar a sinalização --short-reporting-delay para receber relatórios assim que forem gerados.

Execute este comando no terminal para ativar essa flag:

YOUR_PATH/TO/EXECUTABLE/Chrome --short-reporting-delay

Usar o DevTools

No Chrome, use o DevTools para ver os relatórios que foram ou serão enviados.

A partir de outubro de 2021, esse recurso será experimental. Para usá-la, siga estas etapas:

  1. Use o Chrome 96 ou versões mais recentes (digite chrome://version no navegador para confirmar).
  2. Digite ou cole chrome://flags/#enable-experimental-web-platform-features na barra de URL do Chrome.
  3. Clique em Ativado.
  4. Reinicie o navegador.
  5. Abra o Chrome DevTools.
  6. No Chrome DevTools, abra as Configurações. Em "Experimentos", clique em Ativar o painel da API Reporting no no painel Application.
  7. Atualize o DevTools.
  8. Atualize sua página. Os relatórios gerados pela página em que o DevTools está aberto serão listadas no Chrome DevTools Aplicativo, em API Reporting.
.
Captura de tela do DevTools listando os relatórios
O Chrome DevTools exibe os relatórios gerados na sua página e o status deles.

Status do relatório

A coluna Status informa se um relatório foi enviado.

Status Descrição
Success O navegador enviou o relatório, e o endpoint respondeu com um código de sucesso (200 ou outro código de resposta de sucesso 2xx).
Pending O navegador está tentando enviar o relatório.
Queued O relatório foi gerado, e o navegador não está tentando enviá-lo no momento. Um relatório aparece como Queued em um destes dois casos:
  • O relatório é novo, e o navegador está aguardando para conferir se mais relatórios chegam antes de tentar enviá-lo.
  • O relatório não é novo. o navegador já tentou enviar este relatório, mas falhou, e está aguardando antes de tentar novamente.
MarkedForRemoval Depois de tentar novamente por um tempo (Queued), o navegador parou de tentar enviar o relatório e vai removê-lo da lista de relatórios para envio em breve.

Os relatórios são removidos depois de um tempo, independentemente de terem sido enviados ou não.

Solução de problemas

Os relatórios não são gerados ou enviados como esperado para seu endpoint? Confira algumas dicas para resolver isso.

Os relatórios não são gerados

Os relatórios que aparecem no DevTools foram gerados corretamente. Se o relatório esperado não aparecer nesta lista:

  • Verifique report-to nas suas políticas. Se isso estiver configurado incorretamente, um não será gerado. Acesse Editar suas políticas para corrigir isso. Outra maneira de resolver isso é verificar o console do DevTools no Chrome: se um de erro pop-up no console para a violação esperada, isso significa que sua política provavelmente está e está configurado corretamente.
  • Tenha em mente que apenas os relatórios que foram gerados para o documento no DevTools estão abertos vão aparecer nessa lista. Um exemplo: se o site site1.example incorpora um iframe site2.example que viole uma política e, portanto, gere um relatório, ele só será exibido no DevTools se você abrir o iframe em uma janela própria e abrir o DevTools para essa janela.

Os relatórios são gerados, mas não são enviados ou não são recebidos

E se você conseguir ver um relatório no DevTools, mas o endpoint não o receber?

  • Use atrasos curtos. Talvez você não consiga ver um relatório porque ele ainda não foi enviado!
  • Verifique a configuração do cabeçalho Reporting-Endpoints. Se houver um problema com ele, um relatório que foi gerado corretamente não será enviado. No DevTools, o status do relatório vai continuar Queued (pode pular para Pending e voltar rapidamente para Queued quando uma tentativa de entrega for feita) nesse caso. Alguns erros comuns que podem causar isso:

  • O endpoint é usado, mas não configurado. Exemplo:

Código com um erro
 Document-Policy: document-write=?0;report-to=endpoint-1;
 Reporting-Endpoints: default="https://reports.example/default"

Os relatórios de violação da política de documentos precisam ser enviados para endpoint-1, mas o nome do endpoint não está configurado em Reporting-Endpoints.

  • O endpoint default está ausente. Alguns tipos de relatórios, como descontinuação e intervenção relatórios, serão enviadas apenas para o endpoint chamado default. Leia mais em Configurar o cabeçalho Reporting-Endpoints.

  • Procure problemas na sintaxe dos cabeçalhos da política, como falta de aspas. Mais detalhes.

  • Verifique se o endpoint pode lidar com solicitações de entrada.

    • Verifique se o endpoint oferece suporte às solicitações de simulação do CORS. Caso contrário, ela não vai receber relatórios.

    • Teste o comportamento do endpoint. Para isso, em vez de gerar manualmente, é possível emular o navegador enviando para os endpoints as solicitações semelhantes o que o navegador envia. Execute o comando a seguir:

    curl --header "Content-Type: application/reports+json" \
      --request POST \
      --data '[{"age":420,"body":{"columnNumber":12,"disposition":"enforce","lineNumber":11,"message":"Document policy violation: document-write is not allowed in this document.","policyId":"document-write","sourceFile":"https://dummy.example/script.js"},"type":"document-policy-violation","url":"https://dummy.example/","user_agent":"xxx"},{"age":510,"body":{"blockedURL":"https://dummy.example/img.jpg","destination":"image","disposition":"enforce","type":"corp"},"type":"coep","url":"https://dummy.example/","user_agent":"xxx"}]' \
      YOUR_ENDPOINT
    

    Seu endpoint deve responder com um código de sucesso (200 ou outro código de resposta de sucesso 2xx). Caso contrário, há um problema com sua configuração.

Somente relatório

Os cabeçalhos de política -Report-Only e o Reporting-Endpoints funcionam juntos.

Endpoints configurados em Reporting-Endpoints e especificados no campo report-to do Content-Security-Policy, Cross-Origin-Embedder-Policy e Cross-Origin-Opener-Policy, vai receber denúncias quando essas políticas forem violadas.

Os endpoints configurados em Reporting-Endpoints também podem ser especificados no report-to campo de Content-Security-Policy-Report-Only, Cross-Origin-Embedder-Policy-Report-Only e Cross-Origin-Opener-Policy-Report-Only. Eles também receberão relatórios quando essas políticas forem violadas.

Embora os relatórios sejam enviados nos dois casos, os cabeçalhos -Report-Only não impõem o políticas: nada será corrompido ou realmente será bloqueado, mas você receberá relatórios sobre o que teria falhado ou sido bloqueado.

ReportingObserver

A API JavaScript ReportingObserver pode ajudar você e observar os avisos do lado do cliente.

O ReportingObserver e o cabeçalho Reporting-Endpoints geram relatórios que parecem iguais, mas permitem casos de uso ligeiramente diferentes.

Use ReportingObserver se:

  • Você só quer monitorar descontinuações e/ou intervenções no navegador. O ReportingObserver mostra avisos do lado do cliente, como descontinuações. e intervenções no navegador. No entanto, diferentemente do Reporting-Endpoints, ele não capture qualquer outro tipo de denúncia, como violações da CSP ou COOP/COEP.
  • Você precisa reagir a essas violações em tempo real. ReportingObserver marca é possível anexar um callback a um evento de violação.
  • Você deseja anexar informações adicionais a um relatório para ajudar na depuração, por meio do callback personalizado.

Outra diferença é que o ReportingObserver é configurado somente no lado do cliente: você poderá usá-lo mesmo se não tiver controle sobre os cabeçalhos do lado do servidor defina Reporting-Endpoints.

Leitura adicional

Imagem principal de Nine Koepfer / @enka80 no Unsplash, editada. Agradecemos a Ian Clelland, Eiji Kitamura e Milica Mihajlija pelas avaliações e sugestões neste artigo.