← Índice del curso

Internet de las Cosas · Tema 40

Seguridad básica de dispositivos

Un equipo que acepta órdenes por red también puede recibir solicitudes de quien no corresponde, datos fuera de contrato o tráfico que agota sus recursos. La seguridad empieza por definir qué se protege, quién puede hacer cada cosa y qué ocurre cuando una comprobación falla. Después se combinan controles que sostengan esas decisiones durante toda la vida del dispositivo.

01 · Proteger el sistema completo

Qué debe proteger un dispositivo IoT

Los temas anteriores conectaron sensores, APIs, órdenes y un gemelo de estado. Cada función abre una posibilidad de uso y una responsabilidad de protección. Una lectura falsa puede ocultar un incidente; una credencial compartida puede permitir suplantar otro equipo; un servicio sin límites puede impedir que el firmware siga adquiriendo.

Objetivos que se complementan.
ObjetivoPreguntaEjemplo en el curso
Confidencialidad¿Quién puede conocer la información?Evitar exponer credenciales o datos de otro cliente
Integridad¿Quién puede modificarla y cómo se detectan cambios?No aceptar una configuración alterada o un firmware no autorizado
Disponibilidad¿El servicio necesario sigue funcionando?La adquisición y el control local no se paralizan por una ráfaga de solicitudes
Trazabilidad¿Qué evidencia queda de las decisiones?Quién pidió un cambio y por qué se admitió o rechazó

La seguridad se diseña junto con el comportamiento del producto. Ante una conexión no confiable, el equipo puede rechazar nuevas órdenes y conservar el control local previsto. El rechazo no debe activar una salida por defecto ni borrar silenciosamente su configuración válida.

Una referencia de diseño es NIST IR 8259A, publicada en 2020: reúne capacidades de identificación, configuración, protección de datos, acceso lógico a interfaces, actualización y conocimiento del estado de ciberseguridad. Este tema las aplica como preguntas de ingeniería; ejecutar sus ejemplos no constituye una certificación.

El tema 41 profundiza cifrado, TLS y certificados; el 42, credenciales y aprovisionamiento. Aquí construimos el mapa general y las comprobaciones que deben seguir existiendo aunque se use un transporte cifrado.

02 · Preguntar antes de elegir controles

Un modelo de amenazas pequeño

Un modelo de amenazas describe qué querés proteger, quién podría afectar eso y por qué camino. No hace falta empezar con una lista interminable: una tabla de tu sistema concreto suele revelar decisiones importantes.

Ejemplo para la cámara y su salida auxiliar.
ActivoSituación que importaControl a revisar
Identidad del equipoOtro dispositivo publica como cámara 07Credencial individual y vínculo entre identidad y recurso
Órdenes de salidaUn lector de telemetría intenta activarPermisos por operación y validaciones locales
ConfiguraciónSe cambia un umbral fuera de su rangoAutorización, validación y versión aplicada
DisponibilidadMensajes enormes o demasiadas solicitudesLímites de tamaño, frecuencia, cola y tiempo
Firmware y secretosSe reemplaza software o se extraen credencialesCadena de actualización, almacenamiento y protección física apropiada
Operación y soporteSe filtra una credencial o se retira el equipoRevocación, rotación y baja verificable

Distinguir capacidades del adversario

No es igual alguien en Internet, alguien dentro de la red local, un usuario autenticado con permisos insuficientes o alguien que accede físicamente a la placa. Un dispositivo comprometido también puede tener credenciales válidas. Autenticarlo no vuelve confiables todos sus datos ni le concede autoridad sobre la flota.

Considerá fallas no deliberadas: un firmware que reintenta sin límite, un reloj perdido o un cliente que manda una carga mal formada pueden producir efectos parecidos a un abuso. Los controles de validación y recursos ayudan en ambos casos.

Priorizá por consecuencias y exposición: qué acción puede afectar el proceso, qué secretos abren otros recursos y qué entradas quedan accesibles. La etiqueta «solo es un sensor» no describe su acceso a la red, a la plataforma ni a las credenciales.

03 · Menos entradas innecesarias

Superficie de ataque y exposición

