BLOG | NGINX

Protegendo URLs com o Secure Link Module no NGINX e NGINX Plus

NGINX-Parte-de-F5-horiz-preto-tipo-RGB
Kunal Pariani Miniatura
Kunal Pariani
Publicado em 29 de julho de 2016

Tanto o software de código aberto NGINX quanto o NGINX Plus são muito seguros e confiáveis como servidores web, proxies reversos e caches para seu conteúdo. Para proteção adicional contra acesso de clientes não autorizados, você pode usar diretivas do módulo Secure Link para exigir que os clientes incluam uma string com hash específica na URL do ativo que estão solicitando.

Nesta postagem do blog, discutiremos como configurar os dois métodos implementados no módulo Secure Link. Os snippets de configuração de exemplo protegem arquivos HTML e de lista de reprodução de mídia, mas podem ser aplicados a qualquer tipo de URL HTTP. Os métodos se aplicam tanto ao NGINX quanto ao NGINX Plus, mas por uma questão de brevidade, nos referiremos apenas ao NGINX Plus no restante do blog.

Uma visão geral dos métodos no módulo Secure Link

O módulo Secure Link verifica a validade de um recurso solicitado comparando uma string codificada na URL da solicitação HTTP com a string que ele calcula para essa solicitação. Se um link tiver uma vida útil limitada e o tempo tiver expirado, o link será considerado desatualizado. O status dessas verificações é capturado na variável $secure_link e usado para controlar o fluxo de processamento.

Como mencionado, o módulo fornece dois métodos. Apenas um deles pode ser configurado em um determinado contexto http , servidor ou local .

  • O primeiro e mais simples modo é habilitado pela diretiva secure_link_secret . A sequência codificada é um hash MD5 calculado na concatenação de duas sequências de texto: a parte final da URL e uma palavra secreta definida na configuração do NGINX Plus. (Para obter detalhes sobre a primeira sequência de texto, consulte Usando URLs seguros básicos .)

    Para acessar o recurso protegido, o cliente deve incluir o hash logo após o prefixo da URL, que é uma sequência arbitrária sem barras. Neste URL de exemplo, o prefixo é videos e o recurso protegido é o arquivo bunny.m3u8 :

    /videos/80e2dfecb5f54513ad4e2e6217d36fd4/hls/bunny.m3u8

    Um caso de uso para esse método é quando um usuário carrega uma imagem ou documento em um servidor para compartilhamento, mas quer impedir que qualquer pessoa que saiba o nome do arquivo o acesse até que o link oficial seja publicado.

  • O segundo método, mais flexível, é habilitado pelas diretivas secure_link e secure_link_md5 . Aqui, a string codificada é um hash MD5 de variáveis definidas no arquivo de configuração do NGINX Plus. Mais comumente, a variável $remote_addr é incluída para restringir o acesso a um endereço IP de cliente específico, mas você pode usar outros valores, por exemplo $http_user_agent , que captura o cabeçalho User-Agent e, portanto, restringe o acesso a determinados navegadores.

    Opcionalmente, você pode especificar uma data de expiração após a qual o URL não funcionará mais, mesmo que o hash esteja correto.

    O cliente deve anexar o argumento md5 à URL da solicitação para especificar o hash. Se uma data de expiração for incluída na string que é hash, o cliente também deve anexar o argumento expires para especificar a data, como neste URL de exemplo para solicitar o arquivo protegido pricelist.html :

    /files/pricelist.html?md5=AUEnXC7T-Tfv9WLsWbf-mw&expires=1483228740

O módulo Secure Link está incluído em binários NGINX de código aberto pré-criados em nginx.org , nos pacotes NGINX fornecidos por fornecedores de sistemas operacionais e no NGINX Plus . Ele não é incluído por padrão quando você compila o NGINX a partir da fonte; habilite-o incluindo o argumento --with-http_secure_link_module no comando configure .

Usando URLs básicos seguros

