Java Autobean和XSS问题

Java Autobean和XSS问题,java,json,gwt,xss,autobean,Java,Json,Gwt,Xss,Autobean,我在后端有一个Spring应用程序,在前端有一个GWT应用程序。 当用户登录“index.jsp”时,将以javascript变量的形式输出用户信息 我正在使用AutoBeanFactory将用户信息编码和解码为json。 因为用户可以注册,并且用户信息存储在数据库中,所以我尝试通过在JSP页面中转义用户信息来遵循 我正在使用esapi库进行编码。服务器端代码如下所示: public static String serializeUserToJson(CustomUser user) {

我在后端有一个
Spring
应用程序,在前端有一个
GWT
应用程序。 当用户登录“index.jsp”时,将以javascript变量的形式输出用户信息

我正在使用
AutoBeanFactory
将用户信息编码和解码为json。
因为用户可以注册,并且用户信息存储在数据库中,所以我尝试通过在JSP页面中转义用户信息来遵循

我正在使用esapi库进行编码。服务器端代码如下所示:

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;
    }
使用Autobean进行解码工作正常,但是字符u没有正确显示,而是HTML转义字符(
Ü;ber

当我使用
encodeForJavaScript()
函数时,输出如下:

var data = {'user':'{"email":"john.doe&#x40;gmail.com","lastname":"Doe","firstname":"\\xDCber"}'};
当我试图解码JSON字符串时,我遇到了一个奇怪的问题。在开发模式/托管模式下,解码工作正常,Umlaut正确显示。 但是,只要我在生产模式下运行代码,我就会得到一个未捕获的异常:

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)
我可以想出以下解决办法:

  • 仅依赖输入验证(当数据存储在数据库中时)并删除输出编码。但这不是推荐的方法
  • 将Umlaute替换为普通ASCII字符(ü=>ue),并继续使用输出编码
  • 使用一些转义XSS字符但不使用Umlaute的库 我很感谢你的反馈

    更新:根据Thomas的建议,我现在从JSNI传递一个
    JsoSplittable
    ,然后将其传递给
    AutoBeanCodex.decode
    函数。它在生产模式下运行良好,但在托管模式下,我得到以下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)
    
    导致此异常的代码如下所示:

    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;
    }
    

    警报(有效负载)在生产模式下工作正常。在托管模式下,当我进入
    user.getPayload()
    时,我在
    CompilingClassLoader.java
    findOriginalDeclaringClass
    函数中得到一个NPE。似乎
    declaringClasses
    为null

    您不应该显式地转义任何内容;汽车人已经为你做了。或者更确切地说,如果要转义某些内容,请转义AutoBean的
    getPayload()
    的输出,而不是内部


    您的问题是AutoBeans在可能的情况下(出于性能和安全原因)使用原生的
    JSON.parse()
    ,根据规范,它只支持
    \unnn
    类型的转义,而不支持
    encodeForJavaScript
    输出的
    \xHH
    。换句话说,ESAPI需要一个
    encodeForJSON

    谢谢Thomas的回复。我尝试在有效负载上使用
    encodeForJavaScript()
    ,它似乎工作正常。当Autobean已经为我为每个字段编码有效负载时(安全方面),编码有效负载是否有意义?顺便说一句:实际上,我不明白的是,为什么您(和链接的文章)将AutoBean的负载用作字符串而不是对象文本。然后从JSNI方法(而不是
    字典
    )返回对象作为
    com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable
    ),甚至不需要
    {user:}
    包装器:
    var user=
    原生JsoSplittable getUser()/*-{return$wnd.user;}-*/。至于你最初的问题:我不会使用
    encodeForJavaScript
    ,但是你必须小心你的页面编码(应该已经是UTF-8了),谢谢你的建议。我没想到。我尝试直接返回一个Splittable,它在生产模式下运行良好。然而,在托管模式下,当我调用
    Splittable.getPayload()
    (有关异常详细信息,请参阅我的问题)时,我会得到一个NPE。您必须明确使用
    JsoSplittable
    ,否则(使用
    Splittable
    )GWT无法知道它应该是
    JsoSplittable
    还是
    JsonSplittable
    ;而
    JavaScriptObject
    s(比如
    JsoSplittable
    ;aka overlay类型)是非常特别的野兽:我实际上使用的是
    JsoSplittable
    ,但不知怎的,当GWT编译器为托管模式重写类时,我得到了一个NPE(老实说,我对GWT的内部结构了解不够,不知道它为什么这么做)。我用一些额外的代码更新了我的答案,并突出显示了导致此NPE的调用。这可能是gwt开发模式中的错误吗?
    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;
    }