FlawOpen

flawopen.com/Ruby YAML/Ruby YAML

¿Es seguro YAML.load con entradas no confiables?

CWE-502: Deserialization of Untrusted DataPágina de referencia
Respuesta breve

Comprueba la versión de Psych que carga realmente la aplicación: Psych 4, incluido en Ruby 3.1, cambió YAML.load para usar deserialización segura por defecto. Las versiones anteriores de Psych construían objetos sin esa restricción; Psych también se puede actualizar independientemente de Ruby. YAML.unsafe_load y Marshal.load deserializan objetos Ruby y nunca deben recibir entradas no confiables.

VULNERABLE
# Psych < 4: YAML.load used unsafe
# object construction by default.
YAML.load(params[:config])

# Every Psych version: explicitly unsafe.
YAML.unsafe_load(user_input)
Psych.unsafe_load(user_input)

# Marshal has no restricted safe-load API.
Marshal.load(cookie_value)
FIXED
# Explicit restricted types; keep this list minimal.
require "yaml"
require "date"

data = YAML.safe_load(
  input,
  permitted_classes: [Date],
  permitted_symbols: [],
  aliases: false
)

# For ordinary external data, prefer JSON and
# validate its shape, values, and input size.
data = JSON.parse(input)

Por qué YAML puede ser peligroso

YAML tiene etiquetas que Psych puede convertir en objetos Ruby. Con una carga insegura, un documento etiquetado puede solicitar la construcción de una clase y proporcionar valores para su estado serializado; el impacto depende de las clases disponibles y de su comportamiento. Así se forman cadenas de gadgets de deserialización. Use carga segura para YAML no confiable y valide por separado los datos resultantes. El riesgo general también existe con Python pickle, PHP unserialize y Marshal.load en Ruby.

Rails y otras aplicaciones pueden cargar muchas dependencias, lo que amplía el conjunto de clases y comportamientos disponibles para una cadena de deserialización insegura. Depende del despliegue; no es una propiedad de toda aplicación Rails ni de cualquier documento YAML.

Por qué el cambio de versión generó confusión

Psych 4 cambió YAML.load para usar carga segura por defecto e introdujo unsafe_load para el comportamiento anterior de construcción de objetos. Ruby 3.1 incluyó Psych 4, pero las aplicaciones pueden seleccionar otra versión de la gema Psych; audite la dependencia resuelta y el runtime, no solo la versión de Ruby. Como la carga segura puede rechazar clases específicas de la aplicación o alias, migre con una lista explícita y mínima de clases permitidas en vez de cambiar a unsafe_load.

Si una actualización lleva a introducir unsafe_load, trate esa llamada como un hallazgo cuando la entrada pueda controlarse fuera del límite de despliegue confiable. Prefiera safe_load con la lista mínima necesaria de permitted_classes; no la amplíe solo para silenciar errores de compatibilidad.

FAQ

Preguntas frecuentes

¿YAML.safe_load es completamente seguro?

Psych.safe_load limita las clases deserializadas a las permitidas (por defecto, tipos escalares y colecciones básicos) y rechaza alias. Esto no valida la estructura, el tamaño ni las reglas de negocio de la aplicación. Permita solo las clases necesarias, limite el tamaño y valide los datos devueltos. Algunas implementaciones y flujos YAML han tenido DoS por expansión de alias; CVE-2024-35221 documenta un caso al procesar metadatos de RubyGems. No demuestra que toda llamada a Psych.safe_load sea vulnerable.

¿Y si cargo mis propios archivos de configuración?

Un archivo incluido en un artefacto de aplicación confiable y protegido por integridad tiene un origen distinto de una carga de usuario o un archivo controlado por un tenant. Incluso para configuración interna, prefiera safe_load y permita solo las clases necesarias. Use unsafe_load únicamente para datos fijos y demostrablemente confiables; nunca para contenido influido por usuarios o solicitudes.

¿Marshal puede ser aceptable?

Otro riesgo de Ruby: Marshal.load

A diferencia de YAML, la documentación de Ruby indica que Marshal.load puede reconstruir casi cualquier clase cargada en el proceso y provocar ejecución remota de código si los datos no son confiables. Mantén los datos Marshal dentro de un límite de confianza; nunca deserialices bytes enviados por usuarios o influenciados por atacantes.

Referencias

Véase también Deserialización insegura: riesgos y defensa .NET BinaryFormatter: riesgos y migración ObjectInputStream en Java: deserialización segura Newtonsoft.Json TypeNameHandling: ¿configuración segura? PHP unserialize() y datos no confiables Seguridad de pickle.load() en Python: riesgos de datos no confiables