A maneira mais básica de proteger URLs é com a diretiva secure_link_secret . No trecho de exemplo a seguir, protegemos um arquivo de lista de reprodução de mídia HTTP Live Streaming (HLS) chamado /bunny.m3u8 . Ele é armazenado no diretório /opt/secure/hls , mas é exposto aos clientes usando uma URL que começa com o prefixo videos .

servidor {
ouvir 80;
nome_do_servidor secure-link-demo;

localização /vídeos {
secure_link_secret enigma;
if ($secure_link = "") { return 403; }

reescrever ^ /secure/$secure_link;
}

localização /secure {
interno;
raiz /opt;
}
}

Com esta configuração, para acessar o arquivo /opt/secure/hls/bunny.m3u8 os clientes devem apresentar a seguinte URL:

/videos/80e2dfecb5f54513ad4e2e6217d36fd4/hls/bunny.m3u8

A string com hash vem logo depois do prefixo, que é uma string arbitrária sem barras (aqui, vídeos ).

O hash é calculado em uma sequência de texto que concatena dois elementos:

  • A parte da URL que segue o hash, aqui hls/bunny.m3u8 .
  • O parâmetro para a diretiva secure_link_secret , aqui enigma .

Se a URL de solicitação do cliente não tiver o hash correto, o NGINX Plus definirá a variável $secure_link como a string vazia. O se o teste falha e o NGINX Plus retorna o 403 Proibido código de status na resposta HTTP.

Caso contrário (o que significa que o hash está correto), a diretiva de reescrita reescreve a URL – em nosso exemplo para /secure/hls/bunny.m3u8 (a variável $secure_link captura a parte da URL que segue o hash). URLs que começam com /secure são manipuladas pelo segundo bloco de localização . A diretiva root nesse bloco define /opt como o diretório raiz para arquivos solicitados e a diretiva internal especifica que o bloco é usado somente para solicitações geradas internamente.

Gerando o Hash no Cliente para uma URL Básica Segura

Para obter o hash MD5 em formato hexadecimal que o cliente deve incluir na URL, executamos o comando openssl md5 com a opção -hex :

# echo -n 'hls/bunny.m3u8enigma' | openssl md5 -hex (stdin)= 80e2dfecb5f54513ad4e2e6217d36fd4

Para uma discussão sobre a geração de hashes programaticamente, consulte Gerando o Hash Programaticamente .

Resposta do servidor para URLs básicos protegidos

Os comandos curl de exemplo a seguir mostram como o servidor responde a diferentes URLs protegidas.

Se a URL incluir o hash MD5 correto, a resposta será200 OK :

# curl -I http://secure-link-demo/videos/80e2dfecb5f54513ad4e2e6217d36fd4/hls/bunny.m3u8 | head -n 1 HTTP/1.1 200 OK

Se o hash MD5 estiver incorreto, a resposta será403 Proibido :

# curl -I http://secure-link-demo/videos/2c5e80de986b6fc80dd33e16cf824123/hls/bunny.m3u8 | head -n 1 HTTP/1.1 403 Proibido

Se o hash para bunny.m3u8 for usado para um arquivo diferente, a resposta também será403 Proibido :

# curl -I http://secure-link-demo/videos/80e2dfecb5f54513ad4e2e6217d36fd4/hs/oven.m3u8 | head -n 1 HTTP/1.1 403 Proibido

Usando URLs seguras que expiram

O método mais flexível para proteger URLs usa as diretivas secure_link e secure_link_md5 . Neste exemplo, nós os usamos para permitir acesso ao arquivo /var/www/files/pricelist.html somente de clientes no endereço IP 192.168.33.14 e somente até 31 de dezembro de 2016.

Nosso servidor virtual escuta na porta 80 e manipula todas as solicitações HTTP seguras no bloco de localização /files , onde a diretiva root define /var/www como o diretório raiz para os arquivos solicitados.

