Cómo proteger un frontend de React antes de producción

Sat Aug 29 2026

Cómo proteger un frontend de React antes de producción

Y el 3 de diciembre de 2025, React publicó un aviso crítico sobre React Server Components, GHSA-fv66-9v8q-g76r. Aplica el parche en el límite del framework y sigue usando React.

Además, el historial de avisos publicado por React contiene ocho divulgaciones desde diciembre de 2025 hasta julio de 2026. Usa una regla sencilla de publicación/no publicación: los valores predeterminados de React pueden resolver una preocupación de renderizado, pero un secreto visible, un sink de inyección sin explicación, un flujo de autenticación roto o un problema de alta gravedad del framework aplicable y sin parche bloquean el lanzamiento.

Pero el escape de JSX de React te proporciona una base útil para la revisión de seguridad.

Revisarás el modelo de renderizado de React, los secretos visibles en el navegador, el almacenamiento de tokens y OAuth. También comprobarás las dependencias y los headers desplegados. Después obtendrás un criterio de lanzamiento basado en bloqueadores para el artefacto real de producción.

En este artículo

React es seguro de usar; tu límite de aplicación aún necesita pruebas

La documentación de React afirma: «Cada commit de React se prueba en superficies críticas para el negocio con más de mil millones de usuarios. Más de 100.000 componentes de React en Meta ayudan a validar cada estrategia de migración». Es una evidencia útil de la madurez de la biblioteca. No es una aprobación de la aplicación.

Tu código controla HTML no confiable. Tu build controla la configuración enviada al navegador. Tu API controla el acceso a las facturas. Las dependencias se ejecutan durante la instalación y la ejecución. Tu plataforma de hosting establece los headers que reciben los navegadores.

La pregunta práctica es si tu build usa RSC, Server Functions u otra integración afectada. Si es así, comprueba las versiones corregidas aplicables antes de aprobar el lanzamiento. Si solo publica un bundle del lado del cliente, los avisos de RSC y Server Functions enumerados generalmente no describen ese bundle del navegador; verifica si tu framework sigue ejecutando componentes de servidor afectados en producción.

La lista de comprobación habitual de React está mal organizada. Mezcla la exposición del navegador, la autorización de la API y el comportamiento móvil como si fueran un solo problema. Son límites diferentes, con responsables y pruebas diferentes.

React protege algunas rutas de renderizado. No protege los secretos de tu aplicación, tu modelo de autorización, tu cadena de suministro de paquetes ni tu despliegue.

Mapea los límites del navegador, el servidor y la entrega

Empieza comprobando tres superficies:

SuperficieQué existe en este límiteQué debe probar tu revisión de lanzamiento
Contenido renderizado por ReactLa interpolación ordinaria de JSX escapa los valores según las reglas de renderizado de ReactSinks HTML, esquemas de URL inseguros, ejecución dinámica de código
Aplicación expuesta al navegadorEl navegador ejecuta bundles y realiza solicitudes de redBundles, source maps, variables de entorno, almacenamiento, URLs, tokens, datos personales
Capa de servidor y entregaTu servidor y tu plataforma pueden guardar secretos y aplicar controles de accesoAutorización de API, límites de solicitudes, versiones de RSC/Server Functions, scripts de instalación, HTTPS, CSP, headers

Este modelo mantiene separadas las responsabilidades. El navegador puede mostrar un estado de autorización; no puede mantener confidencial un valor después de recibirlo. Tu servidor puede proteger una clave de proveedor; aun así debe autorizar cada solicitud.

OWASP Bullet-proof React es un proyecto Incubator en la versión 0.0.0. Úsalo como una colección útil de orientación, no como una certificación o un framework de aprobación terminado. Tu criterio debe nombrar la evidencia para cada riesgo.

La página del proyecto de OWASP actualmente enumera Top 10:2025 como la edición vigente. Trata con cautela las páginas que anuncian una edición 2026 separada; la discusión de Security Boulevard también informa de que no existe un nuevo OWASP Top 10 para 2026.

La comprobación genérica de nombres de OWASP no obtiene ninguna aprobación aquí; sí la obtiene una prueba vinculada al artefacto desplegado.

Para cada hallazgo, formula cuatro preguntas. ¿Qué límite falló? ¿Qué puede hacer un atacante? ¿Qué cambió? ¿Qué prueba demuestra el cambio?

