Resumo

  • A árvore de políticas da RFC 5280 duplica estados quando vários mapeamentos chegam à mesma política; os descendentes também são copiados e o pior caso cresce exponencialmente.
  • A RFC 9618 usa um grafo acíclico direcionado com nós compartilhados, preservando a validade do caminho e o conjunto final de políticas enquanto limita o estado ao volume de políticas e mapeamentos de entrada.
  • Uma migração confiável precisa comprovar duas coisas ao mesmo tempo: paridade das decisões e um orçamento medido de CPU, memória e fila sob entradas hostis.

Durante quase todo o dia, a latência do serviço de identidade permaneceu normal. Certificados comuns eram validados em poucos milissegundos. O incidente começou quando cadeias curtas, com mapeamentos de políticas cuidadosamente combinados, chegaram em paralelo. Elas não obtiveram acesso. Mesmo assim, consumiram os trabalhadores que deveriam decidir sobre todos os demais clientes.

O problema não era a quantidade de bytes recebidos, mas a quantidade de estado que o software se julgava obrigado a fabricar. A RFC 9618 retira essa alavanca sem alterar a resposta de política que uma implementação correta deve produzir.

A decisão de política dentro do caminho

Certificados X.509 podem declarar OIDs de política e qualificadores. Uma CA pode restringir as políticas aceitas ao longo do caminho e mapear um OID do domínio emissor para outro no domínio subordinado. A aplicação informa quais políticas aceita inicialmente.

A RFC 5280 processa esses elementos durante a validação de um caminho já formado. Isso é diferente de procurar certificados para montar a cadeia. A RFC 4158 trata da construção do caminho; a RFC 9618 começa depois, no cálculo das políticas que sobrevivem às restrições e aos mapeamentos.

O algoritmo original mantinha valid_policy_tree. Uma rota de mapeamento correspondia a um ramo. Se duas políticas emissoras chegassem à mesma política de sujeito, a árvore criava duas cópias do mesmo estado. Cada cópia repetia seus filhos no próximo nível.

Com dois OIDs por certificado intermediário e todos os mapeamentos entre eles, o número de caminhos dobra a cada profundidade. A cadeia cresce pouco; o objeto interno cresce exponencialmente. Um atacante pode então gastar poucos recursos para impor muito trabalho antes de qualquer decisão de confiança.

Os exemplos citados pela RFC chegaram a produtos. O registro do OpenSSL associa a CVE-2023-0464 ao uso exponencial de recursos na verificação de políticas e documenta um limite de nós. O aviso da Apple registra que um certificado malicioso podia causar negação de serviço na CVE-2023-23524. O histórico de versões do OpenSSL mantém a mitigação visível.

O grafo conserva alcance sem enumerar rotas

Na RFC 9618, valid_policy_graph contém no máximo um nó para cada OID em uma determinada profundidade. Quando várias políticas anteriores chegam ao mesmo nó, ele tem vários pais. Os descendentes aparecem uma vez.

Nada essencial é removido. A árvore antiga equivale à enumeração de todos os caminhos entre a raiz e as folhas do grafo. Para descobrir quais políticas finais são alcançáveis, não é necessário materializar cada rota equivalente. Por isso a nova representação mantém tanto o resultado de validade quanto o conjunto final de políticas.

O ganho é uma fronteira operacional: o tamanho do grafo é limitado linearmente pelo total de políticas e mapeamentos codificados. Entradas grandes continuam custando. O que desaparece é a capacidade de uma entrada compacta ordenar a criação de uma estrutura exponencialmente maior.

Esse desenho preserva uma especificação comum mínima. A norma fixa invariantes de anyPolicy, contadores, mapeamentos, poda e saída. Cada fornecedor pode escolher estruturas e otimizações locais, desde que a decisão continue igual e o custo permaneça controlável.

Uma saída antiga pode recriar a expansão

A RFC 5280 apresentava a árvore completa como saída. Um consumidor legado pode exigir valid_policy_tree mesmo quando o validador opera internamente sobre o grafo. Para atendê-lo, o software expande cada caminho e recria a estrutura insegura.

A RFC 9618 desaconselha e deprecia essa saída. Em seu lugar, recomenda os conjuntos de políticas limitados pela autoridade e pelo usuário. Em geral, a aplicação precisa saber quais políticas venceram, não receber todas as histórias equivalentes que levam a elas.

A reconstrução sob demanda ainda é possível, mas mantém a complexidade exponencial. “Sob demanda” apenas transfere o momento do custo. Se houver uma necessidade real, essa função deve ser separada do caminho compartilhado, receber cota, limite de taxa e dono explícito.

Compatibilidade, portanto, é uma decisão de poder. Uma interface diagnóstica sem proprietário não deve impor risco de disponibilidade a cada autenticação. O requisito precisa nomear o consumidor, a saída indispensável e o orçamento correspondente.

Limites práticos criam novas decisões

Enquanto o grafo não chega a todos os sistemas, é possível limitar profundidade ou quantidade de nós. Porém, profundidade baixa recusa cadeias legítimas; profundidade alta ainda admite ramificação. A RFC observa que aumentar as políticas por certificado pode manter crescimento próximo de O(N^(profundidade/2)).

Um teto de nós só protege se interromper o processamento antes do esgotamento e de modo previsível. A evidência deve informar nó de parada, memória, CPU, erro, repetição pelo cliente e atraso imposto a terceiros. Um número no arquivo de configuração não responde a essas perguntas.

Desabilitar políticas tampouco permite ignorar extensões críticas. Se a implementação não as reconhece, deve rejeitar o certificado. Essa regra preserva o significado assinado do bit crítico; não é um atalho silencioso para reduzir custo.

Dois comprovantes, um mesmo conjunto de testes

O comprovante semântico cobre políticas simples, múltiplos mapeamentos, anyPolicy, exigência explícita, inibição de mapeamento, inibição de anyPolicy, poda e conjunto final vazio. Para cada vetor, compara o sucesso ou fracasso e as políticas finais. Rejeitar todo caso complexo não é uma implementação equivalente.

O comprovante de capacidade varia separadamente profundidade, OIDs por certificado e densidade de mapeamento. Registra bytes, nós e arestas, pico de memória, alocações, CPU, tempo decorrido, causa de término e impacto sob concorrência.

Os comprovantes devem se cruzar. Cada vetor funcional precisa de traço de recursos; cada vetor hostil precisa de veredito exato. Assim não se confunde velocidade obtida pela perda de semântica com compatibilidade obtida mantendo a superfície de negação de serviço.

Também é preciso provar o código em execução: biblioteca carregada, opções de compilação, configuração ativa e chamadas à API de árvore. A publicação de um RFC torna a solução disponível. A instalação de um pacote também não prova que o processo produtivo o carregou.

A RFC 9618 não elimina todos os custos X.509. Construção de caminho, assinaturas, revogação, parsing e autorização permanecem separados. Ela remove uma amplificação bem definida. Essa precisão permite cobrar uma prestação de contas igualmente precisa.

Fontes