JSONP | ||
---|---|---|
www.json-p.org (enlace roto, archivado en web archive) | ||
Información general | ||
Extensión de archivo |
.jsonp | |
Tipo de MIME |
application/json-p | |
Tipo de formato | Intercambio de datos | |
Extendido de | JSON y JavaScript | |
Estándar(es) | RFC 7159, RFC 4329 | |
Formato abierto | ||
JSONP o JSON-P (JSON con relleno) es una técnica de JavaScript para solicitar datos cargando una etiqueta <script>
. Fue propuesto por Bob Ippolito en 2005.[1] JSONP permite compartir datos sin pasar por la política del mismo origen, que no permite ejecutar código JavaScript para leer elementos del Modelo de Objetos de Documento (DOM) 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 URI, nombre de host y número de puerto .
El elemento HTML <script>
generalmente puede ejecutar código JavaScript recuperado de orígenes externos. Sin embargo, los servicios que respondieron con datos JSON puros no pueden compartir datos de orígenes externos antes de la adopción de CORS (intercambio de recursos de origen cruzado).
Por ejemplo, una solicitud a un servicio externo http://server.example.com/Users/1234
puede devolver un registro de una persona llamada Alice en el formato JSON. La sintaxis JSON es coherente con la sintaxis de objetos de JavaScript:
{
"Name": "Alice",
"Id": 1234,
"Rank": 7
}
Sin soporte para CORS, un intento de usar los datos entre dominios da como resultado un error de JavaScript:
<script type="application/javascript"
src="http://server.example.com/Users/1234">
</script>
El navegador descargará el archivo apuntado dentro de la etiqueta <script>
, evaluará su contenido, malinterpretará los datos JSON sin procesar como un bloque y arrojará un error de sintaxis. Incluso si los datos se interpretaran como un literal de objeto de JavaScript, JavaScript no podría acceder a él cuando se ejecutara en el navegador, ya que sin una asignación variable, los literales de objeto son inaccesibles.
En el patrón de uso JSONP, la solicitud de URL señalada por el atributo src
en el elemento <script>
devuelve datos JSON, con un código JavaScript (generalmente una llamada a función) envuelto alrededor de él. Esta "carga útil envuelta" es interpretada por el navegador. De esta manera, una función que ya está definida en el entorno JavaScript puede manipular los datos JSON. A continuación se muestra una solicitud y respuesta JSONP típica.
La llamada a la función parseResponse()
es la "P" de JSONP: el "relleno" o "prefijo" alrededor del JSON puro.[2] Para que JSONP funcione, un servidor debe entregar una respuesta que incluya la función JSONP. JSONP no funciona con resultados con formato JSON. El cliente y el servidor deben acordar la invocación de la función JSONP que se devuelve y la carga útil que recibe la función. Por convención, el servidor que proporciona los datos JSON ofrece al sitio web solicitante el nombre de la función JSONP, generalmente utilizando el nombre jsonp o devolución de llamada como parámetro de cadena de consulta con nombre, 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({"Name": "Alice", "Id": 1234, "Rank": 7});
JSONP solo tiene sentido cuando se usa con un elemento de script. Para cada nueva solicitud JSONP, el navegador debe agregar un nuevo elemento <script>
o reutilizar uno existente. La primera opción, agregar un nuevo elemento de secuencia de comandos, se realiza mediante la manipulación dinámica del DOM, y se conoce como inyección de elementos de secuencia de comandos. El elemento <script>
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 framework tienen funciones de ayuda 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 vería así:
$.getScript("http://server.example.com/Users/192.168.73.96?callback=parseResponse");
Después de inyectar el elemento, el navegador lo evalúa y realiza una petición HTTP GET en la URL src, recuperando el contenido. Luego, el navegador evalúa la carga útil de retorno como JavaScript. Esto es típicamente una invocación de función. De esa manera, el uso de JSONP puede permitir que las páginas del navegador eviten la política del mismo origen mediante la inyección de elementos de script.
El script se ejecuta dentro del alcance de la página incluida y, como tal, todavía está 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 intercambio de recursos de origen cruzado (CORS)), aunque uno podría usar dicha biblioteca para hacer XMLHttpRequests en el propio sitio.
La inclusión de etiquetas de script desde servidores remotos permite a los servidores remotos inyectar 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 JavaScript en la página web original, ese código puede recuperar JavaScript adicional de cualquier dominio, sin pasar por la política del mismo origen.[3] El Encabezado HTTP de la Política de seguridad de contenido permite a los sitios web indicar a los navegadores web desde qué scripts de dominio se pueden incluir.
Se realizó un esfuerzo alrededor de 2011 para definir una definición de subconjunto estricta más segura para JSONP[4] que los navegadores podrían aplicar en las solicitudes de script con un tipo MIME específico como "application/json-p". Si la respuesta no se analizó como JSONP estricto, el navegador podría arrojar un error o simplemente ignorar la respuesta completa. Sin embargo, este enfoque fue abandonado a favor de CORS, y el tipo MIME correcto para JSONP sigue siendo application/javascript
.[5]
JSONP tiene los mismos problemas que resolver JSON con eval()
: ambos interpretan el texto JSON como JavaScript, lo que significa diferencias en el manejo de los caracteres unicode U+2028 (separador de línea) y U+2029 (separador de párrafo) de JSON propiamente dicho. Esto hace que algunas cadenas JSON no sean legales en JSONP; Los servidores que sirven JSONP deben escapar de estos caracteres antes de la transmisión.[6]
Los nombres de devolución de llamada no sanitizados pueden usarse para pasar datos maliciosos a los clientes, evitando las restricciones asociadas con el tipo de contenido de application/json
, como se demuestra en el ataque de descarga de archivos reflejados (RFD) de 2014.[7]
Los puntos finales inseguros de JSONP también pueden inyectarse con datos maliciosos.[8]
Las implementaciones ingenuas de JSONP están sujetas a ataques de Cross-site request forgery (CSRF o XSRF).[9] Debido a que la etiqueta 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 con JSON se evalúen en el contexto de la página maliciosa, posiblemente divulgando contraseñas u otros datos confidenciales si el usuario está actualmente conectado al otro sitio.
Esto es problemático solo si los datos codificados con JSON contienen información confidencial que no debe divulgarse a un tercero, y el servidor depende de la política del mismo origen del navegador para bloquear la entrega de la información confidencial.
Rosetta Flash es una técnica de explotación que permite a un atacante explotar servidores con un punto final JSONP vulnerable al hacer que Adobe Flash Player crea que un applet Flash especificado por el atacante se originó en el servidor vulnerable. Flash Player implementa una política del mismo origen que le permite a uno hacer solicitudes (con cookies) y recibir respuestas del sitio de alojamiento. El applet puede enviar los datos recuperados al atacante. Este es un exploit de origen cruzado con un impacto similar a incrustar un applet Flash arbitrario en el dominio vulnerable. El exploit utiliza una carga útil de ActionScript compilada en un archivo SWF compuesto enteramente de caracteres alfanuméricos mediante la creación de una secuencia zlib con un encabezado particular y bloques DEFLATE con codificación Huffman ad hoc. El archivo SWF alfanumérico resultante solo se usa como parámetro de devolución de llamada de una llamada JSONP. Los sitios de alto perfil como Google, YouTube, Twitter, Yahoo!, Yandex, LinkedIn, eBay, Instagram y Tumblr fueron vulnerables hasta julio de 2014.[10] Esta vulnerabilidad fue descubierta y publicada por el ingeniero de seguridad de Google Michele Spagnuolo[11] en los CVE 2014-4671[12] y CVE 2014-5333.[13] La versión de lanzamiento de Adobe Flash Player 14.0.0.145, lanzada el 8 de julio de 2014, introdujo una validación más fuerte de los archivos Flash,[14] y en la versión 14.0.0.176, lanzada el 12 de agosto de 2014, finalmente implementó la corrección,[15] evitando esta vulnerabilidad de trabajar.
En julio de 2005, George Jempty sugirió que se anteponga una asignación de variable opcional a JSON.[16][17] La propuesta original para JSONP, donde el relleno es una función de devolución de llamada, parece haber sido hecha por Bob Ippolito en diciembre de 2005[18] y ahora es utilizada por muchas aplicaciones Web 2.0 como Dojo Toolkit, Google Web Toolkit y servicios web.