¿Por qué una solicitud GET no debería cambiar los datos en el servidor?

109

En todo Internet, veo el siguiente consejo:

Un GET nunca debe cambiar los datos en el servidor; use una solicitud POST para eso

¿Cuál es la base de esta idea?

Si hago un servicio php que inserta datos en la base de datos y le paso los parámetros en la cadena de consulta GET, ¿por qué eso es incorrecto? (Estoy usando declaraciones preparadas, para encargarme de la inyección SQL). ¿Es una solicitud POST de alguna manera más segura?

¿O hay alguna razón histórica para esto? Si es así, ¿qué tan válido es este consejo hoy?

Devdatta Tengshe
fuente
Gracias por hacer esta pregunta, y gracias @Oded por la respuesta bien redactada Siempre necesité una referencia para enviar a las personas que hacen esta pregunta hacia :)
Benjamin Gruenbaum
También vea HTTP PUT - stackoverflow.com/questions/630453/put-vs-post-in-rest (con notas sobre ser idempotente)
Bratch
2
@JoachimSauer Si bien GET los habría salvado del rastreador, el problema raíz era la falta de autenticación. Cualquier script kiddy podría haberlos PUBLICADO también en el olvido.
CodesInChaos

Respuestas:

185

Esto no es un consejo.

A GETse define de esta manera en el protocolo HTTP . Se supone que es idempotente y seguro .

En cuanto a por qué, GETse puede almacenar en caché y actualizar en un navegador. Una y otra vez.

Esto significa que si se hace el mismo GETde nuevo, se va a insertar en la base de datos de nuevo .

Considere lo que esto puede significar si se GETconvierte en un enlace y un motor de búsqueda lo rastrea. Tendrá su base de datos llena de datos duplicados.

También sugiero leer los URI, la capacidad de direccionamiento y el uso de HTTP GET y POST .


También hay un problema con la captación previa de enlaces en algunos navegadores: realizarán una llamada para buscar enlaces, incluso si el autor de la página no lo indica.

Si, por ejemplo, su cierre de sesión está detrás de un "GET", vinculado desde cada página de su sitio, las personas pueden cerrar sesión solo debido a este comportamiento.

Oded
fuente
35
Muchas, muchas, muchas herramientas, utilidades, rastreadores web y otras cosas que muchos suponen que GETnunca será una acción destructiva (con razón, ya que se especifica de esta manera). Si ahora rompe su aplicación al romper esa especificación, podrá conservar ambas partes de su aplicación.
Joachim Sauer
77
@NimChimpsky: se cambia por a GET. Ese consejo es simplemente incorrecto. Seguro significa que el usuario no puede ser considerado responsable de los efectos secundarios, no que no puede haber efectos secundarios. De lo contrario, no podría tener archivos de registro para su servidor, ¡lo cual sería absurdo! Esto se explica claramente en la sección 9.1.1 de RFC2616.
Jörg W Mittag
8
@ JörgWMittag: No diría "simplemente incorrecto", diría "redactado incorrectamente". Un GET no debería tener un cambio, ya que es el objetivo. Por supuesto , puede contar, registrar y observar una solicitud GET. Pero no debe modificar sus datos comerciales reales.
Joachim Sauer
23
@NimChimpsky A GETno debería cambiar el recurso solicitado por el GET, pero eso no significa 'nada en el servidor debería cambiar'. Por supuesto, cosas como registros, contadores y otros estados del servidor pueden cambiar durante cualquier solicitud.
Eric King
8
Hace algunos años, Google lanzó un complemento de navegador (iirc) que buscaría previamente páginas a través de enlaces. Esto también sucedió en algunos paneles de control que se diseñaron mal: las URL provocarían que se escribiera o incluso se borrara un registro o algo en el servidor (piense en post? Action = delete). Esto provocó la ejecución de acciones sin que el usuario lo supiera. Google suspendió ese complemento por esa razón, iirc, incluso si fue culpa del fabricante de la aplicación web el uso de GET para cambiar de estado.
Cthulhu
24

Cada verbo HTTP tiene su propia responsabilidad. Por ejemplo GET, según lo definido por RFC

significa recuperar cualquier información (en forma de entidad) identificada por el URI de solicitud.

POST, por otro lado, significa insertar o más formalmente

El método POST se utiliza para solicitar que el servidor de origen acepte la
entidad incluida en la solicitud como un nuevo subordinado del recurso
identificado por el URI de solicitud en la línea de solicitud

Razones para mantenerlo de esta manera:

  • Es muy simple y funciona en la escala global de Internet desde 1991
  • Cumplir con el principio de responsabilidad única
  • Otras partes utilizan GETpara actuar como medios de recuperación de información y minería de datos
  • Se supone que GET es una operación segura que nunca modifica el estado del recurso
  • Consideraciones de seguridad, GETes efectivamente una lectura , mientras POSTque efectivamente es una escritura
  • GET es almacenado en caché por navegadores, nodos en la red, proveedores de servicios de Internet
  • A menos que el contenido cambie, GETa la misma URL debe devolver los mismos resultados a todos los usuarios o, de lo contrario, no tendrá ninguna confianza en el resultado devuelto

Para completar y solo para aplicar el uso correcto (fuente) :

  • GETlos parámetros se pasan como parte de la URL, que tiene una longitud pequeña y limitada de 256 caracteres de forma predeterminada, y algunos servidores admiten más de 4000 caracteres. Si desea insertar un registro largo, no hay una forma legítima de pasar estos datos en
  • Cuando se utiliza Conexión segura, ̶ tales como TLS, ̶ URL es no conseguir cifrada, ̶ por lo tanto, todos los parámetros de ̶ ̶G̶E̶T̶̶ se transfieren texto sin formato. La URL está actualmente encriptada con TLS, por lo que TLS está bien.
  • Insertar datos binarios o caracteres no ASCII usando no GETes práctico
  • GET se vuelve a ejecutar si un usuario presiona el botón Atrás en un navegador
  • Es posible que algunos rastreadores antiguos no indexen las URL con un ?signo dentro
oleksii
fuente
1
¿Estás seguro de que la URL no está encriptada sobre TLS? Tenía la impresión de que los apretones de manos SSL / TLS se producen antes de que se transfieran los encabezados HTTP. Esta es la razón por la cual el alojamiento virtual de sitios HTTPS en una sola dirección IP es difícil. ¿Estoy equivocado?
Brandon
Así es, lo arreglé
oleksii
2
@Brandon Los navegadores modernos envían el dominio del host de forma transparente como parte del protocolo de enlace TLS (conocido como indicación del nombre del servidor), para permitir alojar más de un dominio por dirección IP. La parte de ruta / consulta de la url está protegida por TLS. No hay diferencia entre GET y otros verbos HTTP en ese sentido.
CodesInChaos
9

EDITAR: Antes, dije POST ayuda a protegerlo contra CSRF pero esto está mal. No pensé esto correctamente. Debe requerir un token oculto único de alcance de sesión en todas sus solicitudes para cambiar los datos para protegerse contra CSRF.

En los primeros días de internet había aceleradores de navegador. Estos programas comenzarían a hacer clic en los enlaces de una página para almacenar en caché el contenido. Google Web Accelerator fue uno de estos programas. Esto podría causar estragos en una aplicación que realiza cambios cuando se hace clic en un enlace. Supongo que todavía hay personas que usan software acelerador.

Los servidores y navegadores proxy almacenarán en caché las solicitudes GET, por lo que cuando el usuario acceda a la página nuevamente, es posible que no envíe la solicitud a su aplicación, por lo que el usuario cree que tomó una acción, pero realmente no lo hizo.

Sarel Botha
fuente
1
CSRF es igualmente posible con GET y POST. Por ejemplo, el atacante puede incluir un formulario de envío automático en su sitio para activar una solicitud POST. El enfoque estándar para prevenir CSRF incluye explícitamente un valor desconocido para el atacante en la solicitud (a diferencia de lo que implícitamente incluye encabezados de cookies).
CodesInChaos
8