La superficie de ataque es el conjunto de interfaces y comportamientos por los que se puede interactuar con el sistema. Incluye servicios de red, configuración inicial, actualizaciones, puertos de depuración, archivos, dependencias y canales de soporte.

  • Servicios de red: servidor web, MQTT, BLE de configuración y cualquier puerto de administración.
  • Entradas de datos: JSON, tópicos, rutas, tamaños de archivo, parámetros y mensajes retenidos.
  • Interfaces físicas: consola serial, depuración y almacenamiento extraíble según el hardware.
  • Cadena de software: bibliotecas, firmware descargado y herramientas que publican actualizaciones.
  • Plataforma: API, broker, base de datos, panel y cuentas administrativas.

Inventariá qué necesita estar activo en operación. Una consola abierta para desarrollo no tiene por qué seguir expuesta en un equipo desplegado. Un modo de configuración puede tener una activación local, duración limitada y permisos propios, en lugar de quedar disponible indefinidamente.

Una red privada no sustituye los controles

Estar detrás de NAT o en una Wi-Fi con contraseña reduce ciertos accesos, pero no verifica quién publica una orden en el broker ni qué puede hacer un equipo interno. Segmentación y reglas de red limitan caminos; identidad y autorización limitan acciones.

Si el producto solo necesita iniciar conexiones hacia un servicio, no se abre automáticamente un puerto entrante del router para administrarlo. La arquitectura de control del tema 37 funciona con conexiones iniciadas por el dispositivo y un servicio que aplica permisos.

La revisión también incluye versiones y dependencias. Se registra qué software contiene la imagen y cómo se sabrá que una vulnerabilidad aplicable requiere actualización. El inventario se mantiene durante el soporte, no solo el día de fabricación.

04 · Saber quién se presenta

Identidad y credenciales por defecto

Un nombre de dispositivo es un identificador; no es una prueba de identidad. El campo device="07", una dirección MAC o un tópico elegido por el emisor no reemplazan una credencial verificada.

Una contraseña conocida de fábrica o una misma clave incrustada en todas las unidades amplía el impacto de una filtración. Si todos actúan como la misma cuenta con acceso a la flota, conocer el secreto de un equipo puede dar autoridad sobre otros.

Credenciales por equipo y permisos pequeños

Cada unidad debería poder identificarse de manera individual y revocarse sin retirar las credenciales de todas las demás. El servicio liga esa identidad con recursos concretos. El mecanismo puede usar certificados o tokens apropiados al entorno; su emisión, protección y rotación se desarrollan en el tema 42.

device_id = aux07
credential_ref = identidad-aprovisionada-07
publish_allowed = sitioA/07/tel
subscribe_allowed = sitioA/07/cmd

La referencia indica dónde obtener la credencial.
No contiene una contraseña ni una clave privada.

El firmware no necesita llevar la cuenta administrativa del broker ni de la base. Las credenciales de dispositivos, usuarios y servicios cumplen funciones distintas. Tampoco se colocan claves privadas en código publicado, HTML del panel, mensajes de diagnóstico o ejemplos que luego se copian a producción.

Revocar también cambia el servicio

La revocación debe traducirse en una política efectiva: impedir nuevas autenticaciones y resolver sesiones ya abiertas según el mecanismo usado. Borrar un texto en el panel no garantiza que una conexión autenticada anterior haya perdido acceso.

El almacenamiento local depende del equipo y del riesgo físico. Guardar un secreto en un archivo con otro nombre no lo cifra. Las capacidades de protección del microcontrolador y su proceso de aprovisionamiento deben evaluarse antes de prometer resistencia a extracción.

05 · Identidad no equivale a autoridad

Autenticación y autorización

Autenticar establece una identidad mediante un mecanismo verificado. Autorizar decide si esa identidad puede hacer una operación sobre un recurso. Un equipo 08 correctamente autenticado no debería publicar datos como 07 ni enviarle órdenes.

Política acotada del laboratorio.
IdentidadPermisoNo incluye
equipo07Publicar en sitioA/07/telÓrdenes o recursos de equipo08
equipo08Publicar en sitioA/08/telSuplantar la telemetría de 07
operadorSolicitar en sitioA/07/cmdOtros sitios o administración de identidades

El servicio niega por defecto y comprueba el permiso en cada operación relevante, incluyendo API, broker y acceso a resultados. Un ID difícil de adivinar no reemplaza esa comprobación. La guía de autorización de OWASP desarrolla privilegio mínimo, denegación por defecto y verificación por solicitud.

El alcance sale del contexto confiable

