◇ OpenCyber Academy
Ingeniería · Capítulo 4 · Español

Capítulo 4 — Programación I

Aprendizaje gratuito y abierto para quienes desean proteger a las personas y la tecnología. No se necesita una cuenta para estudiar ni consultar las respuestas.

Capítulo 4 — Programación I

Mapa de la lección

Lección 04 publicada. Mapa, ejemplo, EX010.

flowchart TD
  N0["Resultados de aprendizaje"]
  N1["4.1 Los programas como instrucciones precisas"]
  N2["4.2 Estructuras de datos"]
  N3["4.3 Entradas no confiables"]
  N4["4.5 Pruebas"]
  N5["Síntesis del capítulo"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5
  N5 --> EX["Ejemplo resuelto"]
  EX --> Q["Ejercicio EX010"]

Dibujo lógico

flowchart LR
  N0["Resultados de aprendizaje"]
  N1["4.1 Los programas como instrucciones precisas"]
  N2["4.2 Estructuras de datos"]
  N3["4.3 Entradas no confiables"]
  N4["4.5 Pruebas"]
  N5["Síntesis del capítulo"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX010"]
[Resultados de aprendizaje]
    |
    v
[4.1 Los programas como instrucciones precisas]
    |
    v
[4.2 Estructuras de datos]
    |
    v
[4.3 Entradas no confiables]
    |
    v
[4.5 Pruebas]
    |
    v
[Síntesis del capítulo]
    |
    v
[Ejemplo resuelto] --> [Ejercicio EX010]

Resultados de aprendizaje

Usted será capaz de (1) escribir programas pequeños y correctos en Python con funciones, bucles y estructuras de datos; (2) manejar entradas no confiables de forma segura; (3) probar código con aserciones; y (4) leer los laboratorios del libro y modificarlos.

4.1 Los programas como instrucciones precisas

Un programa dice exactamente lo que hace el ordenador. Las variables guardan valores; las funciones empaquetan pasos; los bucles repiten; los condicionales eligen. El código legible usa nombres claros y funciones pequeñas. Los laboratorios de este libro usan solo la biblioteca estándar de Python, de modo que se ejecutan en cualquier lugar. La precisión tiene dos caras: el ordenador hace lo que dice el código, no lo que quería decir su autor. Un bucle que se detiene un elemento antes o una condición escrita con 'or' en lugar de 'and' producen resultados erróneos sin ningún aviso. Sam lee cada laboratorio línea por línea antes de ejecutarlo y predice la salida, de modo que una sorpresa en el resultado impreso señala una laguna en su comprensión.

4.2 Estructuras de datos

Las listas guardan secuencias; los diccionarios asocian claves con valores; los conjuntos guardan elementos únicos. Un inventario de modelos es, de forma natural, una lista de diccionarios; una lista de permitidos de una política es, de forma natural, un conjunto. Elegir la estructura adecuada hace el código más corto y más seguro: la pertenencia a un conjunto es una prueba clara y rápida para «¿está permitida esta herramienta?». Una lista también serviría como lista de permitidos, pero comprobar la pertenencia en ella obliga a recorrer todos los elementos, y pueden colarse duplicados sin que nadie lo note. La estructura equivocada también puede ocultar un fallo de seguridad: si las herramientas permitidas se guardan como una sola cadena separada por comas, una comprobación como 'search' in allowed aceptaría también una herramienta llamada 'research'.

4.3 Entradas no confiables

Todo dato que venga de fuera del programa (archivos, mensajes de red, salidas de modelos) es no confiable. Nunca lo pase a eval, a comandos de shell ni a consultas de bases de datos sin una validación estricta. Esto se aplica a la IA: OWASP LLM 2025 incluye el manejo inadecuado de la salida (LLM05) porque las aplicaciones suelen ejecutar o representar directamente la salida del modelo. Analice con analizadores estrictos (p. ej., json.loads), valide tipos y rangos, y rechace los campos desconocidos.

Rechazar los campos desconocidos importa porque los sistemas posteriores pueden actuar sobre campos que el validador nunca miró. Un analizador estricto los rechaza de plano.

4.4 Los errores como información

Cuando el código falla, debe fallar de forma cerrada y explicarse. Un analizador que descarta en silencio un campo desconocido podría dejar pasar un campo que el sistema posterior sí respeta; un analizador que rechaza el objeto entero con un mensaje claro es más seguro y más fácil de depurar. Registre el rechazo con contexto suficiente para investigarlo (hora, origen, motivo), pero sin copiar contenido sensible en los registros. Las excepciones son el mecanismo normal de Python: lance ValueError con un mensaje y deje que quien llama decida si reintentar, consultar a una persona o detenerse.

4.5 Pruebas

Un assert enuncia lo que debe ser verdadero. Las pruebas unitarias llaman a funciones con entradas elegidas y comprueban las salidas, incluidos los casos límite (listas vacías, valores muy grandes, texto mal formado). Las pruebas hacen que los cambios posteriores sean seguros. En el código de seguridad, las pruebas más valiosas son las que deberían fallar: una orden de trabajo con un campo de más, una prioridad escrita de otra forma, un identificador de activo que contiene signos de puntuación. Cada rechazo esperado se escribe como una prueba, de modo que un cambio posterior que relaje la validación rompa la compilación en lugar de llegar a producción. Sam conserva como caso de prueba permanente cada entrada mal formada que alguna vez causó un problema.

4.6 Ejemplo resuelto (caso sintético de NRS)

Sam escribe una función que analiza la orden de trabajo propuesta por Wrench (JSON) antes de que llegue al sistema de órdenes de trabajo:

import json
ALLOWED = {"asset_id", "priority", "summary"}
def parse_order(text):
    d = json.loads(text)
    assert isinstance(d, dict) and set(d) <= ALLOWED, "unexpected fields"
    assert d.get("priority") in {"low", "medium", "high"}, "bad priority"
    assert isinstance(d.get("asset_id"), str) and d["asset_id"].isalnum(), "bad asset id"
    return d
print(parse_order('{"asset_id": "AX12", "priority": "high", "summary": "bearing hot"}'))
try:
    parse_order('{"asset_id": "AX12; rm", "priority": "high"}')
except AssertionError as e:
    print("rejected:", e)

El programa imprime la orden válida y después rejected: bad asset id. La salida del modelo se trata como datos que hay que validar, nunca como código.

El identificador de activo se rechaza porque el punto y coma y el espacio no superan la comprobación alfanumérica, de modo que un texto que parece un comando de shell nunca llega al sistema de órdenes de trabajo. La función nunca exige un resumen, así que una orden sin él pasaría; Sam añade esa comprobación a continuación. En producción, Sam sustituye las aserciones por excepciones explícitas, por la razón que se da entre los errores siguientes.

4.7 Errores y límites

Cada error tiene una señal reconocible en la revisión de código. Las llamadas a eval, exec o subprocess con una cadena construida a partir de la salida del modelo son lo primero que hay que buscar. Los validadores sin pruebas para entradas rechazadas probablemente nunca se han probado con datos malos. Las aserciones que protegen condiciones de seguridad deberían convertirse en sentencias if que lancen una excepción con nombre, porque, de lo contrario, una ejecución optimizada en producción se saltaría la comprobación por completo sin ningún mensaje.

Ejercicios — capítulo 4

Síntesis del capítulo

Programar para la seguridad de la IA empieza por un hábito: tratar todo lo que viene de fuera, incluida la salida del modelo, como datos no confiables, validarlo estrictamente y probar los extremos.

Siga estudiando — capítulo 04 de 50

← Capítulo 03 (borrador) · Índice de capítulos del curso · Capítulo 05 (borrador) →