A diretiva secure_link define duas variáveis que capturam argumentos na URL da solicitação: $arg_md5 é definido como o valor do argumento md5 e $arg_expires como o valor do argumento expires .

A diretiva secure_link_md5 define a expressão que é hash para gerar o valor MD5 para a solicitação; durante o processamento da URL, o hash é comparado ao valor de $arg_md5 . A expressão de exemplo aqui inclui o tempo de expiração passado na solicitação (capturado na variável $secure_link_expires ), a URL ( $uri ), o endereço IP do cliente ( $remote_addr ) e a palavra enigma .

servidor {
ouvir 80;
nome_do_servidor secure-link-demo;

localização /arquivos {
raiz /var/www;
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr enigma";

se ($secure_link = "") { retornar 403; }
se ($secure_link = "0") { retornar 410; }
}
}

Com esta configuração, para acessar /var/www/files/pricelist.html , um cliente com endereço IP 192.168.33.14 deve enviar esta URL de solicitação antes de Sat Dec 31 23:59:00 UTC 2016 :

/files/pricelist.html?md5=AUEnXC7T-Tfv9WLsWbf-mw&expires=1483228740

Se o hash na URL enviada pelo cliente (capturado na variável $arg_md5 ) não corresponder ao hash calculado pela diretiva secure_link_md5 , o NGINX Plus definirá a variável $secure_link como a string vazia. O teste if falha e o NGINX Plus retorna o403 Código de status proibido na resposta HTTP.

Se os hashes corresponderem, mas o link tiver expirado, o NGINX Plus define o $link_seguro variável para 0; novamente o se o teste falha, mas desta vez o NGINX Plus retorna o 410 Perdido código de status na resposta HTTP.

Gerando o Hash e o Tempo de Expiração em um Cliente

Agora vamos ver como um cliente calcula os argumentos md5 e expires para incluir na URL.

O primeiro passo é determinar o equivalente de tempo Unix da data de expiração, porque esse valor está incluído na expressão com hash no formato da variável $secure_link_expires . Para obter o horário Unix – o número de segundos desde Epoch (1970-01-01 00:00:00 UTC) – usamos o comando date com a opção -d e o especificador de formato +%s .

Em nosso exemplo, estamos definindo o tempo de expiração para Sat Dec 31 23:59:00 UTC 2016 , então o comando é:

# data -d "2016-12-31 23:59" +%s1483228740

O cliente inclui esse valor como o argumento expires=1483228740 na URL da solicitação.

Agora executamos a string definida pela diretiva secure_link_md5$secure_link_expires$uri$remote_addr enigma – por meio de três comandos:

  • O comando openssl md5 com a opção -binary gera o hash MD5 em formato binário.
  • O comando openssl base64 aplica a codificação Base64 ao valor com hash.
  • Os comandos tr substituem o sinal de mais ( + ) pelo hífen ( - ) e a barra ( / ) pelo sublinhado ( _ ) e excluem o sinal de igual ( = ) do valor codificado.

Para nosso exemplo, o comando completo é:

# echo -n '1483228740/files/pricelist.html192.168.33.14 enigma' | openssl md5 -binário | openssl base64 | tr +/ -_ | tr -d = AUEnXC7T-Tfv9WLsWbf-mw

O cliente inclui esse valor como o argumento md5=AUEnXC7T-Tfv9WLsWbf-mw no URL da solicitação.

Gerando o Hash Programaticamente

Se o seu servidor web NGINX Plus estiver servindo conteúdo dinâmico de um servidor de aplicativos, tanto o NGINX Plus quanto o servidor de aplicativos precisarão usar o mesmo URL seguro. Você pode gerar o hash para o argumento md5 na URL programaticamente. A seguinte função do Node.js gera um hash correspondente ao definido no snippet de configuração do NGINX Plus acima. Ele aceita um tempo de expiração, URL, endereço IP do cliente e palavra secreta como argumentos e retorna o hash MD5 em formato binário codificado em Base64.

var crypto = require("crypto");