Los permisos se obtienen de la identidad autenticada y de la política del servicio. No se aceptan porque la carga diga "role":"admin". La ruta o el tópico solicitado se contrasta con esa política; si el cuerpo también trae destinatario, ambos deben ser coherentes.

Una ACL de MQTT se revisa en ambas direcciones: publicación y suscripción. Un comodín amplio puede permitir acceder a todos los dispositivos o recibir órdenes de otros. El ejemplo usa recursos exactos para que el alcance sea visible; una política real también cubre los canales de estado, resultados y configuración necesarios.

Las restricciones locales del tema 37 siguen siendo necesarias. Un operador autorizado para solicitar ON no obtiene por eso permiso para ignorar un interbloqueo o superar la duración máxima.

06 · Proteger el canal y el significado

Proteger datos y validar mensajes

La conexión debe proteger confidencialidad e integridad y comprobar la identidad del extremo. Cifrar mientras se acepta cualquier servidor permite conectar con uno que no corresponde. La verificación de certificados, nombre y cadena de confianza se estudia en el tema 41.

Un canal autenticado tampoco valida automáticamente el contenido. Un equipo legítimo puede mandar datos fuera de rango o un comando mal formado. Se comprueban tipos, campos, unidad, longitud, operación y coherencia con el recurso autorizado.

Controles sobre una orden, antes de producir efectos.
ComprobaciónEjemploQué evita
Tamaño real de entradaMáximo 512 bytes antes de interpretar JSONReservas de memoria sin límite
Esquema y tiposVersión entera 1; duración entera, no booleanoInterpretaciones ambiguas
Lista de operaciones permitidasset_output con argumentos definidosEjecutar instrucciones arbitrarias recibidas como texto
Rango y coherenciaEntre 1 y 10 segundos, equipo correctoAcciones fuera de contrato
Identidad y vigenciaReintento conocido o solicitud nueva todavía admisibleReaplicar o admitir órdenes viejas

Se interpreta el formato con un parser y se valida el resultado, en lugar de evaluar texto recibido como código. La guía de validación de OWASP distingue validación sintáctica y semántica y recomienda especificar entradas permitidas.

La deduplicación del tema 38 no autentica un comando: alguien que no tiene permiso no debe consultar su resultado ni provocar trabajo de ejecución. Para una identidad conocida con el mismo contenido se puede recuperar el resultado, incluso después del plazo de admisión original. Una solicitud nueva vencida no se aplica.

Un CRC detecta ciertas alteraciones accidentales, pero no prueba quién creó los datos: quien puede cambiarlos puede recalcularlo. Autenticación criptográfica, cifrado y comprobación de errores tienen propósitos diferentes.

07 · Poder seguir funcionando

Disponibilidad y límites de recursos

Un microcontrolador tiene memoria, CPU, conexiones y almacenamiento acotados. El sistema limita cargas, solicitudes simultáneas, colas, tiempo de espera, reintentos y registros. Una ráfaga no debería dejar sin oportunidad de ejecución a la adquisición y al control local.

  • Tamaño: revisar bytes realmente recibidos antes de reservar o interpretar una carga grande.
  • Frecuencia: cuotas por identidad, recurso y servicio, con ventanas y respuestas definidas.
  • Concurrencia y tiempo: limitar tareas pendientes y conexiones que no progresan.
  • Colas: capacidad, prioridad y descarte explícitos; reservar atención para eventos importantes.
  • Registros: tamaño y retención para que una repetición de errores no llene la flash.

El laboratorio usa una cuota de tres admisiones nuevas por diez segundos y un máximo de 512 B. Son límites didácticos, no valores universales. Las consultas de resultados, los duplicados y la autenticación también necesitan sus propios presupuestos; una cuota de comandos nuevos no protege todo el trabajo del servidor.

La respuesta al límite no activa nada ni reinicia un temporizador de salida. Se conserva el estado local previsto y se informa la causa cuando el canal lo permite. Los reintentos del cliente respetan la política de espera, en lugar de aumentar la carga inmediatamente.

Disponibilidad con comportamiento previsible

Un error de seguridad o una caída de la nube no elimina las reglas locales del proceso. El equipo define cómo continuar o desactivar según su función. Esa decisión se verifica con las condiciones eléctricas y físicas del producto, además del software de comunicaciones.

08 · Mantener la protección

Firmware, mantenimiento y recuperación

