河南高端网站建设,推荐广东中山网站建设,网站设计不同的原因,响应式网站建设企业XSS 问题的根源在于#xff0c;原本是让用户传入或输入正常数据的地方#xff0c;被黑客替换为了 JavaScript 脚本#xff0c;页面没有经过转义直接显示了这个数据#xff0c;然后脚本就被 执行了。更严重的是#xff0c;脚本没有经过转义就保存到了数据库中#xff0c;随…XSS 问题的根源在于原本是让用户传入或输入正常数据的地方被黑客替换为了 JavaScript 脚本页面没有经过转义直接显示了这个数据然后脚本就被 执行了。更严重的是脚本没有经过转义就保存到了数据库中随后页面加载数据的时候数据中混入的脚本又当做代码执行了。黑客可以利用这个漏洞 来盗取敏感数据诱骗用户访问钓鱼网站等。 RequestMapping(xss)
Slf4j
Controller
public class XssController {Autowiredprivate UserRepository userRepository;//显示xss页面GetMappingpublic String index(ModelMap modelMap) {//查数据库User user userRepository.findById(1L).orElse(new User());//给View提供ModelmodelMap.addAttribute(username, user.getName());return xss;}//保存用户信息PostMappingpublic String save(RequestParam(username) String username, HttpServletRequest request) {User user new User();user.setId(1L);user.setName(username);userRepository.save(user);//保存完成后重定向到首页return redirect:/xss/;}
}
//用户类同时作为DTO和Entity
Entity
Data
public class User {Idprivate Long id;private String name;
}使用Thymeleaf 模板引擎来渲染页面
div stylefont-size: 14pxform idmyForm methodpost th:action{/xss/}label th:utext${username}/ !--对于 Thymeleaf 模板引擎需要注意的是使用 th:utext 来显示数据是不会进行转义的需要使用 th:text--input idusername nameusername size100 typetext/button th:textRegister typesubmit//form
/div解决方法可以使用 HTML 转码。既然是通过 RequestParam 来获取请求参数那我们定义一个 InitBinder 实现数据绑定的时候对字符串进行转码即 可。
ControllerAdvice
public class SecurityAdvice {InitBinderprotected void initBinder(WebDataBinder binder) {//注册自定义的绑定器binder.registerCustomEditor(String.class, new PropertyEditorSupport() {Overridepublic String getAsText() {Object value getValue();return value ! null ? value.toString() : ;}Overridepublic void setAsText(String text) {//赋值时进行HTML转义setValue(text null ? null : HtmlUtils.htmlEscape(text));}});}
} 但是解决问题的方式不全面InitBinder 是 Spring Web 层面的处理逻辑如果有代码不通过 RequestParam 来获取数据而是直接从 HTTP 请求 获取数据的话这种方式就不会奏效。比如 user.setName(request.getParameter(username)); 最好的解决方式是定义一个 servlet Filter通过 HttpServletRequestWrapper 实现 servlet 层面的统一参数替换。
//自定义过滤器
Component
Order(Ordered.HIGHEST_PRECEDENCE)
public class XssFilter implements Filter {Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletExceptio n {chain.doFilter(new XssRequestWrapper((HttpServletRequest) request), response);}
}
public class XssRequestWrapper extends HttpServletRequestWrapper {public XssRequestWrapper(HttpServletRequest request) {super(request);}Overridepublic String[] getParameterValues(String parameter) {//获取多个参数值的时候对所有参数值应用clean方法逐一清洁return Arrays.stream(super.getParameterValues(parameter)).map(this::clean).toArray(String[]::new);}Overridepublic String getHeader(String name) {//同样清洁请求头return clean(super.getHeader(name));}Overridepublic String getParameter(String parameter) {//获取参数单一值也要处理return clean(super.getParameter(parameter));}//clean方法就是对值进行HTML转义private String clean(String value) {return StringUtils.isEmpty(value)? : HtmlUtils.htmlEscape(value);}}
这种方式还是不够彻底原因是无法处理通过 RequestBody 注解提交的 JSON 数据。比如有这样一个 PUT 接口直接保存了客户端传入的 JSON User 对 象
PutMapping
public void put(RequestBody User user) {userRepository.save(user);
}因此我们需要自定义一个json的反序列器进行处理 //注册自定义的Jackson反序列器Beanpublic Module xssModule() {SimpleModule module new SimpleModule();module.module.addDeserializer(String.class, new XssJsonDeserializer());return module;}
public class XssJsonDeserializer extends JsonDeserializerString {Overridepublic String deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {String value jsonParser.getValueAsString();if (value ! null) {//对于值进行HTML转义return HtmlUtils.htmlEscape(value);}return value;}Overridepublic ClassString handledType() {return String.class;}
} 这样就实现了既能转义 Get/Post 通过请求参数提交的数据又能转义请求体中直接提交的 JSON 数据。但是目前这种只能堵新漏确保新数据进入数据 库之前转义。如果因为之前的漏洞数据库中已经保存了一些 JavaScript 代码那么读取的时候同样可能出问题。因此我们还要实现数据读取的时候也 转义。
GetMapping(user)
ResponseBody
public User query() {return userRepository.findById(1L).orElse(new User());
}修改之前的 SimpleModule 加入自定义序列化器并且实现序列化时处理字符串转义 //注册自定义的Jackson序列器Beanpublic Module xssModule() {SimpleModule module new SimpleModule();module.addDeserializer(String.class, new XssJsonDeserializer());module.addSerializer(String.class, new XssJsonSerializer());return module;}
public class XssJsonSerializer extends JsonSerializerString {Overridepublic ClassString handledType() {return String.class;}Overridepublic void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {if (value ! null) {//对字符串进行HTML转义jsonGenerator.writeString(HtmlUtils.htmlEscape(value));}}
} 还要考虑一种情况如果需要在 Cookie 中写入敏感信息的话我们可以开启 HttpOnly 属性。这样 JavaScript 代码就无法读取 Cookie 了即便页面被 XSS 注 入了攻击代码也无法获得我们的 Cookie。
//服务端读取Cookie
GetMapping(readCookie)
ResponseBody
public String readCookie(CookieValue(test) String cookieValue) {return cookieValue;
}
//服务端写入Cookie
GetMapping(writeCookie)
ResponseBody
public void writeCookie(RequestParam(httpOnly) boolean httpOnly, HttpServletResponse response) {Cookie cookie new Cookie(test, zhuye);//根据httpOnly入参决定是否开启HttpOnly属性cookie.setHttpOnly(httpOnly);response.addCookie(cookie);
} 由于 test 和 _ga 这两个 Cookie 不是 HttpOnly 的。通过 document.cookie 可以输出这两个 Cookie 的内容 为 test 这个 Cookie 启用了 HttpOnly 属性后就不能被 document.cookie 读取到了输出中只有 _ga 一项 但是服务端可以读取到这个 cookie