Abraham, miembro de nuestro equipo, nos hizo el favor de crear este post ya que controla a la perfección dicho tema.
Solución:
Un posible enfoque podría ser iterar sobre las entradas del objeto y escapar individualmente de cada key y valor una vez que la biblioteca elegida construya el nodo.
Siguiendo mi comentario anterior, implementé una solución recursiva simple usando Jackson (de su pregunta) y GSON, una biblioteca diferente donde los objetos son un poco más fáciles de construir y el código es más legible. El mecanismo de escape utilizado es el codificador Java OWASP:
jackson
private static JsonNode clean(JsonNode node)
if(node.isValueNode()) // Base case - we have a Number, Boolean or String
if(JsonNodeType.STRING == node.getNodeType())
// Escape all String values
return JsonNodeFactory.instance.textNode(Encode.forHtml(node.asText()));
else
return node;
else // Recursive case - iterate over JSON object entries
ObjectNode clean = JsonNodeFactory.instance.objectNode();
for (Iterator> it = node.fields(); it.hasNext(); )
Map.Entry entry = it.next();
// Encode the key right away and encode the value recursively
clean.set(Encode.forHtml(entry.getKey()), clean(entry.getValue()));
return clean;
GSON
private static JsonElement clean(JsonElement elem)
if (elem.isJsonPrimitive()) // Base case - we have a Number, Boolean or String
JsonPrimitive primitive = elem.getAsJsonPrimitive();
if(primitive.isString())
// Escape all String values
return new JsonPrimitive(Encode.forHtml(primitive.getAsString()));
else
return primitive;
else if (elem.isJsonArray()) // We have an array - GSON requires handling this separately
JsonArray cleanArray = new JsonArray();
for(JsonElement arrayElement: elem.getAsJsonArray())
cleanArray.add(clean(arrayElement));
return cleanArray;
else // Recursive case - iterate over JSON object entries
JsonObject obj = elem.getAsJsonObject();
JsonObject clean = new JsonObject();
for(Map.Entry entry : obj.entrySet())
// Encode the key right away and encode the value recursively
clean.add(Encode.forHtml(entry.getKey()), clean(entry.getValue()));
return clean;
Entrada de muestra (ambas bibliotecas):
"nested":
"": ""
,
"xss": ""
Salida de muestra (ambas bibliotecas):
"nested":
"<html>": "<script>(function()alert('xss1'))();</script>"
,
"xss": "<script>(function()alert('xss2'))();</script>"
Creo que la respuesta de Paul Benn es el mejor enfoque en general, pero si no desea iterar sobre los nodos json, podría considerar usar Encode.forHtmlContent, que no se escapa de las comillas. Siento que esto es probablemente seguro ya que no puedo pensar en cómo introducir una cita adicional en una cotización string podría causar un exploit. ¡Dejaré que el lector revise los documentos y decida por sí mismo!
hiedra.xml
y algo de código para hacer la codificación html
private String objectToJson(Object value)
String result;
try
result = jsonWriter.writeValueAsString(value);
return Encode.forHtmlContent(result);
catch (JsonProcessingException e)
return "null";
Aquí tienes las reseñas y valoraciones
Si te sientes a gusto, eres capaz de dejar una sección acerca de qué le añadirías a esta crónica.