JSX escapa los valores ordinarios; inspecciona cada vía de escape

React escapa los valores interpolados en JSX ordinario:

<p>{displayName}</p>

Si displayName contiene markup, React lo renderiza como texto. Mantén esta ruta predeterminada siempre que sea posible.

Busca dangerouslySetInnerHTML, eval() y new Function() en el repositorio. Comprueba los valores href o src ensamblados dinámicamente, los renderizadores de Markdown o HTML, los esquemas de URL que pueden convertirse en javascript: y los componentes de terceros que aceptan HTML sin procesar.

// Valor predeterminado seguro: el nombre proporcionado por el usuario se renderiza como texto
<h2>{displayName}</h2>

// Inseguro a menos que invoice.notes se haya saneado deliberadamente
<div dangerouslySetInnerHTML={{ __html: invoice.notes }} />

Elimina dangerouslySetInnerHTML cuando la funcionalidad no requiera HTML. Cuando el sink permanezca, exige una razón documentada, una política de entrada y pruebas para el sanitizer que hayas elegido. Usa la allowlist documentada de ese sanitizer en lugar de asumir que existe un conjunto universalmente seguro de elementos y atributos.

Prueba los casos que permite tu renderizador. Comprueba los esquemas de URL ejecutables, los atributos de manejadores de eventos y los atributos peligrosos de SVG o HTML. Prueba los enlaces, imágenes y frames renderizados. Las URL data: merecen un tratamiento explícito cuando la funcionalidad no las requiere.

«Está almacenado en nuestra base de datos» no significa que sea confiable. Una base de datos suele contener contenido enviado por usuarios, integraciones o archivos importados.

Revisa los enlaces con el mismo cuidado que el HTML. Una URL controlada por el usuario es un dato que necesita validación, incluso cuando JSX renderiza de forma segura el elemento que la rodea.

La validación y autorización del lado del servidor siguen siendo obligatorias. Ocultar un botón en React cambia la interfaz. No cambia quién puede llamar a la API.

Un secreto visible para el cliente bloquea el lanzamiento

Considera un dashboard de React donde los usuarios suben facturas a un proveedor de procesamiento de documentos. El diseño tentador envía la solicitud directamente desde el navegador, lo que significa que la clave privada del proveedor debe llegar al navegador mediante una solicitud, un bundle o una configuración generada.

La documentación de seguridad de React Native establece la regla: «Nunca almacenes claves API sensibles en el código de tu aplicación. Cualquier cosa incluida en tu código podría ser accedida en texto plano por cualquiera que inspeccione el bundle de la aplicación».

Si un secreto está en un bundle de React, el lanzamiento queda bloqueado. El nombre de .env es infraestructura de build, no protección. En muchos toolchains de React, las variables de entorno destinadas al cliente se insertan durante el build; verifica el comportamiento de tu framework e inspecciona el artefacto. Cambiar el nombre de una variable a .env es higiene de configuración, no gestión de secretos.

Usa un límite concreto:

  1. El cliente de React sube la factura a tu función serverless.
  2. La función autentica al usuario y comprueba la autorización.
  3. La función autoriza la factura y el tenant solicitados en el servidor; nunca dependas de un ID de factura o de un estado de interfaz oculto.
  4. La función lee la clave privada del proveedor desde la configuración del servidor.
  5. Llama a la API de procesamiento de documentos y devuelve únicamente el resultado permitido.
React client → Blackhawk-controlled serverless function → third-party invoice API
                                                                    ↑
                                                             private API key

[IMAGE PLACEHOLDER: React client sends invoices to a Blackhawk-controlled serverless function, which alone holds the secret and calls the third-party invoice API]

La función sigue necesitando límites de solicitudes, validación de entradas, autorización y un tratamiento cuidadoso de los errores. Tu proxy también necesita esos controles; reenviar cada solicitud a todo el mundo produce una filtración costosa.

Antes del lanzamiento, inspecciona los bundles de JavaScript y los source maps, la configuración pública generada, las solicitudes de red del navegador y el almacenamiento. Comprueba también las respuestas de error y los hostnames visibles para el cliente. Omite los source maps de producción o restringe su acceso de acuerdo con tu política de depuración. En ambos casos, comprueba que no contengan credenciales.

Un identificador de cliente público puede estar diseñado para exponerse; asígnale únicamente los privilegios previstos para clientes públicos.