function generateSecurePathHash(expires, url, client_ip, secret) {
if (!expires || !url || !client_ip || !secret) {
return undefined;
}

var input = expires + url + client_ip + " " + secret;
var binaryHash = crypto.createHash("md5").update(input).digest();
var base64Value = new Buffer(binaryHash).toString('base64');
return base64Value.replace(/=/g, '').replace(/+/g, '-').replace(///g, '_');
}

Para calcular o hash do nosso exemplo atual, passamos estes argumentos:

generateSecurePathHash(nova Data('31/12/2016 23:59:00').getTime()), '/files/pricelist.html', “192.168.33.14”, "enigma");

Resposta do servidor para URLs protegidas com tempos de expiração

Os comandos curl de exemplo a seguir mostram como o servidor responde a URLs protegidas.

Se um cliente com endereço IP 192.168.33.14 incluir o hash MD5 correto e o tempo de expiração, a resposta será200 OK :

# curl -I --interface "192.168.33.14" 'http://secure-link-demo/files/pricelist.html?md5=AUEnXC7T-Tfv9WLsWbf-mw&expires=1483228740' | head -n 1 HTTP/1.1 200 OK

Se um cliente com um endereço IP diferente enviar o mesmo URL, a resposta será403 Proibido :

# curl -I --interface "192.168.33.33" 'http://secure-link-demo/files/pricelist.html?md5=AUEnXC7T-Tfv9WLsWbf-mw&expires=1483228740' | head -n 1 HTTP/1.1 403 Proibido

Se o valor hash do argumento md5 estiver incorreto, a resposta será403 Proibido :

# curl -I --interface "192.168.33.14" 'http://secure-link-demo/files/pricelist.html?md5=qeUNjiY2FTIVMaXUsxG-7w&expires=1483228740' | head -n 1 HTTP/1.1 403 Proibido

Se a URL expirou (a data representada pelo argumento expires está no passado), a resposta é410 Perdido :

# curl -I --interface "192.168.33.14" 'http://secure-link-demo/files/pricelist.html?md5=Z2rNva2InyVcRTlhqAkT4Q&expires=1467417540' | head -n 1 HTTP/1.1 410 Desaparecido

Exemplo – Protegendo arquivos de segmento com uma data de expiração

Aqui está outro exemplo de uma URL segura com data de expiração, usada para proteger tanto a lista de reprodução de um ativo de mídia quanto os arquivos de segmento.

Uma diferença do exemplo anterior é que adicionamos um bloco de configuração de mapa aqui para remover a extensão da lista de reprodução (arquivo .m3u8 ) e dos segmentos HLS (arquivos .ts ) à medida que capturamos o nome do arquivo na variável $file_name , que é passada para a diretiva secure_link_md5 . Isso serve para proteger solicitações para os segmentos .ts individuais, bem como para a lista de reprodução.

Outra diferença do primeiro exemplo é que incluímos a variável $http_user_agent (que captura o cabeçalho User-Agent ) na diretiva secure_link_md5 , para restringir o acesso a clientes em navegadores específicos (por exemplo, para que a URL funcione no Safari, mas não no Chrome ou Firefox).

mapa $uri $file_name {
padrão nenhum;
"~*/s/(?<nome>.*).m3u8" $name;
"~*/s/(?<nome>.*).ts" $name;
}

servidor {
ouvir 80;
nome_servidor secure-link-demo;

localização /s {
raiz /opt;
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$file_name$http_user_agent enigma";

se ($secure_link = "") { retornar 403; }
se ($secure_link = "0") { retornar 410; }
}
}

Resumo

O módulo Secure Link no NGINX permite que você proteja arquivos contra acesso não autorizado adicionando dados codificados, como o hash de uma parte específica do URL. Adicionar um tempo de expiração também limita a validade dos links, para maior segurança.

Para experimentar o NGINX Plus, comece hoje mesmo seu teste gratuito de 30 dias ou entre em contato conosco para discutir seus casos de uso.


"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."