Tenho um certificado de cliente que tem caracteres não ingleses no assunto. Quando uso o atributo $ssl_client_s_dn do ngx_http_ssl_module, o valor é escapado duas vezes.
valor real = "L=D'UNCONGÉ"
o \xC3 é codificado novamente para obter \x5CC3, preciso evitar isso
valor real = "L=D'UNCONG\x5CC3\x5C89"
valor esperado = "L=D'UNCONG\xC3\x89"
Alguém pode me aconselhar sobre o que fiz de errado? Como posso evitar isso?
Há muitas peças neste quebra-cabeça.
Primeiro, deixe-me comentar que o "Valor Esperado" que você mencionou inclui constantes numéricas Hexadecimais (formato: \xHH). Isso só seria 'esperado' onde esta é uma codificação padrão.
O próprio Nginx usa a maioria das literais de string e constantes numéricas, como representações hexadecimais, octais ou Unicode, sem problemas nas configurações; e o mesmo acontece quando ele lê cabeçalhos. Por isso, fiquei inicialmente confuso com o formato incomum desse "escape duplo".
Verifiquei o RFC sobre a codificação Cert de Distinguished Name (DN). Há uma diferença sutil em como a Cert String é formatada em comparação a algo como strings de URL duplamente codificadas, mas a diferença é importante.
Observe o RFC e, em particular, observe a tabela de exemplos no final da seção 5 e veja os valores "entre aspas" vs. UTF-8: https://datatracker.ietf.org/doc/html/rfc2253#section-5
O RFC afirma que o Cert UTF-8 "É" (0xC389) quando Quoted é \C3\89. Com base no Valor Real que você relatou, isso confirma => que o Nginx está lendo um valor Quoted do próprio Cert.
Então o Nginx escapa o "\" em \C3\89 e isso explica por que ele se torna \x5CC3\x5C89 como em seu "Valor Real".
No entanto, essa representação incomum é (provavelmente) limitada aos Logs do próprio Nginx - dependendo de suas configurações, provavelmente não é o valor real de $ssl_client_s_dn.
Por padrão, o Nginx escapa strings em Logs, mas não escapa valores em variáveis. Em relação ao formato de log, você pode evitar o escape adicionando uma opção à diretiva log_format:
Anexe a variável $ssl_client_s_dn ao local apropriado no valor log_format para que ela possa ser verificada. Estude o Nginx Log Module se necessário para ver como fazer isso https://nginx.org/en/docs/http/ngx_http_log_module.html
Se você é novo no Nginx, isso pode ser muita coisa para destrinchar... então deixe-me rever os pontos principais e, após cuidadosa consideração, espero que você consiga adaptar a configuração corretamente para atender ao caso de uso exato:
Este problema é específico da implementação, o que significa que não tem uma solução genérica. Não há uma questão fundamental simples aqui - é um caso complexo de vários fatores dependentes.
Evitar logs de escape ajudará você a entender melhor o valor que recebeu do certificado; certifique-se de registrar pelo menos $ssl_client_s_dn até ter certeza de que tudo está funcionando conforme o esperado.
Se algum Proxy/Servidor/Serviço diferente de um Usuário Final enviar o Certificado do Cliente para o Nginx, tenha em mente que esta é a principal Dependência: a maneira como eles constroem a codificação do Certificado determina como o DN do Assunto do Certificado será lido pelo Nginx.
Observe que este ponto sozinho poderia resolver casos em que o Upstream está deturpando o valor do Header - Se você estiver enviando o valor de $ssl_client_s_dn para o App/Uptream em um Header, certifique-se de NÃO colocá-lo entre aspas, porque isso não precisa ser escapado para o Nginx e provavelmente não precisa ser escapado pelo Upstream. Se isso já tiver sido feito, altere para que você o coloque entre aspas. Embora eu prefira o método sem aspas, esta simples alteração pode "consertar" o problema para o App/Upstream:
Estou fazendo muitas suposições. Você pode não encaminhar o Cert Subject DN para outro recurso, você pode apenas avaliá-lo no Nginx, você pode já ter atualizado log_format com escape=none , há tantas situações possíveis...
Em todos esses casos, lembre-se de que cada componente pode escapar do valor manipulado ou escapá-lo somente em Logs. Saber como cada componente é configurado e como eles manipulam String Literals, caracteres multibyte UTF-8 e/ou Constantes Numéricas é essencial!
Considere pelo menos estes fatores:
a => Remetente do Certificado do Cliente, como os dados iniciais do Certificado são formatados
b => O Nginx normalmente usa o DN exato do assunto do certificado que recebe, mas escapa valores em seus próprios logs por padrão, a menos que você substitua esse comportamento.
c => App/Upstream (se você enviar $ssl_client_s_dn no Cabeçalho) pode escapar o valor do Cabeçalho, ou pode deixá-lo intocado e apenas escapá-lo em Logs como o Nginx...
Esses são conceitos fundamentais - que tal fornecer uma solução para a questão?
Há uma maneira de modificar o valor do Cert DN definido em $ssl_client_s_dn ao enviar para o Upstream, se isso for desejado.
Registre o valor de $client_dn_fix e $ssl_client_s_dn.
Ao revisar os Logs, você poderá ver o valor real do Certs Subject DN expresso em $ssl_client_s_dn e $client_dn_fix, validando então a visão do Nginx sobre ele.
Depois de revisar os logs para entender melhor as operações internas, você pode substituir valores como "hex escape" ou "original" por uma string como: "L=D'UNCONGÉ" (literais UTF-8) ou "L=D'UNCONG\xC3\x89" para que o Upstream receba o "Valor Esperado".
Há muitas maneiras possíveis de implementar os conceitos aqui, depende apenas do que você realmente precisa.
Edição: O OP confirmou que "L=D'UNCONG\C3\89" aparece nos Logs ao verificar o valor de $ssl_client_s_dn , então o mapa a seguir finalmente é útil, observe que isso reduz Chave/Valores para 2 entradas: