Forma correcta de proteger los hidden input

B

Estoy construyendo un CMS y me encuentro ocupado con la parte de editar los posts. Lo referente a la edición lo tengo ubicado dentro de un formulario quien se llamará a si mismo para procesar la solicitud. Para saber el id del post que estoy editando tengo como campo oculto su ID, pero claro, como el cliente puede acceder al código HTML podría alterar ese ID.

El método que llevo usando es el tema de los token. Crearme una función que empleado una clave privada del lado del servidor, encripte por ejemplo todo lo que le pase como parámetro, en este caso el ID del post. Sin embargo si utilizo este método, este token es válido para toda la historia de la navegación de mi web.

Si el ID lo encripto junto con un timestamp, pues estoy en las mismas a no ser que verifique por ejemplo cuánto hace que se generó el token y si se hizo por ejemplo hace 5 minutos invalidarlo. Sin embargo es una solución que no me termina mucho por convencer.

Luego he estado mirando el caso que se utiliza aquí en MV. Cada vez que refresco la web se genera un token nuevo y el antiguo sigue siendo válido para editar por ejemplo el comentario. ¿Cómo funciona el sistema en este caso? ¿Hace uso de variables de sesión? Por ejemplo, tener

$_SESSION["formulario_enviar_post_tokens"] = array(
 array("jrhebf3084fh43ufhbjwfd", "efhjo39f7yg3refbhdjlaasuydb3yb23", "fehwbfhjwebfo2ygbd2wef")
);

¿Tendría que guardar en un array todos los tokens que genero? Es que si guardo solo uno, si abro varias pestañas, machaco el anterior... ¿Guardar todo esto no consumirá muchos recursos?

No sé, a ver que me comentáis.

EDIT: Otra pregunta. ¿Qué diferencia hay entre tokens y nonce?

Dostoievski

A mí me parece más sencillo un sistema de permisos la verdad :P en plan algo así (ejemplo rapido de como lo hace la gema cancan de ruby, las restricciones están definidas en la clase Ability)

ability = Ability.new(current_user)

if ability .can? :edit, post
  post.update(params[:post])
else 
  lanzas excepción
end
Kiroushi

No es más fácil que hagas una llamada a una URL que contenga el ID del post?

/post/3/edit

Si haces petición GET, te muestra el formulario, y si haces petición POST PUT te hace update.

De todas formas, si estás pensando en proteger tu base de datos a nivel de frontend es que estás fallando supinamente en el diseño de acceso a datos.

Básicamente tienes que leer a nivel de BBDD el author_id o como quiera que hayas llamado a la columna que identifique al user que lo creó, y hacer un:

if ($user->id !== $post->id) {
abort()
}

Esta es la aproximación. Déjate de historias de tokens, que eso sólo sirve para evitar CSRF.

2 respuestas
B

#3 Lo que comentas de las restricciones de los datos ya las tengo controladas y lo cierto es que no debería haberme preocupado más por la seguridad puesto que todos los accesos y acciones están comprobados, pero la historia vino de lo siguiente:

Tengo dos roles para el panel de administración: autor y administradores. El autor solo puede tocar lo que es suyo y los administradores como es lógico pueden editar todo. Como comentaba, todo esto está controlado a nivel de datos. Pero me puse a pensar si, el administrador que puede editar todo, se encuentra editando el post con id 1, modifica los input hidden para que sea un 2. A fin de cuentas no pasa nada, porque puede editar todos los contenidos pero a lo que voy es que el servidor no le ha dado autorización en esa pantalla para hacerlo. Pero este supuesto creo que no está tan ligado con la seguridad sino más bien con la navegación pero vamos, creo que voy a pasar del tema para no complicarlo más.

2 respuestas
Kiroushi

#4 Lo que dices es razonable, pero eso es una cuestión más moral que de diseño. Si no te fías de un administrador, no le des rol de administrador.

Who watches the watchmen?

Añade una tabla user_logs, y en cada insert o edit guardas un registro adicional en esa tabla apuntando los cambios. Así sabrás si alguien está haciendo cambios indeseados.

Si aún así quieres seguir con esa mecánica, te vuelvo a repetir que la forma correcta es hacer la llamada a una URL que contenga el ID.

/post/3/edit

Y si quieres asegurarte que nadie cambia el destino del post para que pasen cosas raras, mete un hidden input cuyo valor sea el ID del post mezclado con un hash específico de la aplicación, que luego tú a nivel de comprobación desempaquetas y ves si coinciden.

De esta forma, en la URL tienes la ID, y en el hidden tienes una cadena random de caracteres que nadie va a poder modificar de manera aleatoria para que coincida con un cambio de URL.

Un saludo.

Dostoievski

#4 Pero digo yo que si el administrador quiere editar el post id=2, se irá a ese post y lo editará. Que sentido tiene irse al html, cambiar el Id y enviarlo cuando puede hacer lo mismo navegando por la web? xD

Es que es algo que es cuestión de permisos. El usuario tiene permiso para editar X? Lo edita.

1 respuesta
B

#6 Sentido no tiene ninguno porque como bien dices se puede hacer navegando sin embargo es una posibilidad de edición. Es un tanto extraña porque a parte de no ser maliciosa no creo que nadie se ponga ahí a editar pero es una alternativa que existe. Simplemente se me ocurrió ayer la estúpida idea de bloquearla. xD Que claro, toda esta ralladura me viene de que estuve leyendo un artículo de la protección de los datos en los formulario y de ver cómo trabajaba wordpress que metían tokens a cualquier acción. Entonces de ahí pensé que a lo mejor sería conveniente meter tokens pero como dijo #3, solo sirve para CSRF.

1 respuesta
Kiroushi

#7 Meten tokens porque imagina que tienes una URL que edita un recurso, o lo borra:

http://dominio.com/posts/3/delete

Si no tienes un input con el que verificar que el request viene de un formulario de tu web (intencionado), bastaría con que alguien insertara una imagen con src="http://dominio.com/posts/3/delete" para que el navegador lanzara una petición get a esa dirección y se cargara el registro.

Creo que esto es un motivo adicional para utilizar mecánica CRUD con GET/POST/PUT/DELETE.

2 respuestas
JuAn4k4

Tu ponle el id en hidden o en la url, y luego comprueba que tiene permisos para modificar ese id, si lo tiene, que haga lo que quiera con él que para eso es suyo.

#8 El PUT y el DELETE no estan soportados en muchos browsers.

B

Vale, muchas gracias a todos. #8 El ejemplo que me pusiste me ayudó ahora a ver un caso realista del problema ese porque leí mucho pero no ponían un ejemplo tan claro como el que me pusiste.

Usuarios habituales