La seguridad incluye instalar, operar, actualizar y retirar el dispositivo. Un producto necesita saber qué versión tiene cada equipo, cómo reconocer software autorizado, cómo recuperar una actualización fallida y hasta cuándo recibirá mantenimiento.

En plataformas ESP32, las funciones disponibles dependen de la variante y del entorno. La visión de seguridad de ESP-IDF organiza protección del software, identidad, almacenamiento y comunicación. Secure Boot verifica autenticidad de software; el cifrado de flash tiene otro propósito. Ninguno sustituye la autorización de una orden.

Estas opciones se planifican junto con generación y custodia de claves, recuperación, depuración y fabricación. Algunas configuraciones de hardware son definitivas; no se activan como una receta genérica sin revisar la variante y el proceso. Los temas de OTA y actualización segura profundizan esa etapa.

Registrar decisiones sin filtrar secretos

Se puede registrar identidad verificada, recurso, operación, resultado, causa y tiempo. No se guardan contraseñas, tokens completos, claves privadas ni cargas enteras por costumbre. Los registros tienen permisos y retención; el soporte no necesita todos los secretos para explicar un rechazo.

Un intento anónimo se registra como identidad no verificada, aunque su cuerpo diga «operador». Una auditoría que copia ese texto como si fuese una identidad autenticada también puede inducir a error.

Responder a una credencial comprometida

La operación debe poder localizar el alcance, revocar o limitar la identidad, revisar cambios y aprovisionar una sustitución. Al retirar un equipo se revocan accesos y se resuelven datos y secretos que queden en su almacenamiento. Una restauración de fábrica no basta si la credencial sigue autorizada en el servicio.

09 · Una barrera concreta

Un validador ejecutable en Python

Este bloque valida una orden de demostración. El destinatario esperado viene de una ruta ya autorizada, no de un rol afirmado por el cuerpo. La función solo comprueba formato y coherencia: no autentica, no cifra, no ejecuta y no reemplaza el registro de comandos.

Python en el navegador. Los dos bloques son independientes y no acceden a una red ni a dispositivos. Podés editarlos y ejecutarlos con Pyodide o con Python 3 local.

import json
import re


def objeto_sin_duplicados(pares):
    resultado = {}
    for clave, valor in pares:
        if clave in resultado:
            raise ValueError("Clave JSON repetida")
        resultado[clave] = valor
    return resultado


def constante_invalida(valor):
    raise ValueError("Constante JSON no admitida")


def validar_orden(raw, destino_autorizado):
    if type(raw) is not bytes or len(raw) > 512:
        raise ValueError("Tipo o tamaño de entrada inválido")
    d = json.loads(raw.decode("utf-8"), object_pairs_hook=objeto_sin_duplicados,
                   parse_constant=constante_invalida)
    campos = {"v", "id", "target", "op", "duration_s", "expires_s"}
    if type(d) is not dict or set(d) != campos:
        raise ValueError("Campos no admitidos")
    if type(d["v"]) is not int or d["v"] != 1:
        raise ValueError("Versión inválida")
    if type(d["id"]) is not str or not re.fullmatch(r"[A-Za-z0-9_-]{1,32}", d["id"]):
        raise ValueError("Identidad de comando inválida")
    if type(d["target"]) is not str or d["target"] != destino_autorizado:
        raise ValueError("Destinatario incompatible con el recurso autorizado")
    if d["op"] != "set_output":
        raise ValueError("Operación no admitida")
    if type(d["duration_s"]) is not int or not 1 <= d["duration_s"] <= 10:
        raise ValueError("Duración inválida")
    if type(d["expires_s"]) is not int or d["expires_s"] <= 0:
        raise ValueError("Vencimiento inválido")
    return d


valida = dict(v=1, id="cmd-demo", target="07", op="set_output", duration_s=5, expires_s=20)
raw = json.dumps(valida).encode("utf-8")
assert validar_orden(raw, "07")["duration_s"] == 5
invalidas = [
    json.dumps(dict(valida, duration_s=True)).encode(),
    json.dumps(dict(valida, target="08")).encode(),
    json.dumps(dict(valida, role="admin")).encode(),
    b'{"id":"A","id":"B"}',
    b" " * 513,
    b'{"duration_s":NaN}',
    bytes([255])
]
for entrada in invalidas:
    try:
        validar_orden(entrada, "07")
    except ValueError:
        pass
    else:
        raise AssertionError("Se aceptó una entrada inválida")
print("8 comprobaciones superadas.")
print("Una orden estructuralmente válida todavía necesita identidad, permiso y admisión.")

El límite se calcula con la longitud real de raw, antes de interpretar JSON. No se confía en un campo de la carga que afirme «mido 120 bytes». En un servidor se acota también lo que se recibe del transporte, para no acumular primero un cuerpo ilimitado.

Se usa type(x) is int porque en Python un booleano también puede pasar ciertas comprobaciones basadas en enteros. Los nombres duplicados y las constantes no válidas se rechazan para evitar interpretaciones diferentes entre componentes.

expires_s usa un reloj relativo solo para esta demostración. Comprobar su vencimiento corresponde a la admisión de una orden nueva con una referencia temporal confiable, después de reconocer si existe un resultado de esa identidad. Un resultado guardado puede consultarse sin volver a ejecutar.

10 · Decisiones por capas

Modelo de controles por capas

El siguiente modelo reúne comprobaciones del cliente, del servicio y del protocolo de comandos. Las identidades y la verificación del servidor son resultados simulados de mecanismos externos: en producción salen de bibliotecas y credenciales verificadas, nunca de booleanos que un cliente escribe en su JSON.

Cada fila es un caso independiente, con contexto previo definido. La fila de cuota indica que ya hubo tres admisiones en esa ventana; la de reentrega trae un registro conocido. No se genera tráfico ni se prueban hosts. El rastro de decisiones reúne las distintas capas del modelo: una conexión detenida por el cliente antes de enviar no produciría por eso un registro de solicitud en el servidor real.

def casos():
    base = dict(principal="equipo07", autenticada=True, revocada=False, servidorValido=True,
                recurso="sitioA/07/tel", tipo="tel", bytes=120, valor=41, duracion=5,
                vence=20, anteriores=0, conocida=False, igual=True, legitima=False)
    def caso(nombre, **cambios):
        return dict(base, nombre=nombre, **cambios)
    operador = dict(principal="operador", recurso="sitioA/07/cmd", tipo="cmd")
    return [
        caso("Telemetría propia", legitima=True),
        caso("Orden autorizada", **operador, legitima=True),
        caso("Emisor anónimo", **operador, autenticada=False),
        caso("Equipo 08 publica como 07", principal="equipo08"),
        caso("Un sensor publica una orden", recurso="sitioA/07/cmd", tipo="cmd"),
        caso("Credencial revocada", revocada=True),
        caso("Orden nueva vencida", **operador, vence=9),
        caso("Valor fuera de rango", valor=5000),
        caso("Carga demasiado grande", **operador, bytes=4096),
        caso("Cuota de ventana agotada", anteriores=3),
        caso("Servidor no verificado", **operador, servidorValido=False),
        caso("Reentrega legítima conocida", **operador, conocida=True, legitima=True),
        caso("Mismo ID, otro contenido", **operador, conocida=True, igual=False),
        caso("Operador fuera de su sitio", **dict(operador, recurso="sitioB/07/cmd"))
    ]


