Cómo insertar JSON arbitrario en la etiqueta de script de HTML

Me gustaría almacenar el contenido de un JSON en la fuente de un documento HTML, dentro de una etiqueta de script.

El contenido de ese JSON depende de las entradas enviadas por el usuario, por lo que es necesario tener mucho cuidado para sanear esa cadena para XSS.

He leído dos conceptos aquí en SO.

1. Reemplace todas las apariciones de la etiqueta </script en <\/script , o reemplace todas </ en <\/ lado del servidor.

En cuanto al código, se parece a lo siguiente (usando Python y jinja2 para el ejemplo):

     // view data = { 'test': 'asdasas\'daas"da', } context_dict = { 'data_json': json.dumps(data, ensure_ascii=False).replace('</script', r'<\/script'), } // template  var data_json = {{ data_json | safe }};  // js access it simply as window.data_json object 

    2. Codifique los datos como una cadena JSON codificada en una entidad HTML, y unescape + analice en el lado del cliente. Unescape es de esta respuesta: https://stackoverflow.com/a/34064434/518169

     // view context_dict = { 'data_json': json.dumps(data, ensure_ascii=False), } // template  var data_json = '{{ data_json }}'; // encoded into HTML entities, like < > &  // js function htmlDecode(input) { var doc = new DOMParser().parseFromString(input, "text/html"); return doc.documentElement.textContent; } var decoded = htmlDecode(window.data_json); var data_json = JSON.parse(decoded); 

    Este método no funciona porque \" en una fuente de script se convierte " en una variable JS. Además, crea un documento HTML mucho más grande y tampoco es realmente legible para los humanos, por lo que me gustaría ir con el primero si no significa un gran riesgo para la seguridad.

    ¿Existe algún riesgo de seguridad en el uso de la primera versión? ¿Es suficiente desinfectar una cadena codificada JSON con .replace('</script', r'<\/script') ?

    Referencia en SO:
    ¿La mejor manera de almacenar JSON en un atributo HTML?
    ¿Por qué dividir la etiqueta al escribirla con document.write ()?
    Etiqueta de script en cadena de JavaScript
    Desinfectar el contenido del elemento
    Escape </ en el contenido de la etiqueta de script

    Algunos grandes recursos externos sobre este tema:
    tojson de implementación del filtro tojson de Flask.
    La ayuda y la fuente del método json_escape de Rail.
    Una discusión de 5 años en el boleto de Django y el código propuesto

    En primer lugar, tu paranoia está bien fundada.

    • un analizador de HTML podría ser engañado por una etiqueta de script de cierre (es mejor asumirlo por cualquier etiqueta de cierre)
    • un JS-parser podría ser engañado por barras invertidas y comillas (con un codificador realmente malo)

    , sería mucho más “seguro” codificar todos los caracteres que podrían confundir a los diferentes analizadores involucrados. Mantenerlo legible para el ser humano podría contradecir su paradigma de seguridad.

    Nota : El resultado de la encoding de la cadena JSON debe ser canónico y OFC, no roto, como en parsable. JSON es un subconjunto de JS y, por lo tanto, puede analizarse JS sin ningún riesgo. Entonces, todo lo que tiene que hacer es asegurarse de que la instancia de HTML-Parser que extrae el código JS no sea engañada por sus datos de usuario.

    Así que el verdadero escollo es la anidación de ambos analizadores. En realidad, le insto a que ponga algo así en una solicitud por separado. De esa manera evitarías ese escenario por completo.

    Asumiendo todos los estilos posibles y las correcciones de errores que podrían ocurrir en un analizador de este tipo, podría ser que otras tags (abrir o cerrar) logren una hazaña similar.

    Como en: sugiriendo al analizador que la etiqueta de script haya finalizado implícitamente.

    Por lo tanto, es recomendable codificar la barra y todas las llaves (/, <,>), no solo el cierre de una etiqueta de script, en el método reversible que elija, siempre y cuando no confunda el analizador HTML:

    • La mejor opción sería base64 (pero usted quiere más legible)
    • HTMLentities servirá, aunque confundiendo a los humanos 🙂
    • Hacer su propio escape también funcionará, simplemente escape de los caracteres individuales en lugar del fragmento

    En conclusión, sí, probablemente sea mejor con algunos cambios, pero tenga en cuenta que ya estará a un paso de estar "seguro", al intentar algo como esto en primer lugar, en lugar de cargar el JSON a través de XHR o al menos usar Una cadena rigurosa que codifica como base64.

    PD: Si puedes aprender del código de otras personas que codifica las cadenas, eso es bueno, pero no debes recurrir a las "bibliotecas" o las funciones de otras personas si no hacen exactamente lo que necesitas. Así que más bien escriba y pruebe minuciosamente su propio codificador (de / en) y sepa que esta trampa ha sido sellada.