سؤال

I have a Spring application on the backend and a GWT application on the frontend. When the user is logged in ''index.jsp'' will output the user information as a javascript variable.

I am using AutoBeanFactory to encode and decode the user information as json.
Because the user can register and the user information are stored in the database I try to follow the OWASP XSS Preventing cheat sheet by escaping the user information in the JSP page.

I am using the esapi library to do the encoding. The server side code looks like this:

public static String serializeUserToJson(CustomUser user) {
        String json;
        AppUserProxy appUserProxy = appUserFactory.appuser().as();
        appUserProxy.setFirstname(encoder.encodeForHTML(user.getFirstname()));
        appUserProxy.setLastname(encoder.encodeForHTML(user.getLastname()));
        AutoBean<AppUserProxy> bean = appUserFactory.appuser(appUserProxy);
        json = AutoBeanCodex.encode(bean).getPayload();
        return json;
    }

I tried to use encodeForHTML and encodeForJavaScript(). This works fine for normal characters however as soon as I use Umlaute characters (ü, ä, ö) I run into problems.

If I use the encodeforHTML() function the javascript variable looks like this (note firstname has an ü):

var data = {'user':'{"email":"john.doe&#x40;gmail.com","lastname":"Doe","firstname":"&Uuml;ber"}'};

Decoding with Autobean works fine however the character ü is not displayed properly but the HTML escaped one (&Uuml;ber).

When I use the encodeForJavaScript() function the output is as follows:

var data = {'user':'{"email":"john.doe&#x40;gmail.com","lastname":"Doe","firstname":"\\xDCber"}'};

When I try to decode the JSON string I run into a weird problem. In Development Mode/Hosted Mode decoding works fine and the Umlaut is properly displayed. However as soon as I run the code in Production Mode I get an uncaught Exception:

java.lang.IllegalArgumentException: Error parsing JSON: SyntaxError: Unexpected token x
{"email":"john.doe&#x40;gmail.com","lastname":"Doe","firstname":"\xDCber"}
    at Unknown.java_lang_RuntimeException_RuntimeException__Ljava_lang_String_2V(Unknown Source)
    at Unknown.java_lang_IllegalArgumentException_IllegalArgumentException__Ljava_lang_String_2V(Unknown Source)
    at Unknown.com_google_gwt_core_client_JsonUtils_throwIllegalArgumentException__Ljava_lang_String_2Ljava_lang_String_2V(Unknown Source)
    at Unknown.com_google_gwt_core_client_JsonUtils_safeEval__Ljava_lang_String_2Lcom_google_gwt_core_client_JavaScriptObject_2(Unknown Source)
    at Unknown.com_google_web_bindery_autobean_shared_impl_StringQuoter_split__Ljava_lang_String_2Lcom_google_web_bindery_autobean_shared_Splittable_2(Unknown Source)
    at Unknown.com_google_web_bindery_autobean_shared_AutoBeanCodex_decode__Lcom_google_web_bindery_autobean_shared_AutoBeanFactory_2Ljava_lang_Class_2Ljava_lang_String_2Lcom_google_web_bindery_autobean_shared_AutoBean_2(Unknown Source)
    at Unknown.com_gmi_nordborglab_browser_client_mvp_main_UserInfoPresenter_onBind__V(Unknown Source)

I can think of following solutions:

  1. Rely only on input validation (when the data is stored in the db) and remove the output encoding. But that's not the recommended approach.
  2. Replace the Umlaute with normal ASCII characters (ü => ue) and continue to use output encoding
  3. use some library which escapes the XSS characters but leaves the Umlaute alone.

I am thankful for some feedback

Update: Based on Thomas suggestions I am now passing a JsoSplittable from JSNI and then passing this to the AutoBeanCodex.decode function. It works fine in Production Mode however in Hosted Mode I get following NPE:

java.lang.NullPointerException: null
    at com.google.gwt.dev.shell.CompilingClassLoader$MyInstanceMethodOracle.findOriginalDeclaringClass(CompilingClassLoader.java:428)
    at com.google.gwt.dev.shell.rewrite.WriteJsoImpl.isObjectMethod(WriteJsoImpl.java:307)
    at com.google.gwt.dev.shell.rewrite.WriteJsoImpl.visitMethod(WriteJsoImpl.java:289)
    at com.google.gwt.dev.shell.rewrite.WriteJsoImpl$ForJsoInterface.visitMethod(WriteJsoImpl.java:228)
    at com.google.gwt.dev.asm.ClassAdapter.visitMethod(ClassAdapter.java:115)
    at com.google.gwt.dev.shell.rewrite.RewriteJsniMethods.visitMethod(RewriteJsniMethods.java:350)
    at com.google.gwt.dev.asm.ClassReader.accept(ClassReader.java:774)
    at com.google.gwt.dev.asm.ClassReader.accept(ClassReader.java:420)
    at com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.rewrite(HostedModeClassRewriter.java:251)
    at com.google.gwt.dev.shell.CompilingClassLoader.findClassBytes(CompilingClassLoader.java:1236)
    at com.google.gwt.dev.shell.CompilingClassLoader.findClass(CompilingClassLoader.java:1059)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)

The code which causes this exception is following:

private native final JsoSplittable getJsoUserdata() /*-{
    if (typeof $wnd.user   !== 'undefined')
        return $wnd.user;
    return null;
}-*/;

@Override
public JsoSplittable getUserdata() {
    JsoSplittable user = null;
    user = getJsoUserdata();
    if (user != null) {
       String payload = user.getPayload();
       Window.alert(payload);
    }
    return user;
}

Window.alert(payload) works fine in production mode. In Hosted mode when I step into user.getPayload() I get a NPE in findOriginalDeclaringClass function of the CompilingClassLoader.java. It seems that declaringClasses is null

هل كانت مفيدة؟

المحلول

You shouldn't explicitly escape anything; AutoBeans do it for you already. Or rather, if you want to escape something, escape the output of AutoBean's getPayload(), not the innards.

Your problem is that AutoBeans uses native JSON.parse() when possible (for both performance and safety reasons) which, per spec, only supports \uNNNN kinds of escapes, and not the \xHH that encodeForJavaScript outputs. In other words, ESAPI would need an encodeForJSON.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top