def evaluar(tls=True, autenticacion=True, autorizacion=True, validacion=True,
            limites=True, deduplicacion=True, auditoria=True):
    permisos = {"equipo07": ["sitioA/07/tel"], "equipo08": ["sitioA/08/tel"],
                "operador": ["sitioA/07/cmd"]}
    filas, registros = [], []
    for i, c in enumerate(casos()):
        permitido = nueva = False
        confiable = autenticacion and c["autenticada"] and not c["revocada"]
        if tls and not c["servidorValido"]:
            barrera, resultado = "TLS", "No iniciar intercambio con servidor no verificado"
        elif limites and c["bytes"] > 512:
            barrera, resultado = "Tamaño", "Carga supera 512 B"
        elif autenticacion and (not c["autenticada"] or c["revocada"]):
            barrera = "Autenticación"
            resultado = "Credencial revocada" if c["revocada"] else "Identidad no verificada"
        elif autorizacion and (not confiable or c["recurso"] not in permisos.get(c["principal"], [])):
            barrera, resultado = "Autorización", "Sin permiso sobre este recurso y operación"
        elif validacion and ((c["tipo"] == "tel" and not -400 <= c["valor"] <= 1250) or
                            (c["tipo"] == "cmd" and not 1 <= c["duracion"] <= 10)):
            barrera, resultado = "Validación", "Datos fuera del contrato"
        elif deduplicacion and c["conocida"] and not c["igual"]:
            barrera, resultado = "Identidad de comando", "Conflicto: no reemplazar el contenido original"
        elif deduplicacion and c["conocida"]:
            permitido = True
            barrera, resultado = "Resultado guardado", "Reentrega reconocida sin nueva aplicación"
        elif validacion and c["tipo"] == "cmd" and c["vence"] <= 10:
            barrera, resultado = "Vigencia", "No admitir una orden nueva vencida"
        elif limites and c["anteriores"] >= 3:
            barrera, resultado = "Cuota", "Ventana de 10 s: tres admisiones ya consumidas"
        else:
            permitido, nueva = True, c["tipo"] == "cmd"
            barrera = "Admisión"
            resultado = "Nueva aplicación simulada" if nueva else "Telemetría admitida"
        filas.append(dict(nombre=c["nombre"], permitido=permitido, nueva=nueva,
                          barrera=barrera, resultado=resultado, legitima=c["legitima"],
                          reaplicacion=nueva and c["conocida"]))
        if auditoria:
            registros.append(dict(intento=i+1, principal=c["principal"] if confiable and barrera not in ("TLS", "Tamaño") else "no_verificada",
                                  recurso=c["recurso"], decision="permitir" if permitido else "denegar", etapa=barrera))
    return dict(filas=filas, registros=registros,
                admitidas=sum(f["permitido"] for f in filas),
                bloqueadas=sum(not f["permitido"] for f in filas),
                indebidas=sum(f["permitido"] and not f["legitima"] for f in filas),
                reaplicaciones=sum(f["reaplicacion"] for f in filas),
                legitimasBloqueadas=sum(not f["permitido"] and f["legitima"] for f in filas))


base = evaluar()
for f in base["filas"]:
    print(f'{f["nombre"]:30} {"ADMITIDA" if f["permitido"] else "BLOQUEADA":9} {f["barrera"]}')
assert (base["admitidas"], base["bloqueadas"], base["indebidas"]) == (3, 11, 0)
assert base["reaplicaciones"] == 0
assert base["legitimasBloqueadas"] == 0
assert len(base["registros"]) == 14
assert evaluar(autorizacion=False)["indebidas"] == 3
assert evaluar(validacion=False)["indebidas"] == 2
assert evaluar(limites=False)["indebidas"] == 2
assert evaluar(deduplicacion=False)["reaplicaciones"] == 2
assert evaluar(tls=False)["indebidas"] == 1
assert evaluar(autenticacion=False)["legitimasBloqueadas"] == 3
assert evaluar(autenticacion=False, autorizacion=False)["indebidas"] == 5
assert evaluar(auditoria=False)["registros"] == []
print("\n12 comprobaciones superadas.")

Con todas las barreras se admiten dos solicitudes nuevas legítimas y una reentrega conocida sin reaplicar. Se bloquean once casos y se producen catorce registros de decisión. Las doce comprobaciones terminan sin errores.

El campo bytes del modelo representa el tamaño observado por el simulador, no un dato confiado al emisor. Las banderas de identidad y servidor tampoco implementan criptografía. El validador anterior muestra una comprobación real sobre bytes; autenticación y TLS se integran con mecanismos adecuados a la plataforma.

El modelo muestra la primera barrera que detuvo cada caso. Eso no significa que las siguientes dejaran de ser necesarias. Una implementación verifica todo lo requerido antes de producir efectos y no confía en que otra capa siempre habrá filtrado el problema.

11 · Exploración interactiva

Decisiones de seguridad por escenario

Evaluá los catorce casos y quitá una barrera por vez. La comparación incluye usos legítimos para detectar también una política que bloquea todo. Cada ejecución es local; no usa contraseñas, certificados ni conexiones reales.

Política de una instalación de dos equipos

Casos admitidos—
Casos bloqueados—
Solicitudes indebidas admitidas—
Reaplicaciones de comandos conocidos—
Casos legítimos bloqueados—
Registros de auditoría producidos—

Preparando evaluación.

Decisiones de los casos

Cada fila parte de su contexto independiente. «Admitida» solo describe esta evaluación simulada.
CasoDecisiónEtapaExplicación

Registro de decisiones simulado

