Java對類進行XSS過濾


Jackson對返回數據進行XSS過濾

定義一個類繼承ObjectMapper

package demo;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.web.util.HtmlUtils;

import java.io.IOException;

public class CustomObjectMapper extends ObjectMapper {
    private static final long serialVersionUID = -3448961813323784217L;

    public CustomObjectMapper() {
        SimpleModule module = new SimpleModule("HTML XSS Serializer",
                new Version(1, 0, 0, "FINAL","com.yihaomen","ep-jsonmodule"));
        module.addSerializer(new JsonHtmlXssSerializer(String.class));
        this.registerModule(module);
    }

    class JsonHtmlXssSerializer extends JsonSerializer<String> {

        public JsonHtmlXssSerializer(Class<String> string) {
            super();
        }

        @Override
        public Class<String> handledType() {
            return String.class;
        }

        @Override
        public void serialize(String value, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException,
                JsonProcessingException {
            if (value != null) {
                String encodedValue = HtmlUtils.htmlEscape(value.toString());
                jsonGenerator.writeString(encodedValue);
            }
        }
    }
}  

在Spring配置文件中進行配置

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean
            class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="supportedMediaTypes">
                <list>
                    <value>application/json;charset=UTF-8</value>
                    <value>text/html;charset=UTF-8</value>
                </list>
            </property>
            <property name="objectMapper">
                <bean class="demo.CustomObjectMapper" />
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

添加測試Controller,驗證上面配置是否成功

@RequestMapping("/testXSS")
@ResponseBody
public UserDTO testXSS() {
    UserDTO userDTO = new UserDTO();
    userDTO.setFullName("<script>alert(1)</script>");
    return userDTO;
}

用Postman測試接口,可以看到,fullName字段的數據已經過html編碼過濾了

以上是對jackson返回數據進行xss過濾的配置,下面是我自己通過java代碼實現的一種方法

JAVA反射實現XSS過濾

package demo;

import org.apache.commons.lang.StringUtils;
import org.springframework.web.util.HtmlUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author zhangkun
 * @date 2020/3/10 11:34
 */
public class XssUtil {
    public static void xssObject(Object obj) {
        // 獲取返回對象類所有字段
        List<Field> fields = new ArrayList<>(Arrays.asList(obj.getClass().getDeclaredFields()));
        // 獲取返回對象的父類的所有字段,(如果有的話,根據自己情況刪減)
        List<Field> parentFields = new ArrayList<>(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields()));
        // 合並父類字段和本類字段(如果有的話,根據自己情況刪減)
        fields.addAll(parentFields);

        // 維護一個以轉義的名單列表,避免同一字段重復被轉義,因為父類和子類可能存在相同的字段名(沒有父類的情況可刪減)
        List<String> nameList = new ArrayList<>();
        
        for (Field field : fields) { //遍歷所有屬性
            String type = field.getGenericType().getTypeName();
            String name = field.getName(); //獲取屬性的名字
            if (type.equals(String.class.getTypeName()) && !nameList.contains(name)) {
                // 如果使用到了這個字段,則添加到名單中,以免下次重復被使用
                nameList.add(name);
                
                // 根據javaBean規范,set或get方法字段的首字母大寫
                name = name.substring(0, 1).toUpperCase() + name.substring(1);

                Method getMethod = null;
                Method setMethod = null;
                try {
                    // 獲取本類的get、set方法
                    getMethod = obj.getClass().getMethod("get" + name);
                    setMethod = obj.getClass().getMethod("set" + name, new Class[]{String.class});
                } catch (NoSuchMethodException e) {
                    try {
                        // 如果沒有獲取到則去父類獲取(如果有的話,根據自己情況刪減)
                        getMethod = obj.getClass().getSuperclass().getMethod("get" + name);
                        setMethod = obj.getClass().getSuperclass().getMethod("set" + name, new Class[]{String.class});
                    } catch (NoSuchMethodException e1) {
                    }
                }
				
                if (getMethod != null && setMethod != null) {
                    String value = null;
                    try { 
                        // 通過get方法獲取到值
                        String value = (String) getMethod.invoke(obj);
                        if (StringUtils.isBlank(value)) {
                            continue;
                        }
                        // 對字段進行html編碼,然后通過set方法再塞回去
                        String s = HtmlUtils.htmlEscape(value);
                        setMethod.invoke(obj, s);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

測試Controller

@RequestMapping("/testXSS")
@ResponseBody
public UserDTO testXSS() {
    UserDTO userDTO = new UserDTO();
    userDTO.setFullName("<script>alert(\"佛山館所屬管'\")</script>");
    // 使用我自己寫的XSS過濾工具類
    XssUtil.xssObject(userDTO);
    return userDTO;
}

Postman測試返回結果

大功告成!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM