Capítulo 15 — Infraestructura crítica y seguridad sectorial: laboratorio guiado
Mapa de la lección
Lección 15 publicada. Mapa, ejemplo, EX043.
flowchart TD
N0["Resultados de aprendizaje y conexión"]
N1["15.1 Integridad y autenticidad"]
N2["15.2 Forma canónica"]
N3["15.3 Comparación en tiempo constante"]
N4["15.4 Frescura y orden"]
N5["15.5 Construcción guiada"]
N6["15.6 Lo que esta construcción no hace"]
N7["Síntesis del capítulo"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
N7 --> EX["Ejemplo resuelto"]
EX --> Q["Ejercicio EX043"]
Dibujo lógico
flowchart LR
N0["Resultados de aprendizaje y conexión"]
N1["15.1 Integridad y autenticidad"]
N2["15.2 Forma canónica"]
N3["15.3 Comparación en tiempo constante"]
N4["15.4 Frescura y orden"]
N5["15.5 Construcción guiada"]
N6["15.6 Lo que esta construcción no hace"]
N7["Síntesis del capítulo"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX043"]
[Resultados de aprendizaje y conexión]
|
v
[15.1 Integridad y autenticidad]
|
v
[15.2 Forma canónica]
|
v
[15.3 Comparación en tiempo constante]
|
v
[15.4 Frescura y orden]
|
v
[15.5 Construcción guiada]
|
v
[15.6 Lo que esta construcción no hace]
|
v
[Síntesis del capítulo]
|
v
[Ejemplo resuelto] --> [Ejercicio EX043]
Resultados de aprendizaje y conexión
Al terminar este capítulo, usted debería ser capaz de:
- explicar qué demuestra un código de autenticación de mensajes (MAC) y qué no demuestra;
- implementar el sellado y la verificación con HMAC-SHA-256 mediante una comparación en tiempo constante;
- añadir frescura (ventana temporal) y orden (números de secuencia) para frustrar la reproducción;
- probar la construcción con mensajes deliberadamente erróneos y registrar los resultados como evidencia.
Esta es la primera construcción guiada del componente central de LANTERN. El validador del capítulo 3 comprueba que los mensajes estén bien formados y sean plausibles; no puede saber si un mensaje procede realmente de una estación. Aquí se añade la autenticación. El diseño es deliberadamente sencillo para que cada línea pueda entenderse; un diseño de producción añadiría la gestión de claves y la seguridad del transporte (véase «Lo que esta construcción no hace», más abajo).
15.1 Integridad y autenticidad
Un hash como SHA-256 detecta los cambios accidentales, pero cualquiera puede calcularlo, de modo que un atacante que modifica un mensaje también puede recalcular su hash. Un MAC se calcula con una clave secreta; solo quienes poseen la clave pueden producir un MAC válido. HMAC, definido en el RFC 2104, construye un MAC a partir de una función hash. Si un mensaje lleva un HMAC válido con la clave de una estación, el receptor sabe que lo produjo un poseedor de la clave y que no se modificó en tránsito.
Un MAC no proporciona confidencialidad (el mensaje sigue siendo legible), no demuestra que el sensor de la estación sea correcto y no impide que un atacante reenvíe un mensaje capturado anteriormente: una reproducción.
15.2 Forma canónica
Un MAC se calcula sobre bytes. Si el emisor y el receptor serializan el mismo mensaje de forma distinta (otro orden de claves, otros espacios), la verificación falla. Los mensajes de LANTERN se serializan como JSON con las claves ordenadas y sin espacios, y el MAC se calcula exactamente sobre esos bytes. Defina siempre la forma canónica de manera explícita. Un retransmisor que vuelve a serializar el JSON analizado antes de comprobar el MAC puede aceptar bytes que la estación nunca firmó.
El Lab 15 serializa con las claves ordenadas y sin espacios en ambos lados, de modo que la pasarela de la estación y el retransmisor calculan el MAC sobre bytes idénticos; una actualización del firmware de la pasarela que cambiara los espacios se manifestaría de inmediato como una ráfaga de fallos de MAC de esa estación.
15.3 Comparación en tiempo constante
Comparar el MAC recibido con el esperado mediante una comparación de cadenas ordinaria puede revelar, a través del tiempo de respuesta, cuántos caracteres iniciales coincidieron. La función hmac.compare_digest de Python compara en un tiempo que no depende de dónde difieren las entradas (documentación de hmac de Python). Úsela en todas las comparaciones de MAC. Tanto el Lab 15 como el Lab 29 usan hmac.compare_digest, nunca ==, y el retransmisor que rechazó 40 de 40 pruebas de fallo de MAC en el capítulo 23 debe hacer lo mismo.
Una comparación ordinaria permitiría a un atacante situado en la red de las estaciones averiguar, carácter a carácter, un MAC válido para una lectura de cloro falsificada.
15.4 Frescura y orden
Dos comprobaciones baratas frustran la reproducción. Una ventana temporal rechaza los mensajes cuya marca de tiempo se aleja del reloj del receptor más de un número fijado de segundos (aquí, 120 s); depende de que ambos relojes sean correctos (capítulo 17). Un número de secuencia por estación, que aumenta con cada mensaje, permite al receptor rechazar cualquier mensaje cuyo número no sea mayor que el último aceptado. Ambos valores van dentro del cuerpo protegido por el MAC, así que un atacante no puede cambiarlos sin invalidar el MAC.
El error de reloj de 422 segundos de PS-09 muestra el coste: sus mensajes legítimos no superarían la ventana de 120 s.
15.5 Construcción guiada
El laboratorio siguiente implementa seal (el lado de la estación) y Relay.receive (el lado del retransmisor) y ejecuta siete pruebas: dos mensajes correctos, una reproducción, un valor manipulado, un mensaje sellado con la clave equivocada, un mensaje caducado y un mensaje truncado. Lab 15 (guárdelo como lab15.py y ejecútelo con 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]))
Salida 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
Trabájelo en este orden: (1) lea seal e identifique la forma canónica; (2) lea receive y enumere las comprobaciones en orden; (3) prediga el resultado de cada prueba; (4) ejecútelo; (5) para cada rechazo, nombre la comprobación que lo detectó. Después cambie el orden de las comprobaciones para que la comprobación de reproducción y su actualización de estado se ejecuten antes que la comprobación del MAC, y piense en lo que podría hacer ahora un atacante: enviar mensajes sin autenticar con números de secuencia altos y bloquear una estación. El orden importa: primero autentique y después confíe en el contenido.
15.6 Lo que esta construcción no hace
La clave del laboratorio aparece en el código fuente porque es falsa; en producción, la clave reside en un almacén protegido, legible solo por la cuenta de servicio del retransmisor, y cada estación tiene su propia clave para que una clave robada no permita a un atacante suplantar a todas las estaciones. Las claves necesitan procedimientos de generación, distribución, rotación y revocación (capítulo 18). Aquí el transporte no está cifrado; en producción, el tramo entre el retransmisor y el historiador usa TLS 1.3 (RFC 8446), y que el tramo de las estaciones pueda usar TLS depende de lo que admitan los dispositivos de campo. Muchos dispositivos de campo antiguos no pueden calcular HMAC en absoluto; en ese caso, el MAC lo aplica una pequeña pasarela en la estación, y el enlace entre la pasarela y el dispositivo debe protegerse físicamente.
15.7 Errores frecuentes y límites
- Usar un hash simple y llamarlo autenticación.
- Calcular el MAC sobre un objeto vuelto a serializar en lugar de sobre los bytes recibidos.
- Comparar los MAC con
==. - Actualizar el estado de reproducción antes de la autenticación, o antes de que se hayan superado todas las demás comprobaciones.
- Una sola clave compartida por todas las estaciones.
- Claves en el código fuente, en repositorios de configuración o en incidencias.
- Suponer que un MAC válido significa una lectura correcta.
Cada uno tiene una consecuencia en LANTERN. Un hash simple permite a cualquiera que alcance el retransmisor calcular un resumen de aspecto válido. Una sola clave para todas las estaciones significa que una clave extraída de la pasarela de PS-07, donde el archivo de la clave estaba en una tarjeta extraíble, falsifica mensajes de las catorce estaciones. Y un MAC válido en una lectura de cloro de 9,80 mg/L solo demuestra que la envió PS-07, no que el analizador sea correcto.
Ejercicios — capítulo 15
EX043 — Orden de las comprobaciones. Objetivo: ver por qué la autenticación va primero. Entradas: el Lab 15. Restricciones: modifique una copia. Pasos: (1) mueva la comprobación de reproducción antes de la comprobación del MAC, dejando la actualización de estado donde está; (2) construya un mensaje sin autenticar para PS-04 con seq 1000000 y un MAC erróneo; (3) envíelo y después un mensaje legítimo con seq 3; (4) informe de lo que ocurre y de por qué el orden original es más seguro. Evidencia esperada: código, salida y explicación. Rúbrica: 3 = muestra que, con el estado actualizado antes de la autenticación, un atacante puede bloquear los mensajes futuros de una estación (o, si la actualización de estado sigue después de la comprobación del MAC, muestra que no hay bloqueo y explica por qué la posición de la actualización es la verdadera salvaguarda); la explicación enlaza con «autenticar y después confiar»; 2 = observación sin explicación; 1 = no ejecutado; 0 = nada.
EX044 — Claves por estación. Objetivo: ampliar el diseño. Entradas: el Lab 15. Restricciones: solo la biblioteca estándar. Pasos: (1) dé a cada estación su propia clave en un diccionario; (2) busque la clave por el ID de estación, pero tenga en cuenta que el ID de estación está dentro del cuerpo; (3) explique cómo puede leer con seguridad el ID de estación antes de verificar el MAC y qué no debe hacer con él antes de la verificación. Evidencia esperada: código y explicación. Rúbrica: 3 = analiza el cuerpo solo para seleccionar una clave, verifica el MAC con esa clave antes de usar cualquier otro campo o de actualizar el estado y rechaza las estaciones desconocidas; explica que el ID sin verificar es solo un selector de clave; 2 = funciona, explicación escasa; 1 = usa una sola clave; 0 = nada.
EX045 — Tamaño de la ventana. Objetivo: elegir la ventana de frescura. Entradas: los relojes de las estaciones se mantienen dentro de ±2 s mediante NTP desde el retransmisor; el operador de telecomunicaciones retrasa a veces los mensajes hasta 40 s; tras una caída del operador, las estaciones suben una acumulación de 24 horas. Restricciones: un único diseño que resuelva los tres casos. Pasos: (1) elija una ventana para los mensajes en vivo; (2) explique cómo deben tratarse las subidas de la acumulación sin abrir una brecha de reproducción. Evidencia esperada: media página. Rúbrica: 3 = ventana en vivo de unos 60–120 s razonada; acumulación aceptada por una vía separada y explícita (por ejemplo, un modo de acumulación marcado que siga exigiendo un MAC válido y números de secuencia estrictamente crecientes, con una antigüedad acotada y una alerta cuando se use); 2 = ventana correcta, acumulación insegura o vaga; 1 = ventana muy grande para todo; 0 = nada.
Síntesis del capítulo
Autentique los mensajes con HMAC sobre una forma canónica definida, compare los MAC en tiempo constante y frustre la reproducción con una ventana temporal y números de secuencia por estación incluidos en el cuerpo autenticado. Primero autentique; después confíe en el contenido y regístrelo. Un MAC demuestra el origen y la integridad, no la confidencialidad ni la corrección del sensor. Las claves necesitan su propio ciclo de vida, y cada estación debería tener la suya.
Siga estudiando — capítulo 15 de 30
← Capítulo 14 · Índice de capítulos del curso · Capítulo 16 →
Continúe con la siguiente lección escrita.