◇ OpenCyber Academy
Tecnólogo · Capítulo 15 · Português

Capítulo 15 — Infraestrutura crítica e segurança setorial: laboratório guiado

Ensino gratuito e aberto para quem deseja proteger pessoas e tecnologias. Não é preciso criar uma conta para estudar ou consultar as respostas.

Capítulo 15 — Infraestrutura crítica e segurança setorial: laboratório guiado

Mapa da aula

Aula 15 publicada. Mapa, exemplo, EX043.

flowchart TD
  N0["15.1 Integridade e autenticidade"]
  N1["15.2 Forma canônica"]
  N2["15.3 Comparação em tempo constante"]
  N3["15.4 Atualidade e ordem"]
  N4["15.5 Construção guiada"]
  N5["15.6 O que esta construção não faz"]
  N6["Síntese do capítulo"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6
  N6 --> EX["Exemplo resolvido"]
  EX --> Q["Exercício EX043"]

Desenho lógico

flowchart LR
  N0["15.1 Integridade e autenticidade"]
  N1["15.2 Forma canônica"]
  N2["15.3 Comparação em tempo constante"]
  N3["15.4 Atualidade e ordem"]
  N4["15.5 Construção guiada"]
  N5["15.6 O que esta construção não faz"]
  N6["Síntese do capítulo"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> Q["EX043"]
[15.1 Integridade e autenticidade]
    |
    v
[15.2 Forma canônica]
    |
    v
[15.3 Comparação em tempo constante]
    |
    v
[15.4 Atualidade e ordem]
    |
    v
[15.5 Construção guiada]
    |
    v
[15.6 O que esta construção não faz]
    |
    v
[Síntese do capítulo]
    |
    v
[Exemplo resolvido] --> [Exercício EX043]

Objetivos de aprendizagem e conexão

Depois deste capítulo, você deve ser capaz de:

Esta é a primeira construção guiada do componente central do LANTERN. O validador do Capítulo 3 confere se as mensagens são bem formadas e plausíveis; ele não consegue dizer se uma mensagem realmente veio de uma estação. Aqui você acrescenta a autenticação. O projeto é deliberadamente simples, para que cada linha possa ser entendida; um projeto de produção acrescentaria gestão de chaves e segurança de transporte (veja "O que esta construção não faz", abaixo).

15.1 Integridade e autenticidade

Um hash como o SHA-256 detecta mudanças acidentais, mas qualquer um pode calculá-lo, então um atacante que muda uma mensagem também pode recalcular o seu hash. Um MAC é calculado com uma chave secreta; apenas quem detém a chave consegue produzir um MAC válido. O HMAC, definido na RFC 2104, constrói um MAC a partir de uma função de hash. Se uma mensagem carrega um HMAC válido sob a chave de uma estação, o receptor sabe que ela foi produzida por quem detém a chave e não foi alterada em trânsito.

Um MAC não fornece confidencialidade (a mensagem continua legível), não prova que o sensor da estação está certo e não impede que um atacante reenvie uma mensagem capturada antes — uma reprodução.

15.2 Forma canônica

Um MAC é calculado sobre bytes. Se o remetente e o receptor serializarem a mesma mensagem de formas diferentes — ordem de chaves diferente, espaçamento diferente —, a verificação falha. As mensagens do LANTERN são serializadas como JSON com chaves ordenadas e sem espaços, e o MAC é calculado exatamente sobre esses bytes. Defina sempre a forma canônica de modo explícito.

Um relay que reserializa o JSON interpretado antes de conferir o MAC pode aceitar bytes que a estação nunca assinou.

O Laboratório 15 serializa com chaves ordenadas e sem espaços dos dois lados, de modo que o gateway da estação e o relay calculam o MAC sobre bytes idênticos; uma atualização de firmware do gateway que mudasse o espaçamento apareceria de imediato como uma rajada de falhas de MAC daquela estação.

15.3 Comparação em tempo constante

Comparar o MAC recebido com o esperado usando uma comparação comum de strings pode vazar, pelo tempo, quantos caracteres iniciais coincidiram. O hmac.compare_digest do Python compara num tempo independente de onde as entradas diferem (documentação do hmac do Python). Use-o em toda comparação de MAC.

O Laboratório 15 e o Laboratório 29 usam ambos hmac.compare_digest, nunca ==, e o relay que rejeitou 40 de 40 testes de falha de MAC no Capítulo 23 precisa fazer o mesmo.

Uma comparação comum permitiria que um atacante na rede da estação descobrisse, um caractere de cada vez, um MAC válido para uma leitura de cloro forjada.

15.4 Atualidade e ordem

Duas verificações baratas derrotam a reprodução. Uma janela de tempo rejeita as mensagens cujo carimbo de tempo esteja a mais de um número definido de segundos do relógio do receptor (aqui, 120 s); ela depende de os dois relógios estarem certos (Capítulo 17). Um número de sequência por estação, que cresce a cada mensagem, permite ao receptor rejeitar qualquer mensagem cujo número não seja maior que o último aceito. Os dois valores ficam dentro do corpo autenticado pelo MAC, então um atacante não consegue alterá-los sem invalidar o MAC.

O erro de relógio de 422 segundos da PS-09 mostra o custo: as suas mensagens genuínas falhariam na janela de 120 s.

15.5 Construção guiada

O laboratório abaixo implementa seal (o lado da estação) e Relay.receive (o lado do relay) e executa sete testes: duas mensagens boas, uma reprodução, um valor adulterado, uma mensagem selada com a chave errada, uma mensagem velha e uma mensagem truncada.

Laboratório 15 (salve como lab15.py, execute com python3 lab15.py):

# Lab 15 - guided build: an authenticated telemetry relay message with replay protection (offline, stdlib only)
# Synthetic key and messages. A real deployment keeps the key in a protected store, never in source code.
import hmac, hashlib, json
KEY = bytes.fromhex("6c616e7465726e2d6c61622d6b65792d6e6f742d7265616c2d30303031")  # "lantern-lab-key-not-real-0001"
WINDOW = 120  # seconds
def seal(station, tag, value, ts, seq, key=KEY):
    body = json.dumps({"st": station, "tag": tag, "v": value, "ts": ts, "seq": seq}, sort_keys=True, separators=(",", ":"))
    mac = hmac.new(key, body.encode(), hashlib.sha256).hexdigest()
    return body + "." + mac
class Relay:
    def __init__(self, key): self.key, self.last = key, {}
    def receive(self, wire, now):
        try:
            body, mac = wire.rsplit(".", 1)
        except ValueError:
            return "reject: malformed"
        good = hmac.new(self.key, body.encode(), hashlib.sha256).hexdigest()
        if not hmac.compare_digest(good, mac):
            return "reject: bad MAC"
        m = json.loads(body)
        if abs(now - m["ts"]) > WINDOW:
            return "reject: outside time window"
        if m["seq"] <= self.last.get(m["st"], -1):
            return "reject: replay"
        self.last[m["st"]] = m["seq"]
        return f"accept {m['st']} {m['tag']}={m['v']}"
r = Relay(KEY)
now = 1_790_000_000
a = seal("PS-04", "LEVEL_M", 3.42, now - 5, 1)
b = seal("PS-04", "LEVEL_M", 3.44, now - 1, 2)
tampered = a.replace('"v":3.42', '"v":9.42')
forged = seal("PS-04", "LEVEL_M", 1.00, now, 3, key=b"guessed-key")
old = seal("PS-07", "CL2_MGL", 0.6, now - 900, 1)
tests = [("first", a), ("second", b), ("replay of first", a), ("tampered value", tampered),
         ("wrong key", forged), ("stale", old), ("truncated", a[:20])]
for name, wire in tests:
    print(f"{name:<16} {r.receive(wire, now)}")
print("MAC length (hex chars):", len(a.rsplit('.', 1)[1]))

Saída esperada (Python 3.13.5):

first            accept PS-04 LEVEL_M=3.42
second           accept PS-04 LEVEL_M=3.44
replay of first  reject: replay
tampered value   reject: bad MAC
wrong key        reject: bad MAC
stale            reject: outside time window
truncated        reject: malformed
MAC length (hex chars): 64

Trabalhe nesta ordem: (1) leia seal e identifique a forma canônica; (2) leia receive e liste as verificações, em ordem; (3) preveja o resultado de cada teste; (4) execute; (5) para cada rejeição, nomeie a verificação que a pegou. Depois mude a ordem das verificações para que a verificação de reprodução e a sua atualização de estado rodem antes da verificação do MAC, e pense no que um atacante poderia fazer agora: enviar mensagens não autenticadas com números de sequência altos e bloquear uma estação. A ordem importa — autentique primeiro, depois confie no conteúdo.

15.6 O que esta construção não faz

A chave do laboratório está impressa no código-fonte porque é falsa; em produção, a chave fica num armazenamento protegido, legível apenas pela conta de serviço do relay, e cada estação tem a sua própria chave, para que uma chave roubada não permita a um atacante se passar por todas as estações. As chaves precisam de procedimentos de geração, distribuição, rotação e revogação (Capítulo 18). O transporte aqui não é cifrado; em produção, o trecho relay–historiador usa TLS 1.3 (RFC 8446), e se o trecho da estação pode usar TLS depende do que os dispositivos de campo suportam. Muitos dispositivos de campo mais antigos nem conseguem calcular HMACs; nesse caso, o MAC é aplicado por um pequeno gateway na estação, e o enlace gateway–dispositivo precisa ser protegido fisicamente.

15.7 Erros comuns e limites

Cada um tem uma consequência no LANTERN. Um hash simples permite que qualquer um que alcance o relay calcule um digest de aparência válida. Uma chave para todas as estações significa que uma chave retirada do gateway da PS-07, onde o arquivo da chave ficava num cartão removível, forja mensagens para todas as catorze estações. E um MAC válido numa leitura de cloro de 9,80 mg/L prova apenas que a PS-07 a enviou, e não que o analisador está certo.

Exercícios — capítulo 15

EX043 — Ordem das verificações. Objetivo: ver por que a autenticação vem primeiro. Entradas: Laboratório 15. Restrições: modifique uma cópia. Etapas: (1) mova a verificação de reprodução para antes da verificação do MAC, mantendo a atualização de estado onde está; (2) monte uma mensagem não autenticada para a PS-04 com seq 1000000 e um MAC errado; (3) envie-a, e depois uma mensagem genuína com seq 3; (4) relate o que acontece e por que a ordem original é mais segura. Evidência esperada: código, saída e explicação. Rubrica: 3 = mostra que, com o estado atualizado antes da autenticação, um atacante consegue bloquear as mensagens futuras de uma estação (ou, se a atualização de estado ficar depois da verificação do MAC, mostra que não há bloqueio e explica por que a posição da atualização é a verdadeira salvaguarda); a explicação se liga a "autenticar, depois confiar"; 2 = observação sem explicação; 1 = não executado; 0 = nada.

EX044 — Chaves por estação. Objetivo: estender o projeto. Entradas: Laboratório 15. Restrições: apenas a biblioteca padrão. Etapas: (1) dê a cada estação a sua própria chave num dicionário; (2) busque a chave pelo ID da estação — mas note que o ID da estação fica dentro do corpo; (3) explique como você pode ler com segurança o ID da estação antes de verificar o MAC, e o que você não pode fazer com ele antes da verificação. Evidência esperada: código e explicação. Rubrica: 3 = interpreta o corpo apenas para selecionar uma chave, verifica o MAC com essa chave antes de usar qualquer outro campo ou atualizar o estado, rejeita estações desconhecidas; explica que o ID não verificado é apenas um seletor de chave; 2 = funciona, explicação rasa; 1 = usa uma única chave; 0 = nada.

EX045 — Tamanho da janela. Objetivo: escolher a janela de atualidade. Entradas: os relógios das estações são mantidos dentro de ±2 s por NTP a partir do relay; a operadora às vezes atrasa mensagens em até 40 s; depois de uma interrupção da operadora, as estações enviam um acúmulo de 24 horas. Restrições: um projeto que trate os três. Etapas: (1) escolha uma janela para as mensagens ao vivo; (2) explique como os envios de acúmulo devem ser tratados sem abrir uma brecha de reprodução. Evidência esperada: meia página. Rubrica: 3 = janela ao vivo de cerca de 60–120 s, com raciocínio; acúmulo aceito por um caminho separado e explícito (por exemplo, um modo de acúmulo sinalizado que ainda exige MAC válido e números de sequência estritamente crescentes, com uma idade limitada e um alerta quando usado); 2 = janela certa, acúmulo inseguro ou vago; 1 = janela muito grande para tudo; 0 = nada.

Síntese do capítulo

Autentique as mensagens com HMAC sobre uma forma canônica definida, compare os MACs em tempo constante e derrote a reprodução com uma janela de tempo e números de sequência por estação levados dentro do corpo autenticado. Autentique primeiro, depois confie no conteúdo e registre-o. Um MAC prova a origem e a integridade, e não a confidencialidade nem a correção do sensor. As chaves precisam do seu próprio ciclo de vida, e cada estação deve ter a sua.

Continue estudando — capítulo 15 de 30

← Capítulo 14 · Índice de capítulos do curso · Capítulo 16 →

Siga para a próxima lição escrita.