JSONP , o JSON-P (JSON con relleno), es una técnica histórica de JavaScript<script>
para solicitar datos mediante la carga de un elemento, [1] que es un elemento destinado a cargar JavaScript ordinario. Fue propuesto por Bob Ippolito en 2005. [2] JSONP permite compartir datos sin pasar por la política del mismo origen, que no permite ejecutar código JavaScript para leer elementos DOM de medios o datos XMLHttpRequest obtenidos desde fuera del sitio de origen de la página. El sitio de origen se indica mediante una combinación de esquema de URI , nombre de host y número de puerto .
JSONP es vulnerable a que la fuente de datos reemplace la llamada de función inocua con código malicioso, por lo que ha sido reemplazado por el uso compartido de recursos de origen cruzado (disponible desde 2009 [3] ) en las aplicaciones modernas.
En general, se permite que el <script>
elemento HTML ejecute código JavaScript recuperado de orígenes externos. Sin embargo, los servicios que responden con datos JSON puros no podían compartir datos de orígenes externos antes de la adopción de CORS ( intercambio de recursos entre orígenes ).
Por ejemplo, una solicitud a un servicio externo http://server.example.com/Users/1234
puede devolver un registro de una persona llamada Clem en formato JSON. La sintaxis de JSON es coherente con la sintaxis de objetos de JavaScript.
{ "Nombre" : "Clem" , "Id" : 1234 , "Rango" : 7 }
Sin soporte para CORS, un intento de utilizar los datos en diferentes dominios genera un error de JavaScript.
< script type = "application/javascript" src = "http://server.example.com/Users/1234" > </ script >
El navegador descargará el <script>
archivo, evaluará su contenido, malinterpretará los datos JSON sin procesar como un bloque y generará un error de sintaxis. Incluso si los datos se interpretaran como un literal de objeto de JavaScript, no se podría acceder a ellos mediante JavaScript que se ejecuta en el navegador, ya que sin una asignación de variable, los literales de objeto son inaccesibles.
En el patrón de uso de JSONP, la solicitud de URL a la que apunta el src
atributo en el <script>
elemento devuelve datos JSON, con código JavaScript (normalmente una llamada a una función) envuelto alrededor de ellos. Esta "carga útil envuelta" es luego interpretada por el navegador. De esta manera, una función que ya está definida en el entorno de JavaScript puede manipular los datos JSON. A continuación, se muestra una solicitud y una respuesta JSONP típicas.
La llamada de función a parseResponse() es la "P" de JSONP, el "relleno" o "prefijo" alrededor del JSON puro. [4] Para que JSONP funcione, un servidor debe responder con una respuesta que incluya la función JSONP. JSONP no funciona con resultados con formato JSON. La invocación de la función JSONP que se envía de vuelta y la carga útil que recibe la función deben ser acordadas por el cliente y el servidor. Por convención, el servidor que proporciona los datos JSON ofrece al sitio web solicitante que nombre la función JSONP, generalmente usando el nombre jsonp o callback como el parámetro de cadena de consulta nombrado, en su solicitud al servidor: <script src="http://server.example.com/Users/1234?callback=parseResponse"></script>
.
En este ejemplo, la carga útil recibida sería:
parseResponse ({ "Nombre" : "Clem" , "Id" : 1234 , "Rango" : 7 });
JSONP solo tiene sentido cuando se utiliza con un elemento de script. Para cada nueva solicitud JSONP, el navegador debe agregar un nuevo <script>
elemento o reutilizar uno existente. La primera opción (agregar un nuevo elemento de script) se realiza mediante manipulación dinámica del DOM y se conoce como inyección de elemento de script . El <script>
elemento se inyecta en el DOM HTML, con la URL del punto final JSONP deseado establecido como el atributo "src". Esta inyección dinámica de elementos de script generalmente se realiza mediante una biblioteca auxiliar de JavaScript. jQuery y otros marcos tienen funciones auxiliares de JSONP; también hay opciones independientes.
Un ejemplo de uso de jQuery para inyectar dinámicamente un elemento de script para una llamada JSONP se ve así:
$ . getScript ( "http://servidor.ejemplo.com/Usuarios/192.168.73.96?callback=parseResponse" );
Una vez que se inyecta el elemento, el navegador lo evalúa y realiza una operación HTTP GET en la URL de origen, recuperando el contenido. Luego, el navegador evalúa la carga útil devuelta como JavaScript. Esto suele ser una invocación de función. De esa manera, el uso de JSONP puede permitir que las páginas del navegador eludan la política del mismo origen a través de la inyección de elementos de script. [5]
El script se ejecuta dentro del ámbito de la página incluida y, como tal, sigue estando sujeto a restricciones entre dominios en relación con el dominio de la página incluida. Esto significa que una página web no puede, por ejemplo, cargar una biblioteca alojada en otro sitio a través de JSONP y luego realizar solicitudes XMLHttpRequest a ese sitio (a menos que se admita el uso compartido de recursos entre orígenes (CORS)), aunque se podría utilizar una biblioteca de este tipo para realizar solicitudes XMLHttpRequest a un sitio propio.
La inclusión de elementos de script de servidores remotos permite que estos últimos inyecten cualquier contenido en un sitio web. Si los servidores remotos tienen vulnerabilidades que permiten la inyección de JavaScript, la página servida desde el servidor original está expuesta a un mayor riesgo. Si un atacante puede inyectar cualquier JavaScript en la página web original, entonces ese código puede recuperar JavaScript adicional de cualquier dominio, evadiendo la política del mismo origen . [6] El encabezado HTTP de la política de seguridad de contenido permite que los sitios web indiquen a los navegadores web de qué dominio se pueden incluir los scripts.
En 2011 se realizó un esfuerzo para definir una definición de subconjunto estricto más segura para JSONP [1] que los navegadores podrían aplicar en solicitudes de script con un tipo MIME específico como "application/json-p". Si la respuesta no se analizaba como JSONP estricto, el navegador podría generar un error o simplemente ignorar la respuesta completa. Sin embargo, este enfoque se abandonó a favor de CORS y el tipo MIME correcto para JSONP sigue siendo [7]application/javascript
.
JSONP conllevaba los mismos problemas que resolver JSON con eval()
: ambos interpretan el texto JSON como JavaScript, lo que implicaba diferencias en el manejo de U+2028 ( Separador de línea ) y U+029 ( Separador de párrafo ) del JSON propiamente dicho. Esto hacía que algunas cadenas JSON no fueran legales en JSONP; los servidores que servían JSONP tenían que escapar estos caracteres antes de la transmisión. [8] Este problema se ha corregido ahora en ES2019. [9]
Se pueden usar nombres de devolución de llamada no sanitizados para pasar datos maliciosos a los clientes, eludiendo las restricciones asociadas con application/json
el tipo de contenido, como se demostró en el ataque de descarga de archivos reflejados (RFD) de 2014. [10]
Los puntos finales JSONP inseguros también pueden ser inyectados con datos maliciosos. [11]
Las implementaciones ingenuas de JSONP están sujetas a ataques de falsificación de solicitud entre sitios (CSRF o XSRF). [12] Debido a que el elemento HTML <script>
no respeta la política del mismo origen en las implementaciones del navegador web, una página maliciosa puede solicitar y obtener datos JSON que pertenecen a otro sitio. Esto permitirá que los datos codificados en JSON se evalúen en el contexto de la página maliciosa, posiblemente divulgando contraseñas u otros datos confidenciales si el usuario está conectado actualmente al otro sitio.
Rosetta Flash es una técnica de explotación que permite a un atacante explotar servidores con un punto final JSONP vulnerable haciendo que Adobe Flash Player crea que un subprograma Flash especificado por el atacante se originó en el servidor vulnerable. Flash Player implementa una política del mismo origen que permite realizar solicitudes (con cookies) y recibir respuestas del sitio de alojamiento. El subprograma puede luego enviar los datos recuperados de vuelta al atacante. Este es un exploit de origen cruzado con un impacto similar a la incrustación de un subprograma Flash arbitrario en el dominio vulnerable. El exploit utiliza una carga útil de ActionScript compilada en un archivo SWF compuesto completamente de caracteres alfanuméricos mediante la elaboración de un flujo zlib con un encabezado particular y bloques DEFLATE con codificación Huffman ad-hoc . El archivo SWF alfanumérico resultante se utiliza luego como parámetro de devolución de llamada de una llamada JSONP. Sitios de alto perfil como Google, YouTube, Twitter, Yahoo!, Yandex, LinkedIn, eBay, GitHub, Instagram y Tumblr fueron vulnerables hasta julio de 2014. [13] Esta vulnerabilidad fue descubierta inicialmente por Erling y Alok Menghrajani, con una presentación pública en una conferencia de seguridad. La explotación de la vulnerabilidad fue posteriormente mejorada por Gábor Molnár. El ingeniero de seguridad de Google Michele Spagnuolo acuñó el término [14] y tiene CVE - 2014-4671 [15] y CVE- 2014-5333. [16] La versión 14.0.0.145 de Adobe Flash Player, lanzada el 8 de julio de 2014, introdujo una validación más fuerte de los archivos Flash, [17] y en la versión 14.0.0.176, lanzada el 12 de agosto de 2014, finalizó la corrección, [18] impidiendo que este exploit funcione.
Antes de la solución de Adobe, los sitios web podían protegerse anteponiendo un comentario de JavaScript vacío (/**/) o incluso solo una nueva línea como los primeros bytes de la respuesta JSONP.
En julio de 2005, George Jempty sugirió que se antepusiera una asignación de variable opcional a JSON. [19] [20] La propuesta original para JSONP, donde el relleno es una función de devolución de llamada, parece haber sido realizada por Bob Ippolito en diciembre de 2005 [21] y ahora es utilizada por muchas aplicaciones Web 2.0 como Dojo Toolkit y Google Web Toolkit .