Pregunta 1¿Por qué se valida también en el servidor, si los valores del formulario ya se comprueban en la pantalla?
Qué tener en cuenta en seguridad — no confiar en la entrada y proteger los secretos
Este artículo forma parte del curso Fundamentos de informática, que construye desde cero los conocimientos prácticos de informática que necesitas como mínimo para programar y hacer vibe coding.
Hay dos puntos básicos: no confiar en lo que introduce el usuario y proteger la información secreta. Con diagramas comprobamos cómo se producen la inyección SQL y el XSS.
Este artículo trata dos de los puntos que hay que tener en cuenta en seguridad.
Uno es no usar tal cual el texto que escribe el usuario y el otro es no poner valores secretos en lugares donde más gente puede leerlos.
- Por qué la validación de la entrada se hace en el servidor
- La inyección SQL, que se produce al unir la entrada a una sentencia, y los marcadores de posición
- El XSS, que se evita con el escapado justo antes de mostrar el texto en la pantalla
- Dónde puedes guardar la información secreta, como las claves de API y las contraseñas
Cualquiera de estas cosas, si ocurre después de publicar, no se puede deshacer, y los datos que ya se leyeron no se recuperan.
«No hay validación de la entrada» significa usar el valor tal como llega
La validación de la entrada (input validation) consiste en comprobar, antes de usar un valor que ha llegado, que tiene el formato, la longitud y el rango previstos.
Para cada campo fijas una condición: el nombre, hasta 50 caracteres; el número de estrellas, de 1 a 5.
| Valor que llega | Condición fijada de antemano | Lo que hace el servidor |
|---|---|---|
| Campo nombre: Ana | Hasta 50 caracteres → encaja | Pasa a guardarse |
| Estrellas: 7 | Un número de 1 a 5 → no encaja | Lo rechaza sin guardarlo |
| Texto: vacío | Un carácter o más → no encaja | Lo rechaza sin guardarlo |
Solo la primera fila cumple la condición y pasa a guardarse; las otras dos no se guardan y el servidor devuelve qué parte no encaja.
El control de la pantalla lo ejecuta el JavaScript (un programa que se ejecuta dentro del navegador) que llegó al dispositivo del usuario.
El usuario puede quitarlo y también puede enviar los datos directamente al servidor sin usar un navegador.
El control de la pantalla está para que el usuario vuelva a escribir la entrada, y lo único que decide si un valor se puede usar es la validación del servidor.
Si guardas sin validar, los valores que no cumplen las condiciones se quedan tal cual en la base de datos.
Un número de estrellas fuera de 1 a 5 descuadra las medias y el orden, y un valor que no se puede leer como número detiene el cálculo de los totales.
Para arreglar los valores que ya han entrado hay que revisar uno a uno los datos que quedan.
Comprueba en el servidor el valor que llega, antes de usarlo
La validación de la entrada consiste en mirar, antes de usar un valor que ha llegado, si cumple las condiciones que fijaste.
El control de la pantalla está para que el usuario reescriba la entrada en el momento y se puede quitar antes de enviar, así que compruebas lo mismo otra vez en el servidor.
Si unes la entrada a una sentencia, el texto escrito se ejecuta como una orden
Cuando busca por nombre las reseñas guardadas, el servidor arma una sentencia en SQL (Structured Query Language, el lenguaje con el que se dan órdenes a una base de datos).
Al unir el texto de la entrada a esa cadena, el conjunto se pasa como una sola.
- SELECT * FROM reviews WHERE name =
- Una orden para sacar de la tabla reviews las filas cuyo name coincide
- Esta forma ya está escrita de antemano en el código
- Ana — un nombre corriente
- ' OR '1'='1 — texto con símbolos
- Lo que escribió el usuario entra tal cual en esta posición
Una vez que ha pasado, la parte que escribió el desarrollador y la que vino del campo del nombre son partes de una misma cadena.
La base de datos lee entera, como una orden, la cadena que recibe.
| Texto puesto en el campo del nombre | Sentencia que resulta de unirlos | Lo que devuelve la base de datos |
|---|---|---|
| Ana | name = 'Ana' | Solo las reseñas de Ana |
| Ana' | name = 'Ana'' | La sentencia se rompe y da error |
| ' OR '1'='1 | name = '' OR '1'='1' | Todas las reseñas |
Que el texto introducido se ejecute como una orden en lugar de como un valor es una inyección (injection), y la que se produce en la base de datos es la inyección SQL (SQL injection).
La forma de escribirlo que lo evita es el marcador de posición (placeholder, una forma de escribir en la que dentro de la sentencia solo se marca con un símbolo la posición donde va el valor y el valor se pasa aparte).
Si se pasan por separado, la base de datos no lee el valor como una orden: busca ese texto como nombre y termina en 0 filas.
La validación comprueba el formato del valor y el marcador de posición hace que el valor se trate como valor sea cual sea su formato.
Como cumplen funciones distintas, se hacen las dos cosas.
No unas la sentencia y el texto de la entrada en una sola cadena
Al unir el texto de la entrada a una sentencia, hasta los símbolos escritos se leen como parte de la orden.
Si pasas la orden y el valor por separado, el texto con símbolos se trata como un simple nombre, así que mantienes las dos cosas: la validación que comprueba el formato y la forma de escribir que hace que el valor se trate como valor sea cual sea su formato.
Si no lo cambias justo antes de mostrarlo, lo publicado se ejecuta en el navegador de otro usuario
Lo mismo ocurre en el punto donde lo publicado sale a la pantalla.
Que el texto introducido se ejecute como JavaScript en el navegador de otro usuario es el XSS (Cross-Site Scripting).
En los dos casos la entrada se lee como una orden y lo único que cambia es quién la lee.
La solución del lado de la pantalla es el escapado (escape, convertir los caracteres que tienen un significado especial a una forma que se muestra como caracteres corrientes).
El punto que lo separa no está en el momento de guardar, sino justo antes de mostrarlo en la pantalla.
| Carácter con significado especial en HTML | Cómo se escribe tras la sustitución | Carácter que se ve en la pantalla |
|---|---|---|
| < | < | < |
| > | > | > |
| & | & | & |
| " | " | " |
| ' | ' | ' |
Si lo sustituyes justo antes de mostrarlo, lo publicado se queda en texto
Si sacas a la pantalla lo publicado tal cual, lo que llevaba mezclado dentro se ejecuta en el navegador de otro usuario.
Lo que detiene esto es la sustitución que se hace justo antes de mostrarlo y, como solo se sustituyen los símbolos, el texto que ve quien lee no cambia.
Los valores secretos no pasan al punto donde más gente puede leerlos
El otro punto es dónde se guarda la información secreta.
Lo que se mira aquí es cuánta gente puede leer el sitio donde la pusiste.
- .env — los valores de las claves de API y las contraseñas
- Archivos de configuración que solo usas en tu computadora
- server.js — el código que se ejecuta en el servidor
- .gitignore — la lista de archivos que no se registran
- .env.example — un modelo con solo los nombres
- index.html — la pantalla de las reseñas
- script.js — el código que se ejecuta en la pantalla
Cuanto más adentro está el marco, más gente puede leerlo: script.js lo lee cualquiera y server.js, en cuanto se registra en Git, lo lee todo el que reciba el código.
Si escribes .env en .gitignore (un archivo que enumera los nombres de los archivos que Git no debe registrar), los valores no entran en el historial, pero
una vez registrados ya no se borran del historial pasado, así que se vuelven a generar los valores mismos.
Un valor que ha entrado en un repositorio público lo puede leer cualquiera en el mundo.
- script.js — el código que llega al navegador
- El final de la URL a la que llamas
- Los mensajes de error que salen en la pantalla
- server.js — el código que se registra en Git
- .env una vez registrado — queda en el historial
- Las variables de entorno del servidor — aquí van las claves de API y las contraseñas
Lo único que entra en el círculo más interior son las variables de entorno del servidor.
No se escriben en los sitios de los dos círculos exteriores ni en los logs (el registro de la actividad que deja el servidor), que a veces se sacan fuera.
El my-app que se ejecuta dentro del recuadro de la derecha carga el valor de las variables de entorno.
Ese valor no entra en los dos recuadros de la izquierda, así que no aumenta la gente que puede leerlo.
Dónde guardarlo lo decide cuánta gente puede leerlo
El sitio donde puedes poner un valor secreto lo decide cuánta gente puede leer ese sitio.
Un archivo que llega al navegador lo lee cualquiera y un archivo registrado en Git llega a todo el que reciba el código, así que no lo escribes en ninguno de los dos y lo pones en las variables de entorno del servidor.
Verificación de conocimientos
Responde cada pregunta una a una.
Pregunta 2¿Qué hace el marcador de posición que evita la inyección SQL?
Pregunta 3¿Cuál es el sitio correcto para guardar la clave de API de un servicio externo?