Si hago un servicio php que inserta datos en la base de datos y le paso los parámetros en la cadena de consulta GET, ¿por qué eso es incorrecto?

La respuesta más simple es "porque eso no es lo que GETsignifica".

Usar GETpara pasar datos para una actualización es como escribir una carta de amor y enviarla en un sobre marcado "OFERTA ESPECIAL - ¡ACTÚE AHORA!" En ambos casos, no debe sorprenderse que el destinatario y / o los intermediarios manejen mal su mensaje .

Nathan Long
fuente
5

Para sus operaciones CRUD en una aplicación centrada en la base de datos, use el siguiente esquema:

Utilice HTTP GET para operaciones de lectura (SQL SELECT)

Usar HTTP PUT para operaciones de actualización (ACTUALIZACIÓN SQL)

Utilice HTTP POST para crear operaciones (SQL INSERT)

Use HTTP DELETE para eliminar operaciones (SQL DELETE)


fuente
3
Poner vs publicar no es como usted dice. Put es para cuando el cliente está modificando el recurso en la ubicación exacta dada. Para una publicación, el servidor finalmente decide la Uri exacta del recurso.
Andy
¿No es HTTP PUT más como un DELETE e INSERT de SQL en lugar de una ACTUALIZACIÓN? También SQL UPDATE puede actualizar muchos registros a la vez, pero HTTP PUT solo actualizará una cosa.
Backwards_Dave
0

Un GET nunca debe cambiar los datos en el servidor; use una solicitud POST para eso

Ese consejo, y todas las respuestas aquí son incorrectas. Obviamente estoy siendo demasiado dramático, las otras respuestas son excelentes, pero creo que el consejo exacto se debe dar como:

Un GET rara vez debe cambiar los datos en el servidor; use una solicitud POST para eso

Decir "nunca" es demasiado extremo, y aunque las otras respuestas aquí explican con precisión por qué "rara vez" debería hacerlo, hay algunos escenarios en los que es perfectamente razonable cambiar los datos con un GET. Un ejemplo es un enlace de verificación de correo electrónico de uso único. Por lo general, estos enlaces contienen un GUID que cuando se accede tendrá que cambiar los datos. Si se implementa correctamente, se ignorarán las solicitudes GET idénticas posteriores.

Obviamente, este es un caso extremo, pero ciertamente vale la pena señalarlo.

TTT
fuente
3
¿Qué sucede si su cliente de correo decide buscar el enlace sin que haga clic en él? Por ejemplo, porque quiere escanearlo en busca de malware. El enfoque adecuado para los enlaces de cancelación de suscripción es llevar a una página donde el usuario puede hacer clic en un botón para cancelar la suscripción (donde el clic del botón activa una solicitud POST).
CodesInChaos
@CodesInChaos - ¡excelente punto! Estoy de acuerdo contigo. He eliminado el ejemplo de cancelación de suscripción y he dejado la verificación por correo electrónico como único ejemplo. Puede haber otros además de la verificación por correo electrónico donde GET tiene sentido, pero no puedo pensar en ninguno en este momento.
TTT
El problema con que GET tenga efectos secundarios se aplica igualmente a la confirmación por correo electrónico. Ahora, el cliente que sigue el enlace confirmaría una cuenta que otra persona creó utilizando su correo electrónico, lo que le permitirá suplantarlo.
CodesInChaos
@CodesInChaos: ese es un tramo. La suplantación de la que habla vendría del mismo nombre de usuario o nombre público personal, no de la misma dirección de correo electrónico, y eso puede suceder independientemente de la dirección de correo electrónico que usen (por lo general, solo el servidor conoce la dirección de correo electrónico del titular de la cuenta). Además, no tendría sentido crear una cuenta con la dirección de correo electrónico de otra persona. ¿Cómo podría eso ayudarlos? No podían controlar su propia cuenta.
TTT