Seguridad y privacidad en formularios HTML | Nicolás Garzón
no confiable
Texto
Copiar HTML constraints → experiencia del usuario
servidor → validación, autorización y seguridad
Un usuario o script puede:
Quitar required.
Cambiar min, max o pattern.
Editar hidden inputs.
Llamar el endpoint directamente.
Cambiar IDs, precios o roles.
El servidor nunca debe confiar en el markup como control de seguridad.
HTML
Copiar < form action = " https://example.com/login" method = " post" > Datos personales, credenciales y tokens deben viajar por HTTPS.
Action accidentalmente HTTP.
Recursos mixtos.
Redirects a conexiones inseguras.
Cookies con Secure.
HTTPS protege transporte, no valida que el servidor sea legítimo ni corrige XSS.
HTML
Copiar < input
type = " password"
name = " password"
autocomplete = " current-password"
>
Guardes la contraseña en localStorage.
La registres en analytics o logs.
Repobles el value desde el servidor.
Impidas pegar o usar password managers.
Envíes por query string.
El servidor debe aplicar hashing de contraseñas con algoritmos apropiados; HTML no resuelve almacenamiento.
HTML
Copiar < form method = " get" >
< input name = " password" >
</ form> Coloca el valor en URL, historial, logs, referrers y capturas. Nunca utilices GET para credenciales o datos privados.
POST tampoco vuelve el dato secreto sin HTTPS y controles de almacenamiento.
HTML
Copiar < input type = " hidden" name = " role" value = " admin" > No es una autorización. El servidor debe determinar el rol desde la identidad autenticada.
Un hidden puede transportar un token CSRF o valor firmado, pero siempre debe verificarse.
Cuando las credenciales se envían automáticamente mediante cookies, otro sitio puede intentar provocar una acción.
Cookies SameSite adecuadas.
Token CSRF.
Comprobación de Origin/Referer según política.
Reautenticación para acciones críticas.
No usar GET para efectos.
HTML
Copiar < input type = " hidden" name = " csrfToken" value = " token-generado-por-servidor" > El token debe estar ligado a una sesión o estrategia verificable; un valor fijo no protege.
Peligroso si se concatena sin encoding:
HTML
Copiar < input value = " ENTRADA_DEL_USUARIO" > El servidor/template debe codificar para contexto de atributo. Para textarea, codifica para contenido HTML.
No existe una sanitización universal para atributo, HTML, URL y JavaScript.
HTML
Copiar < input type = " hidden" name = " next" value = " https://external.example" > Si el servidor redirige sin validar, un atacante puede crear links que terminan en phishing.
Permite rutas internas o una lista de destinos aprobados.
HTML
Copiar < input type = " file" name = " attachment" accept = " application/pdf" > El servidor debe comprobar:
Tamaño máximo.
Firma/magic bytes.
Tipo permitido.
Nombre generado seguro.
Almacenamiento fuera de rutas ejecutables.
Malware cuando el riesgo lo requiere.
Permisos de acceso.
Límites de cantidad y tiempo.
No confíes en extensión, MIME del navegador ni accept.
Un nombre puede contener rutas, caracteres especiales o colisiones.
No lo uses directamente como path. Genera un identificador propio y conserva el nombre original solo como metadata codificada.
No recibas datos de tarjeta directamente si un proveedor puede tokenizarlos mediante componentes y cumplimiento apropiado.
No incluyas PAN, CVV o tokens sensibles en logs, HTML de error o analytics.
Tokens correctos ayudan a password managers y usuarios. Desactivar autocompletado no evita que el navegador o extensiones almacenen información y puede empeorar seguridad al fomentar contraseñas débiles.
Para campos realmente sensibles y efímeros, aplica el token adecuado, por ejemplo one-time-code, y sigue las recomendaciones del flujo.
Un campo visualmente oculto puede detectar bots simples, pero:
Puede afectar autocompletado.
Bots modernos lo evitan.
No sustituye rate limiting.
Debe ocultarse sin confundir tecnologías de asistencia.
Utilízalo como señal secundaria, no como única defensa.
Spam.
Credential stuffing.
Envíos automatizados.
Uploads masivos.
El servidor necesita límites, detección, colas y observabilidad apropiados.
Pregunta si realmente necesitas cada dato.
Menor fricción.
Menor impacto ante filtración.
Menos obligaciones de almacenamiento.
Mejor claridad del formulario.
No pidas fecha de nacimiento, teléfono o dirección “por si acaso”.
Texto
Copiar Correo o contraseña incorrectospuede evitar revelar si una cuenta existe. El nivel de detalle depende del riesgo y la experiencia.
No expongas stack traces, queries, tokens o IDs internos.
Un checkbox preseleccionado no representa consentimiento claro para usos opcionales.
HTML
Copiar < label>
< input type = " checkbox" name = " marketingConsent" value = " yes" >
Quiero recibir comunicaciones de marketing.
</ label> Separa aceptación necesaria de términos y consentimientos opcionales. Registra versión, fecha y base legal según requisitos aplicables.
HTML
Copiar < form action = " /account/delete" method = " post" >
< input type = " hidden" name = " csrfToken" value = " ..." >
< p> Esta acción eliminará permanentemente tu cuenta.</ p>
< label for = " confirmation" > Escribe ELIMINAR para confirmar</ label>
< input id = " confirmation" name = " confirmation" required >
< button type = " submit" > Eliminar cuenta</ button>
</ form> El servidor verifica identidad, CSRF, permiso y texto de confirmación.
Confiar en constraints del cliente.
Enviar secretos mediante GET.
Usar hidden como autorización.
Crear tokens CSRF fijos.
Repoblar passwords.
Confiar en accept o extensión de archivos.
Usar filenames como paths.
Registrar form data completo.
Desactivar password managers.
Pedir datos innecesarios.
Preseleccionar consentimiento opcional.
Mostrar errores internos al usuario.
Todo input cliente es no confiable.
HTTPS protege transporte.
Hidden y readonly no son barreras.
CSRF necesita verificación de servidor.
Los valores repoblados requieren encoding contextual.
Uploads necesitan validación fuerte y límites.
Autocomplete bien utilizado puede mejorar seguridad.
Minimizar datos reduce riesgo.
Autorización, rate limiting y almacenamiento pertenecen al servidor.
¿Por qué un <input type="hidden" name="price" value="100"> no debe definir el precio cobrado?
Respuesta Porque el usuario puede modificarlo antes de enviar. El servidor debe obtener el precio desde una fuente confiable y aplicar las reglas comerciales vigentes.
Semántica nativa antes de ARIA inicia el bloque de accesibilidad general.