Controle Condicional de Visualizações no Django com Last-Modified e ETag, além de Assinaturas Criptográficas Seguras

O controle condicional de visualizações é uma técnica essencial para otimizar o desempenho de aplicações web, reduzindo a carga no servidor e economizando largura de banda. Em vez de reenviar recursos inteiros em cada requisição, o Django permite que o cliente valide se sua cópia em cache ainda é válida — usando mecanismos definidos pela especificação HTTP: Last-Modified e ETag. Além disso, para garantir integridade e autenticidade de dados transitando por canais inseguros, o framework oferece um sistema robusto de assinatura criptográfica.

Last-Modified: Validação Baseada em Timestamp

Quando um cliente acessa um recurso pela primeira vez, o servidor pode incluir o cabeçalho Last-Modified na resposta, indicando a data e hora da última alteração do recurso (no formato RFC 1123):

Last-Modified: Wed, 04 Oct 2023 15:22:17 GMT

Nas requisições subsequentes, o navegador envia o cabeçalho If-Modified-Since com esse valor. Se o recurso não foi modificado desde então, o servidor responde com status 304 Not Modified e corpo vazio — evitando transmissão redundante.

ETag: Identificação Única por Conteúdo

O ETag é um identificador opcioanl, gerado pelo servidor, que representa uma versão específica do recurso — geralmente calculado a partir do conteúdo (ex: hash SHA-256) ou de metadados relevantes. Ele resolve limitações do Last-Modified, como:

  • Arquivos cuja data de modificação muda sem alteração real no conteúdo;
  • Atualizações mais frequentes que um segundo (MTIME não distingue subsegundos);
  • Sistemas onde timestamps não são confiáveis ou indisponíveis.

O fluxo típico com If-None-Match:

  1. Requisição inicial → servidor retorna ETag: "abc123" e status 200;
  2. Requisição subsequente → cliente envia If-None-Match: "abc123";
  3. O servidor compara e, se coincidir, responde com 304.

Observação: Last-Modified e ETag podem coexistir — ambos são verificados independentemente, e a resposta 304 só ocorre se todas as condições condicionais forem satisfeitas.

Implementação no Django

O Django fornece o decorador @condition, que aceita funções para gerar ETag e/ou Last-Modified. Exemplo prático:

from django.views.decorators.http import condition
from django.http import HttpResponse
from myapp.models import Article

def get_article_last_modified(request, article_id):
    try:
        return Article.objects.get(id=article_id).updated_at
    except Article.DoesNotExist:
        return None

def compute_article_etag(request, article_id):
    try:
        article = Article.objects.get(id=article_id)
        return f"{article.id}-{article.updated_at.timestamp()}"
    except Article.DoesNotExist:
        return None

@condition(
    last_modified_func=get_article_last_modified,
    etag_func=compute_article_etag
)
def article_detail(request, article_id):
    article = Article.objects.get(id=article_id)
    return HttpResponse(article.content)

Para casos mais simples, use os decoradores especializados:

from django.views.decorators.http import last_modified, etag

@last_modified(get_article_last_modified)
def article_list(request):
    # ...

@etag(compute_article_etag)
def article_preview(request, slug):
    # ...

Assinaturas Criptográficas no Django

Quando dados sensíveis precisam ser transmitidos por canais não confiáveis (ex: URLs de redefinição de senha, campos ocultos em formulários), o Django oferece APIs de assinatura que garantem integridade e, opcionalmente, temopralidade.

Assinatura Básica

O módulo django.core.signing fornece a classe Signer, que usa a chave secreta do projeto (SECRET_KEY) para gerar e verificar assinaturas:

from django.core.signing import Signer

signer = Signer()
signed_value = signer.sign("user_42")
# Resulta em: "user_42:ZQVd9XJqGkRwYtLcKbHvNpMxY"

original = signer.unsign(signed_value)  # Retorna "user_42"

Uso de Salt (Sal)

O parâmetro salt adiciona variação ao processo de assinatura, impedindo que entradas idênticas gerem assinaturas idênticas — útil para evitar ataques de análise estatística:

signer_v1 = Signer(salt="auth_token")
signer_v2 = Signer(salt="password_reset")

print(signer_v1.sign("test"))  # Diferente de signer_v2.sign("test")

Assinaturas com Timestamp

A classe TimestampSigner incorpora um carimbo de tempo cifrado. Isso permite impor validade temporal:

from django.core.signing import TimestampSigner

ts_signer = TimestampSigner()
token = ts_signer.sign("session:789")

# Verifica se o token tem até 300 segundos de idade
try:
    payload = ts_signer.unsign(token, max_age=300)
except SignatureExpired:
    # Tratar expiração
    pass

Serialização Estruturada

Para estruturas complexas (dicionários, listas), use dumps() e loads(), que combinam serialização JSON com assinatura:

from django.core.signing import dumps, loads

data = {"user_id": 101, "role": "admin", "issued_at": 1717023456}
secure_payload = dumps(data)

# Mais tarde...
restored = loads(secure_payload)  # Retorna o dicionário original

Essas funções também suportam compressão automática para payloads grandes e personalização de salt e key.

Tags: Django http-caching etag last-modified Security

Publicado em 8-7 15:14