Reúne decisiones de las distintas capas del modelo. Se omiten secretos y cargas completas; no se afirma una autenticación que no se alcanzó a verificar.
IntentoIdentidad verificadaRecursoDecisiónEtapa

Recorrido guiado

  1. Con todo activo hay tres casos admitidos y once bloqueados. Los admitidos son telemetría propia, una orden autorizada y una reentrega que devuelve resultado sin aplicar otra vez.
  2. Quitá solo autorización. Se admiten tres casos indebidos: otro equipo publica como 07, un sensor publica órdenes y un operador accede a otro sitio. Todos podían tener una identidad válida.
  3. Restaurá y quitá validación. La orden nueva vencida y la temperatura fuera de rango pasan. Una credencial válida no convierte esos datos en correctos.
  4. Restaurá y quitá límites. Se admiten una carga de 4096 B y una solicitud cuando la cuota de admisiones ya estaba agotada.
  5. Restaurá y quitá deduplicación. La reentrega legítima vuelve a aplicar y el ID con contenido cambiado deja de detenerse en ese control. Hay dos reaplicaciones en estos casos independientes.
  6. Restaurá y quitá la verificación del servidor. El caso con servidor no verificado ya no se detiene. No hay una conexión real: el ejercicio muestra la decisión de confianza que faltaría.
  7. Quitá solo autenticación. La autorización conserva la exigencia de una identidad confiable y bloquea incluso los tres casos legítimos. Si también quitás autorización, pasan solicitudes anónimas o revocadas. Menos rechazos no significa mejor funcionamiento.
  8. Restaurá y quitá auditoría. Las decisiones no cambian, pero desaparece el registro de decisiones. La tabla de casos permanece porque pertenece al simulador, no porque existan logs reales.

La tabla enumera escenarios elegidos, no todos los ataques posibles. La protección física, el almacenamiento de secretos, la calidad de la implementación TLS y la recuperación de firmware no se prueban mediante estas casillas.

¿Por qué no se resume todo en una puntuación de seguridad?

Porque dos políticas con igual número de casos bloqueados pueden tener consecuencias distintas. Bloquear usuarios legítimos, admitir órdenes ajenas o perder trazabilidad son problemas diferentes. Se revisan las propiedades requeridas y sus pruebas, no solo un porcentaje.

12 · Un proceso de trabajo

Método y confusiones frecuentes

  1. Inventariá activos e interfaces: datos, salidas, secretos, servicios, puertos y dependencias.
  2. Definí actores y alcance: identidades distintas y acciones permitidas sobre recursos concretos.
  3. Reducí exposición: quitar funciones que no necesita el equipo en operación.
  4. Protegé el canal: verificar el extremo y mantener claves fuera del alcance que no las necesita.
  5. Validá antes del efecto: tamaños, esquema, semántica, identidad de comando y admisión.
  6. Acotá recursos: frecuencia, memoria, concurrencia, colas y retención de registros.
  7. Planificá el ciclo de vida: actualización, revocación, recuperación y retiro.
  8. Probá casos permitidos y prohibidos: también equipos de otro sitio, credenciales revocadas y entradas inválidas.
  9. Revisá la operación real: logs sin secretos, versiones conocidas y procedimientos que se puedan ejecutar.
«Está en mi Wi-Fi, así que puedo confiar en todo lo que mande»

La red puede contener clientes comprometidos o mal configurados. El servicio verifica identidad, permiso y contenido en cada operación relevante.

«TLS resuelve también los permisos de los usuarios»

Protege el canal y puede participar en la autenticación, pero la aplicación todavía decide qué recurso y operación autoriza a esa identidad.

«Un ID o una MAC difíciles de adivinar son una credencial»

Son identificadores. No prueban posesión de un secreto o de una clave verificada. Conocerlos no debería otorgar permisos.

«Una clave común simplifica todo sin cambiar el alcance de una filtración»

Si varias unidades comparten una identidad con autoridad amplia, comprometer una puede afectar a otras. La identidad individual y los permisos acotados permiten limitar y revocar por unidad.

«El parser aceptó JSON, entonces el comando es válido»

La sintaxis no valida tipo, unidad, rango, destinatario ni vigencia. Un objeto JSON perfectamente formado puede pedir una acción no permitida.

«Cuantos más datos guardo en logs, más seguro es el soporte»

