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 o seu conteúdo. Para obter proteção adicional contra o acesso de clientes não autorizados, você pode usar as diretivas do módulo Secure Link para exigir que os clientes incluam uma string de hash específica na URL do recurso solicitado.
Nesta publicação do blog, discutiremos como configurar os dois métodos implementados no módulo Secure Link. Os exemplos de trechos de configuração protegem arquivos HTML e de playlists 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, para simplificar, mencionaremos apenas o NGINX Plus no restante da publicação.
Visão geral dos métodos do 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 prazo tiver expirado, o link será considerado desatualizado. O status dessas verificações é registrado na variável $secure_link e usado para controlar o fluxo de processamento.
Como mencionado, o módulo oferece dois métodos. Apenas um deles pode ser configurado em um determinado contexto http, server ou location.
- O primeiro modo, que também é o mais simples, é habilitado pela diretiva
secure_link_secret. A string codificada é um hash MD5 calculado a partir da concatenação de duas strings de texto: a parte final da URL e uma palavra secreta definida na configuração do NGINX Plus. (Para obter detalhes sobre a primeira string de texto, consulte Uso de URLs protegidas básicas.) Para acessar o recurso protegido, o cliente deve incluir o hash logo após o prefixo da URL, que é uma string arbitrária sem barras. Nesta URL de exemplo, o prefixo é videos e o recurso protegido é o arquivo bunny.m3u8: /videos/80e2dfecb5f54513ad4e2e6217d36fd4/hls/bunny.m3u8 Um caso de uso desse método ocorre quando um usuário envia uma imagem ou um documento a um servidor para compartilhamento, mas deseja impedir que qualquer pessoa que conheça o nome do arquivo o acesse antes da publicação do link oficial. - O segundo método, mais flexível, é habilitado pelas diretivas
secure_linkesecure_link_md5. Nesse caso, a string codificada é um hash MD5 de variáveis definidas no arquivo de configuração do NGINX Plus. Normalmente, 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, como$http_user_agent, que captura o cabeçalhoUser-Agente, assim, restringe o acesso a determinados navegadores. Opcionalmente, você pode especificar uma data de expiração, após a qual a URL deixa de funcionar, mesmo que o hash esteja correto. O cliente deve acrescentar o argumento md5 à URL da solicitação para especificar o hash. Se uma data de expiração estiver incluída na string usada para gerar o hash, o cliente também deverá acrescentar o argumento expires para especificar a data, como nesta 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 nos binários de código aberto pré-compilados do NGINX fornecidos pelo nginx.org, nos pacotes do NGINX disponibilizados por fornecedores de sistemas operacionais e no NGINX Plus. Ele não é incluído por padrão quando você compila o NGINX a partir do código-fonte. Para habilitá-lo, inclua o argumento --with-http_secure_link_module no comando configure.
Como usar URLs protegidos básicos
A maneira mais básica de proteger URLs é usar a diretiva secure_link_secret. No trecho de exemplo a seguir, protegemos um arquivo de playlist de mídia de HTTP Live Streaming (HLS) chamado /bunny.m3u8. Ele fica armazenado no diretório /opt/secure/hls, mas é disponibilizado aos clientes por meio de uma URL que começa com o prefixo videos.
server {
listen 80;
server_name secure-link-demo;
location /videos {
secure_link_secret enigma;
if ($secure_link = "") { return 403; }
rewrite ^ /secure/$secure_link;
}
location /secure {
internal;
root /opt;
}
}Com essa configuração, para acessar o arquivo /opt/secure/hls/bunny.m3u8 os clientes devem usar a seguinte URL:
/videos/80e2dfecb5f54513ad4e2e6217d36fd4/hls/bunny.m3u8
A string com hash vem logo após o prefixo, que é uma string arbitrária sem nenhuma barra (neste caso, videos).
O hash é calculado sobre uma string de texto que concatena dois elementos:
- A parte da URL que vem após o hash, neste caso, hls/bunny.m3u8.
- O parâmetro da diretiva
secure_link_secret, neste caso,enigma.
Se a URL da solicitação do cliente não tiver o hash correto, o NGINX Plus definirá a variável $secure_link como uma string vazia. O teste if falhará, e o NGINX Plus retornará o código de status 403 Forbidden na resposta HTTP.
Caso contrário (ou seja, se o hash estiver correto), a diretiva rewrite reescreve a URL — em nosso exemplo, para /secure/hls/bunny.m3u8 (a variável $secure_link captura a parte da URL que vem após o hash). As URLs que começam com /secure são processadas pelo segundo bloco location. A diretiva root nesse bloco define /opt como o diretório raiz dos arquivos solicitados, e a diretiva internal especifica que o bloco é usado apenas para solicitações geradas internamente.
Gerando o hash no cliente para uma URL básica protegida
Para obter o hash MD5 no 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)= 80e2dfecb5f54513ad4e2e6217d36fd4Para uma discussão sobre como gerar hashes programaticamente, consulte Gerando o hash programaticamente.
Resposta do servidor a URLs com segurança básica
Os seguintes exemplos de comandos curl 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 1HTTP/1.1 200 OKSe o hash MD5 estiver incorreto, a resposta será 403 Proibido:
# curl -I http://secure-link-demo/videos/2c5e80de986b6fc80dd33e16cf824123/hls/bunny.m3u8 | head -n 1HTTP/1.1 403 ForbiddenSe o hash de bunny.m3u8 for usado para outro arquivo, a resposta também será 403 Proibido:
# curl -I http://secure-link-demo/videos/80e2dfecb5f54513ad4e2e6217d36fd4/hs/oven.m3u8 | head -n 1HTTP/1.1 403 ForbiddenComo usar 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 as usamos para permitir o acesso ao arquivo /var/www/files/pricelist.html somente por clientes com o endereço IP 192.168.33.14 e apenas até 31 de dezembro de 2016.
Nosso servidor virtual escuta na porta 80 e processa todas as solicitações HTTP protegidas no bloco location /files, em que a diretiva root define /var/www como o diretório raiz dos arquivos solicitados.
A diretiva secure_link define duas variáveis que capturam argumentos na URL da solicitação: $arg_md5 recebe o valor do argumento md5, e $arg_expires recebe o valor do argumento expires.
A diretiva secure_link_md5 define a expressão cujo hash é calculado para gerar o valor MD5 da solicitação; durante o processamento da URL, o hash é comparado ao valor de $arg_md5. A expressão de exemplo inclui o tempo de expiração informado 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.
server {
listen 80;
server_name secure-link-demo;
location /files {
root /var/www;
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr enigma";
if ($secure_link = "") { return 403; }
if ($secure_link = "0") { return 410; }
}
}Com essa configuração, para acessar /var/www/files/pricelist.html, um cliente com o endereço IP 192.168.33.14 deve enviar esta URL de solicitação antes de sábado, 31 de dezembro de 2016, às 23:59:00 UTC:
/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 com base na diretiva secure_link_md5, o NGINX Plus definirá a variável $secure_link como uma string vazia. O teste if falhará, e o NGINX Plus retornará o código de status 403 Forbidden na resposta HTTP.
Se os hashes corresponderem, mas o link tiver expirado, o NGINX Plus definirá a variável $secure_link como 0; novamente, o teste if falhará, mas, desta vez, o NGINX Plus retornará o código de status 410 Gone na resposta HTTP.
Como gerar o hash e o tempo de expiração em um cliente
Agora, vamos ver como um cliente calcula os argumentos md5 e expires a serem incluídos na URL.
A primeira etapa é determinar o horário Unix equivalente à data de expiração, pois esse valor é incluído na expressão com hash na forma da variável $secure_link_expires. Para obter o horário Unix – o número de segundos desde a Época Unix (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 horário de expiração como sábado, 31 de dezembro de 2016, às 23:59:00 UTC. Portanto, o comando é:
# date -d "2016-12-31 23:59" +%s1483228740O cliente inclui esse valor como o argumento expires=1483228740 na URL da solicitação.
Agora, processamos a string definida pela diretiva secure_link_md5 — $secure_link_expires$uri$remote_addr enigma — usando três comandos:
- O comando
opensslmd5com a opção-binarygera o hash MD5 em formato binário. - O comando
opensslbase64aplica a codificação Base64 ao valor de hash. - Os comandos
trsubstituem o sinal de mais (+) pelo hífen (-) e a barra (/) pelo sublinhado (_), e excluem o sinal de igual (=) do valor codificado.
Para o nosso exemplo, o comando completo é:
# echo -n '1483228740/files/pricelist.html192.168.33.14 enigma' | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =AUEnXC7T-Tfv9WLsWbf-mwO cliente inclui esse valor como o argumento md5=AUEnXC7T-Tfv9WLsWbf-mw na URL da solicitação.
Gerando o hash programaticamente
Se o seu servidor web NGINX Plus estiver fornecendo conteúdo dinâmico de um servidor de aplicações, tanto o NGINX Plus quanto o servidor de aplicações precisarão usar a mesma URL protegida. Você pode gerar programaticamente o hash para o argumento md5 na URL. A função Node.js a seguir gera um hash correspondente ao definido no trecho de configuração do NGINX Plus acima. Ela recebe como argumentos um tempo de expiração, uma URL, o endereço IP do cliente e uma palavra secreta, 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(new Date('12/31/2016 23:59:00').getTime()), '/files/pricelist.html', “192.168.33.14”, "enigma");Resposta do servidor a URLs protegidas com prazos de expiração
Os seguintes exemplos de comandos curl mostram como o servidor responde a URLs protegidas.
Se um cliente com o endereço IP 192.168.33.14 incluir o hash MD5 e o tempo de expiração corretos, 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 OKSe um cliente com um endereço IP diferente enviar a mesma 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 1HTTP/1.1 403 ForbiddenSe o valor de hash do argumento md5 estiver incorreto, a resposta será 403 Forbidden:
# curl -I --interface "192.168.33.14" 'http://secure-link-demo/files/pricelist.html?md5=qeUNjiY2FTIVMaXUsxG-7w&expires=1483228740' | head -n 1HTTP/1.1 403 ForbiddenSe a URL tiver expirado (a data representada pelo argumento expires estiver no passado), a resposta será 410 Gone:
# curl -I --interface "192.168.33.14" 'http://secure-link-demo/files/pricelist.html?md5=Z2rNva2InyVcRTlhqAkT4Q&expires=1467417540' | head -n 1HTTP/1.1 410 GoneExemplo – Protegendo arquivos de segmento com uma data de expiração
Veja outro exemplo de uma URL protegida com data de expiração, usada para proteger tanto a playlist de um ativo de mídia quanto os arquivos de segmento.
Uma diferença em relação ao exemplo anterior é que adicionamos aqui um bloco de configuração map para remover a extensão da playlist (arquivo .m3u8) e dos segmentos HLS (arquivos .ts) ao capturarmos o nome do arquivo na variável $file_name, que é passada para a diretiva secure_link_md5. Isso permite proteger tanto as solicitações dos segmentos individuais .ts quanto as da playlist.
Outra diferença em relação ao 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 que usam navegadores específicos (por exemplo, para que a URL funcione no Safari, mas não no Chrome ou Firefox).
map $uri $file_name {
default none;
"~*/s/(?<name>.*).m3u8" $name;
"~*/s/(?<name>.*).ts" $name;
}
server {
listen 80;
server_name secure-link-demo;
location /s {
root /opt;
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$file_name$http_user_agent enigma";
if ($secure_link = "") { return 403; }
if ($secure_link = "0") { return 410; }
}
}Resumo
O módulo Secure Link do NGINX permite proteger arquivos contra acesso não autorizado adicionando dados codificados, como o hash de uma parte específica da URL. Adicionar um prazo de expiração também limita o período de validade dos links, proporcionando ainda mais segurança.
Para experimentar o NGINX Plus, comece sua avaliação gratuita de 30 dias hoje mesmo ou entre em contato conosco para conversar sobre seus casos de uso.
Sobre o autor

Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
Descubra como o F5 NGINX One aprimora o gerenciamento e a observabilidade de ambientes híbridos e multicloud ao oferecer um console web centralizado baseado em SaaS, com insights em tempo real, conformidade automatizada e gerenciamento simplificado de configurações.

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa
Descubra como transformar uma definição de esquema OpenAPI em uma configuração NGINX totalmente funcional, operando como um API Gateway com a segurança de um Web Application Firewall e um Portal do Desenvolvedor, usando uma abordagem declarativa de API.

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor
Picos de tráfego e sobrecarga de servidores são preocupações sérias para varejistas online em qualquer época, mas especialmente durante a “Cyber Season” de compras de fim de ano. Detalhamos como você pode aproveitar as soluções de entrega de aplicações de nível de produção da NGINX para se preparar para aumentos repentinos de tráfego.

Distribuição segura de chaves privadas SSL com o NGINX
Descrevemos três maneiras progressivamente mais seguras de proteger chaves privadas SSL ao configurar o NGINX para processar tráfego HTTPS: permitir acesso de leitura apenas ao usuário root, criptografar as chaves com senhas armazenadas separadamente e distribuir as senhas a partir de um repositório central.
Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX
O módulo JavaScript do NGINX lançado com o NGINX Plus R15 oferece suporte a subsolicitações, possibilitando uma classe totalmente nova de casos de uso para o NGINX Plus. Este exemplo completo também usa a API do NGINX Plus e os módulos de chave-valor para converter solicitações de API dos clientes em solicitações separadas para as APIs dos microsserviços que compõem um aplicativo.
Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus
Confira dicas para evitar o esgotamento de portas efêmeras com ajustes no kernel do Linux, conexões keepalive e conexões vinculadas dinamicamente a endereços IP locais.