A geração de chaves não precisa de um ficheiro temporário, e dispensá-lo corrige menos do que a maioria dos guias sugere. O que remove é um ciclo de vida de ficheiro e a corrida de limpeza à sua volta. O que deixa intacto é cada oportunidade de divulgar o segredo depois. O problema de correção nestes scripts costuma estar noutro lugar, porque o diagnóstico e o material da chave viajam em fluxos diferentes.

O ficheiro nunca foi a exposição que pensavas

Um ficheiro temporário é um risco concreto quando é legível por todos, quando sobrevive a uma falha ou quando um passo de limpeza é saltado após uma exceção. São reais, e vale a pena corrigi-los. Também são locais, visíveis e fáceis de testar. Nomeia cada um no teu próprio script antes de concluir que a alteração era cosmética.

Os riscos mais silenciosos estão a jusante: a chave impressa durante a depuração, a exceção que serializa todo o objeto de resultado, o gestor de falhas que escreve um dump de memória, o agregador de registos que recebe o caminho do ficheiro e depois o conteúdo. Evitar um ficheiro temporário não faz nada por nenhum deles, e acreditar no contrário é como uma reescrita bem-intencionada deixa o segredo mais exposto do que antes. Trata a alteração como uma melhoria numa sequência, não como a solução.

O que o cliente escreve quando não é dado um nome de ficheiro

As versões atuais do cliente enviam a chave gerada para a saída padrão quando o comando de chave é dado sem um caminho de saída. É esse comportamento documentado que torna a abordagem em memória viável, e remove a necessidade do truque mais antigo de apontar a ferramenta para um caminho de dispositivo.

import subprocess
proc = subprocess.run(
    ["openvpn", "--genkey", "tls-crypt"],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    check=True,
)
key_material = proc.stdout

Passa o comando como uma lista. Aqui não é precisa qualquer expansão de shell, e ativar uma shell tornaria o limite dos argumentos dependente de aspas que não controlas. Usa o executável previsto por caminho absoluto se o script puder correr com um caminho de pesquisa diferente do que esperas.

A verificação do código de retorno impede que o caminho de sucesso continue após uma invocação falhada. Sem ela, uma cadeia de bytes vazia pode fluir adiante e só falhar muito mais tarde, no ponto em que a chave é usada e a mensagem de erro já não menciona sequer a geração.

Dois fluxos, dois significados

A saída padrão transporta a chave. O erro padrão transporta tudo o que a ferramenta te quer dizer, incluindo avisos sobre a configuração, a versão e o tipo de chave. Redirecionar ambos para um só buffer é o erro que produz uma chave de aspeto plausível com uma frase cosida no meio.

Mantém também separado o caminho de falha. Quando a chamada falha, comunica o comando que falhou e o estado de saída, e depois decide deliberadamente se alguma parte do erro padrão é segura de mostrar. Despejar todo o objeto de resultado num registo é o atalho habitual, e pode levar a chave consigo.

Uma leitura segura para binários importa se depois calculares o hash do material ou o comparares. O modo de texto com uma codificação inesperada pode alterar bytes que nunca se destinavam a ser texto, e uma chave alterada é indistinguível de uma errada até um aperto de mão falhar.

Se mesmo assim o persistires, fá-lo deliberadamente

A maioria das implementações acaba por precisar da chave em disco, na gateway ou num cofre de segredos. Escreve-a uma vez, com permissões restritivas desde a criação em vez de um chmod posterior, e torna a escrita atómica para que um ficheiro parcial nunca seja o que um serviço lê no arranque. Uma renomeação a partir de um caminho no mesmo diretório costuma bastar para isso. Remove o caminho de preparação no ramo de falha, porque a escrita falhada é exatamente a cópia que as pessoas esquecem.

Define o modo do ficheiro quando é criado, e mantém também o modo do diretório restritivo. Evita qualquer caminho sob um local que uma tarefa de cópia de segurança varre sem pensar, porque um segredo numa cópia noturna é um segredo em cada cópia dessa cópia. O mesmo raciocínio aplica-se a um volume partilhado que outros serviços podem ler.

Uma chave partilhada não é uma identidade

Gerar este material não emite um certificado, não inscreve um cliente e não estabelece quem se liga. A chave partilhada protege um canal entre partes que já confiam umas nas outras; a identidade continua a vir do certificado e de qualquer autenticação que esteja à sua frente.

Os tipos de chave também não são intermutáveis. Uma chave simétrica para uma funcionalidade não é um substituto direto para outra, e as versões mais recentes acrescentam as suas próprias variantes com ficheiros diferentes em cada extremidade. Gera o tipo que a tua configuração nomeia de facto, e depois usa-o no papel que esse tipo define.

Testa o resultado com uma ligação real em vez de inspecionar o tamanho do ficheiro. Se o aperto de mão se completa e os registos não mostram avisos de chave, o caminho de geração está correto; se a chave tivesse sido truncada por um fluxo fundido, a ligação falharia de um modo que aponta para o par em vez do script.

Artigos relacionados