Persiste estados inofensivos; mantén los bearer tokens fuera de localStorage

localStorage sobrevive a las recargas, lo que lo hace conveniente. Cualquier JavaScript que se ejecute en la página puede leer un token almacenado allí; una dependencia comprometida es una forma en que ese código podría llegar a ejecutarse.

El almacenamiento solo en memoria elimina la copia persistida después de una recarga; no protege una página activa frente a XSS.

Una sesión basada en cookies puede encajar mejor cuando el servidor controla el diseño. HttpOnly impide que el JavaScript de la página lea directamente la cookie, mientras que Secure restringe la transmisión a HTTPS. Aun así necesitas autorización del lado del servidor y defensas CSRF apropiadas para el diseño de la cookie y de las solicitudes.

Usa esta regla de decisión:

DatosDecisión de lanzamiento
Tema, avisos descartados, preferencias no sensiblesLa persistencia suele ser aceptable
Datos de cachéPersiste únicamente datos que estarías dispuesto a revelar a alguien que pueda leer el perfil del navegador; excluye credenciales y contenidos sensibles de facturas
Tokens de acceso o refreshNo los persistas de forma predeterminada; prefiere una sesión gestionada por el servidor o un flujo cuidadosamente diseñado que solo use memoria
Contenido de facturas y borradores sensiblesPersiste solo después de definir una decisión sobre retención y protección
Árbol completo de Redux o del estado del clienteNo lo serialices automáticamente

Inspecciona la configuración de persistencia en busca de un reducer raíz o una ruta de estado serializado que incluya credenciales, datos de facturas o detalles de usuarios. Revisa Sentry, Crashlytics, logs, analytics y payloads de errores. Busca headers de autorización, tokens, contenido de facturas e información personal.

Esta guía puede decirte qué inspeccionar, pero no puede certificar la autorización de tu API ni la configuración de tu proveedor de identidad desde el frontend. Prueba esos flujos contra el sistema desplegado. Las capturas de pantalla y un informe limpio del scanner son evidencias débiles.

Para el dashboard de facturas, inspecciona el almacenamiento después del inicio de sesión, sube una factura en el artefacto de producción, provoca un error controlado y revisa el payload de telemetría.

OAuth necesita PKCE y un estado de autenticación validado

Para un frontend de React que use OAuth, verifica el flujo de código de autorización con Proof Key for Code Exchange (PKCE).

La secuencia es:

  1. El cliente genera un code_verifier aleatorio.
  2. Deriva un desafío de código S256 aplicando SHA-256 al verifier y codificando el resultado según lo exige el protocolo.
  3. Envía el desafío con la solicitud de autorización.
  4. El proveedor de identidad devuelve un código de autorización.
  5. El cliente envía el verifier original durante el intercambio de tokens.
  6. El proveedor de identidad compara el verifier con el desafío almacenado.

Exige S256 y rechaza los métodos de desafío de código ausentes o más débiles cuando tu proveedor admita esa política. Un código de autorización robado es inútil sin el verifier.

Usa URI de redirección HTTPS y direcciones registradas exactas. Valida state para la protección CSRF del flujo de autorización. Para OpenID Connect, valida primero el nonce. Después comprueba la firma, el issuer y la audiencia del ID token. La validación del código de autorización de OAuth se centra en el cliente, la URI de redirección, state y el flujo del token endpoint; las comprobaciones de OpenID Connect se aplican cuando validas un token de identidad.

La guía de seguridad de React Native advierte que los deep links móviles no son seguros porque los esquemas de URL carecen de registro centralizado y otra aplicación puede reclamar el mismo esquema. Esa salvedad corresponde a los despliegues móviles. El control relevante para la web es PKCE: una redirección transporta la respuesta de autorización; tu cliente y tu servidor deben validar el estado de autenticación resultante.

Aplica parches al límite de servidor de React y revisa npm como entrada ejecutable

No aprobamos un build simplemente porque npm audit esté limpio. Consulta nuestra guía para asegurar una aplicación Django antes de producción. El resultado de audit es una señal; el lockfile, el comportamiento de instalación y el límite del framework aún requieren revisión. Un badge verde de npm audit es un adorno conveniente del dashboard; no es una decisión de lanzamiento.

