How to html escape values by Jackson generator
Asked Answered
S

3

4

We generate lots of JSON objects using Spring and its built-in MappingJacksonHttpMessageConverter . All great.

But now I want to html escape String values of my (any kind of) objects in order to prevent XSS.

So, how do i approach this problem? I first thought I could write a custom Object mapper and put it into the MappkingJacksonHttpMessageConverter. However, the writeValue takes an Object, and I don't want that, I want to have its fields to iterate over. Instead of doing that myself, I bet the jackson converter needs to do that as well. So i want to influence that part.

Now I end up with a SerializerProvider interface. The standard implementation (StdSerializerProvider) is getting called by the ObjectMapper. So somewhere there I want to override/influence the method that is responsible for setting values.

Is that possible? As far as I can see it is hard to extend. I cannot override the StdSerializerProvider to override the method that ObjectMapper uses. Perhaps I need to override another one?

Or, perhaps this is totally wrong and I need to approach it from a totally different angle?

Any thoughts?

Oh btw, implementing the SerializerProvider myself and creating a composit that delegates to the StdSerializerProvider, might be possible but I'd rather not to. (i already have problems instantiating the StdSerializerProvider myself).

Any thoughts are appreciated!

Squalid answered 15/2, 2011 at 16:11 Comment(2)
I have created a Blog post about this: stefanhendriks.wordpress.com/2011/02/16/…Squalid
The blog seems offline now, here is an archived version: web.archive.org/web/20201111190800/https://…Zeniazenith
A
4

There are multiple ways, depending on how you can identify things you want to escape. Some thoughts:

  • If you want to quote/escape all Strings, you could define custom String serializer
  • You could first serialize as JSON tree (JsonNode) -- ObjectMapper.convertValue(pojo, JsonNode.class) -- modify it, then serialize to JSON (ObjectMapper.writeValue(intermediateTree))
  • If you want a flexible system where you can add annotation for fields that need special handling, you could use Jackson 1.7 feature of ContextualSerializers; custom serializer that can reconfigure itself based on annotations. It may seem like an overkill at first, but this could be used to easily specify custom modifiers (annotation property with value of Class, that indicates object that serializer can call to modify value being serialized -- like escape)

EDIT: even better option, as pointed out by @Philzen, is use of CharacterEscapes -> see https://www.cowtowncoder.com/blog/archives/2012/08/entry_476.html

Adrell answered 16/2, 2011 at 7:39 Comment(3)
I have used your third option to register a JsonSerializer that escapes my String valules within a CustomObjectMapper. Works exactly like I want to. Thanks a bunch!Squalid
Just wondering, why didn't you mention this option: cowtowncoder.com/blog/archives/2012/08/entry_476.html ?Zeniazenith
@Zeniazenith looking at date of the answer vs blog post it is because I wrote answer here before blogging about better option :) Thank you for providing the link -- I will edit my answer.Adrell
F
1

This answer was given, while the question was about verifying that the string does not contains XSS and not escaping. This answer will not help you, if you want to escape the strings.

What about validating the created object (Command Object) with JSR 303 Bean Validation?

For example by using a not @NotHtml (annotation and validation) to the String properties of the "Command Object".

(Of course you have to implment the @NotHtml (@NotJavaScript or @NotXSS) Annotations and Validator by your selfe.)

Fasciation answered 15/2, 2011 at 16:49 Comment(5)
It is a possibility but we want to escape the output, not the input. Nor, specifying on each field what to escape. Unless there are very good reasons not to do it that way :)Squalid
@Stefan Hendriks: You are right this is not a good way (but i belive it would work). Anyway: you should NOT look for a way to VALIDATE the fields - INSTEAD you should ESCAPE your content in the correct way - And the escaping should not be done on the Input, but when you put the data in a new environment. (For example nobody sql-escape the http input when he recives it, instead the sql-escaping is done right before the sql statement is build. -- So do the same for HTML escaping: do not escape it when you recive it, but when you build the HTML response, same for JSON!Fasciation
Thats exactly what i want to do (escaping html) but my question remains thoughSqualid
@Stefan Hendriks: in this case you should update your question (or create a new one), because: "But now I want to validate some fields in order to prevent XSS" - is about validating, not escapting.Fasciation
I stand corrected. I have updated the question title and the sentence where it was causing confusion. I understand your suggestion about JSR 303. (We are using that already).Squalid
Z
1

You can create a custom CharacterEscapes implementation for this:

// Custom CharacterEscapes implementation to treat HTML characters like GSON does by default
// (inspired by https://www.cowtowncoder.com/blog/archives/2012/08/entry_476.html)
private final static class HtmlCharacterEscapes extends CharacterEscapes {
    private final int[] asciiEscapes;

    public HtmlCharacterEscapes() {
        // start with set of characters known to require escaping (double-quote, backslash etc)
        int[] esc = CharacterEscapes.standardAsciiEscapesForJSON();
        // and force escaping of a few others:
        esc['<'] = CharacterEscapes.ESCAPE_STANDARD;
        esc['>'] = CharacterEscapes.ESCAPE_STANDARD;
        esc['&'] = CharacterEscapes.ESCAPE_STANDARD;
        esc['='] = CharacterEscapes.ESCAPE_STANDARD;
        esc['\''] = CharacterEscapes.ESCAPE_STANDARD;
        asciiEscapes = esc;
    }

    // this method gets called for character codes 0 - 127
    @Override public int[] getEscapeCodesForAscii() {
        return asciiEscapes;
    }

    // and this for others; we don't need anything special here
    @Override public SerializableString getEscapeSequence(int ch) {
        // no further escaping (beyond ASCII chars) needed:
        return null;
    }
}

I've actually found this on StaxMan's blog, so i'm wondering why they didn't add this option to their answer.

Then you have a couple of options to register it (MAPPER_INSTANCE would be a static / cached JsonMapper):

  1. create an HTML-escaping JSON writer instance:

    ObjectWriter writer = MAPPER_INSTANCE.writer(new HtmlCharacterEscapes());.writer(new HtmlCharacterEscapes());
    
  2. register it globally on the mapper:

    MAPPER_INSTANCE.getFactory().setCharacterEscapes(new HtmlCharacterEscapes());
    
  3. register it globally in Spring Boot:

     @Bean
     public Jackson2ObjectMapperBuilder jacksonBuilder() {
         return new Jackson2ObjectMapperBuilder().factory(new JsonFactory().setCharacterEscapes(new HtmlCharacterEscapes()));
     }    
    
Zeniazenith answered 11/2 at 10:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.