Registrar secretos o cargas completas puede crear otra exposición y llenar el almacenamiento. Se guarda evidencia suficiente, con permisos y retención definidos.

«Revocar significa borrar la contraseña que muestra el panel»

La plataforma debe dejar de aceptar esa identidad y tratar las sesiones existentes según su política. El cambio de una pantalla no demuestra que el acceso haya terminado.

13 · Práctica guiada

Predecí antes de comprobar

1. Equipo autenticado, recurso equivocado

El equipo 08 tiene una credencial válida y publica en el tópico de telemetría de 07. ¿Qué control debe detenerlo?

Ver solución

La autorización por recurso y operación. La autenticación solo establece quién es; no lo autoriza a actuar como otro equipo.

2. Una clave compartida

Cien unidades usan la misma cuenta, que puede publicar en todos los tópicos. Se filtra el secreto de una. ¿Qué alcance tiene el problema y qué diseño lo limita?

Ver solución

Puede afectar a toda la autoridad de esa cuenta. Credenciales individuales y permisos por unidad reducen el alcance y permiten revocación selectiva, aunque cada equipo todavía requiere protección propia.

3. Longitud declarada

Una carga afirma medir 100 bytes, pero el transporte entrega 4096. El límite es 512. ¿Qué longitud se verifica?

Ver solución

Los bytes realmente recibidos, acotados durante la recepción y antes de interpretar la carga. El campo declarado por el emisor no controla el presupuesto de memoria.

4. Duración booleana

La operación exige un entero entre 1 y 10. El JSON trae "duration_s":true. ¿Se admite porque Python puede tratar true como 1?

Ver solución

No. El contrato requiere un entero, no un booleano. La comprobación de tipo debe reflejar esa distinción antes de usar su valor.

5. Canal cifrado con servidor no verificado

El dispositivo cifra, pero acepta cualquier servidor. ¿Qué propiedad falta antes de enviar credenciales?

Ver solución

Verificar la identidad del extremo mediante el mecanismo previsto: cadena de confianza, nombre y demás comprobaciones aplicables. Cifrar no demuestra que el destinatario sea el correcto.

6. Cuota agotada

La política admite tres solicitudes nuevas por ventana de diez segundos. Ya admitió tres. ¿Qué hace con una cuarta dentro de esa misma ventana?

Ver solución

La rechaza o difiere según la política explícita, sin ejecutar primero. Consultas y duplicados también necesitan límites propios para que no agoten otros recursos.

7. Auditoría de un anónimo

Una carga sin autenticación dice que pertenece al administrador. ¿Qué identidad queda en el registro confiable?

Ver solución

Identidad no verificada, junto con metadatos apropiados del intento. El texto afirmado por el cuerpo no se registra como una autenticación exitosa.

8. Retirar un dispositivo

Se restaura una placa a fábrica, pero su credencial sigue autorizada en el broker. ¿La baja está completa?

Ver solución

No. Se revoca el acceso en la plataforma y se trata el material que pueda seguir existiendo en el equipo o sus copias. Reiniciar la configuración local no elimina la autorización remota.

14 · Síntesis

Ideas para recordar

  • La seguridad protege datos, identidad, operaciones y disponibilidad durante todo el ciclo de vida.
  • Un modelo de amenazas relaciona activos, actores, entradas y consecuencias antes de elegir controles.
  • Reducir servicios y permisos innecesarios limita la exposición y el alcance de una filtración.
  • Identificar, autenticar y autorizar son tareas diferentes. Un identificador o una carga JSON no prueban identidad.
  • Credenciales individuales permiten revocar y limitar por equipo; los secretos no se publican en código, paneles ni logs.
  • El canal verificado no sustituye validación, vigencia, deduplicación ni límites locales.
  • Las cargas, frecuencias, colas y registros tienen presupuestos explícitos para preservar disponibilidad.
  • El laboratorio admite tres casos legítimos y bloquea once con todas las barreras; es una prueba de decisiones, no de la seguridad completa del producto.

¿Listo para el tema 41?

Antes de avanzar, dibujá las entradas de tu dispositivo y asigná identidad, permiso y límite a cada una. Revisá cómo se revoca una unidad, qué servicios siguen activos y qué información queda en los registros.

En el próximo tema, Cifrado, TLS y certificados, profundizamos cómo proteger las comunicaciones y verificar el extremo con el que habla el microcontrolador.