Comprueba los paquetes que definen la ejecución: react y react-dom, Next.js u otro framework de React, paquetes de RSC, Server Functions e integraciones de servidor relacionadas, paquetes de autenticación y paquetes específicos de la aplicación. Compáralos con los avisos actuales de la página de seguridad de React.

La oleada de avisos de 2025–2026 incluye un problema crítico de RSC y varios problemas de denegación de servicio de alta gravedad. Si tu despliegue publica únicamente un bundle del lado del cliente, los avisos de RSC y Server Functions enumerados generalmente no describen ese bundle del navegador; verifica si tu framework sigue ejecutando componentes de servidor afectados en producción.

Las notas de investigación describen las dos vulnerabilidades de React de Sherlock en OSV el 15 de agosto de 2026 como una vista de vulnerabilidades activas. Usa esa cifra como una señal separada del total histórico de avisos publicados por GitHub, que incluye problemas corregidos.

safeguard.sh cita incidentes relacionados con ua-parser-js, coa/rc y un gusano de npm que afectó a 500 paquetes. Trátalos como ejemplos de cadena de suministro. Convierte la lección en controles. Confirma y revisa el lockfile, y después instala a partir de él en CI. Elimina paquetes no utilizados e inspecciona los scripts de instalación y las adiciones desconocidas. Audita las dependencias directas y transitivas. Investiga los hallazgos de alta gravedad en lugar de suprimirlos automáticamente. Documenta un control compensatorio cuando no pueda aplicarse una corrección antes del lanzamiento.

Una vulnerabilidad del framework y una vulnerabilidad del núcleo de React son clasificaciones separadas. El proceso de lanzamiento debe detectar ambas.

La parte más difícil de verificar mediante una revisión del frontend es la autorización del lado del proveedor; necesitarás pruebas del sistema desplegado para eso, y ningún escaneo limpio del bundle puede sustituirlas.

CSP limita el comportamiento del navegador después de publicar el código

A menudo se añade CSP antes de corregir la filtración subyacente. Ese orden produce headers tranquilizadores sin reparar la exposición.

Captura los headers de respuesta desplegados. Revisa la aplicación de HTTPS, la protección contra framing cuando corresponda y el tratamiento del tipo de contenido. Comprueba la política de source maps por separado. Construye CSP a partir de los orígenes reales de scripts, conexiones, imágenes, fuentes y frames de la aplicación. Ejecútala primero en modo report-only, conserva el informe de infracciones y registra la resolución de cada infracción legítima antes de aplicar la política.

Una CSP universal copiada es un artefacto de lanzamiento deficiente. Puede romper un despliegue legítimo o permitir más de lo que necesita la aplicación.

CSP añade una capa de control del lado del navegador. No puede eliminar una clave privada de un bundle, reparar la autorización de la API, sanear HTML inseguro ni hacer confiable una dependencia comprometida. Corrige primero esos fallos y después añade CSP como defensa en profundidad.

Usa este criterio de lanzamiento antes de producción

Detén el lanzamiento inmediatamente cuando:

  • una credencial privada o un bearer token sea visible para el cliente;
  • un sink de alto impacto de HTML o ejecución de código carezca de una razón, una política y una prueba;
  • OAuth carezca de PKCE cuando el flujo lo requiera;
  • un aviso aplicable de React, RSC, Server Functions o del framework, de alta gravedad, siga sin resolverse y no exista un control compensatorio documentado.

Para cualquier otro elemento, asigna un responsable identificado y exige evidencias concretas:

  • Código: resultados de la búsqueda en el repositorio, revisión de sinks inseguros y pruebas de esquemas de URL. Incluye pruebas de autorización del servidor.
  • Secretos y datos: inspección de bundles y source maps, inventario de almacenamiento, traza de red y prueba de redacción de telemetría.
  • Autenticación: pruebas desplegadas de inicio de sesión, cierre de sesión, caducidad, refresh, redirección, state y PKCE.
  • Dependencias y entrega: diff revisado del lockfile, instalación del lockfile en CI, resultado de audit y revisión de scripts de paquetes. Registra también la resolución del aviso, los headers desplegados, la prueba de redirección HTTPS, los resultados de CSP en modo report-only y la decisión sobre el acceso a source maps.

Adjunta el escaneo del bundle, la revisión del almacenamiento y la telemetría, las pruebas de PKCE y autenticación, la resolución del lockfile y la captura de los headers desplegados al lanzamiento. Si un bloqueador carece de evidencia, mantén el build fuera de producción.