点击上方名片关注我,为你带来更多踩坑案例
- 引言 -
如果你是一个摸爬滚打几年的开发者,那么这个阶段,对系统设计的合理性绝对是衡量一个人水平的重要标准。
一个好的设计不光能让你工作中避免很多麻烦,还能为你面试的时候增加很多谈资
而且,不同设计之间理念都是有借鉴性参考性的,你见过的设计多了,思考的多了,再次面临一个问题的时候,就会有很多点子不由自主的冒出来。
希望这个系列的文章,能够和大家互相借鉴参考,共同进步。
- XSS攻击 -
XSS跨站脚本攻击(Cross Site Scripting)攻击就是指在网页中通过一些巧妙的方式,注入一些恶意的可执行代码片段等,来非法攻击网站
XSS攻击的方式有很多很多,需要我们在开发环节中的每一个步骤都注意
本文主要介绍一种防止XSS攻击的方法,也是普适性比较高,大家比较容易忽略的一点---服务端保存前端传来的参数,特别是字符串类型的参数,要秉持着不信任的原则,防止把跨站脚本持久化到数据库中
- XSS过滤 -
方法就是在接收参数前,对参数中的String类型的字段做一个筛查,大致思路分为三步:
1.拦截到参数--使用过滤器
2.将参数中的Srting字段拆出来---通过类型判断和遍历,因为有些参数的格式可能会略复杂
3.将String类型的字段做统一替换---可以使用一些现成的正则替换工具
好了,思路就是上面的三步,下面直接贴代码
首先定义我们的过滤器
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
XssHttpRequestWrapper requestWrapper = new XssHttpRequestWrapper(request);
filterChain.doFilter(requestWrapper, servletResponse);
}
@Override
public void destroy() {
}
}
然后注册我们的过滤器
@Configuration
public class XSSFilterRegister {
@Bean
public FilterRegistrationBean<XssFilter> registerXss() {
//通过FilterRegistrationBean实例设置优先级可以生效
FilterRegistrationBean<XssFilter> bean = new FilterRegistrationBean<>();
//注册自定义过滤器
bean.setFilter(new XssFilter());
//过滤器名称
bean.setName("xssFilter");
//过滤所有路径
bean.addUrlPatterns("/*");
return bean;
}
}
最后写过滤器具体要做的事
public class XssHttpRequestWrapper extends HttpServletRequestWrapper {
public XssHttpRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if (!StringUtils.isEmpty(value)) {
value = HtmlUtil.filter(value);
}
return value;
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values != null) {
for (int i = 0; i < values.length; i++) {
String value = values[i];
if (!StringUtils.isEmpty(value)) {
value = HtmlUtil.filter(value);
}
values[i] = value;
}
}
return values;
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> parameters = super.getParameterMap();
Map<String, String[]> map = new LinkedHashMap<>();
if (parameters != null) {
for (String key : parameters.keySet()) {
String[] values = parameters.get(key);
for (int i = 0; i < values.length; i++) {
String value = values[i];
if (!StringUtils.isEmpty(value)) {
value = HtmlUtil.filter(value);
}
values[i] = value;
}
map.put(key, values);
}
}
return map;
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (!StringUtils.isEmpty(value)) {
value = HtmlUtil.filter(value);
}
return value;
}
@Override
public ServletInputStream getInputStream() throws IOException {
InputStream in = super.getInputStream();
InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
BufferedReader buffer = new BufferedReader(reader);
StringBuilder body = new StringBuilder();
String line = buffer.readLine();
while (line != null) {
body.append(line);
line = buffer.readLine();
}
buffer.close();
reader.close();
in.close();
Map<String, Object> map = new HashMap<>();
String json = "";
try {
if (body.toString().startsWith("[")) {
JSONArray jsonArray = JSONUtil.parseArray(body.toString());
JSONArray newArray = new JSONArray();
for (Object object : jsonArray) {
if (object instanceof Integer) {
json = jsonArray.toString();
break;
} else if (object instanceof Long) {
json = jsonArray.toString();
break;
} else {
map = (JSONObject) object;
Map<String, Object> result = new LinkedHashMap<>();
for (String key : map.keySet()) {
filter(result, key, map.get(key));
}
newArray.add(result);
}
}
if (newArray.size() != 0) {
json = newArray.toString();
}
} else if (body.toString().startsWith("{")) {
map = JSONUtil.parseObj(body.toString());
if (!map.isEmpty()) {
Map<String, Object> result = new LinkedHashMap<>();
for (String key : map.keySet()) {
filter(result, key, map.get(key));
}
json = JSONUtil.toJsonStr(result);
}
}
} catch (Exception e1) {
throw new ParamException(e1);
}
ByteArrayInputStream bain = new ByteArrayInputStream(json.getBytes());
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return bain.read();
}
};
}
private static void filter(Map<String, Object> result, String key, Object val) {
if (val instanceof String) {
// 字符串直接替换
result.put(key, HtmlUtil.filter(val.toString()));
} else if (val instanceof JSONObject) {
// JSONObject遍历
JSONObject valObj = new JSONObject(val);
JSONObject newObj = new JSONObject();
for (String keyObj : valObj.keySet()) {
if (valObj.get(keyObj) instanceof String && !StringUtils.isEmpty(valObj.get(keyObj).toString())) {
newObj.set(keyObj, HtmlUtil.filter(valObj.get(keyObj).toString()));
} else {
newObj.set(keyObj, valObj.get(keyObj));
}
}
result.put(key, newObj);
} else if (val instanceof JSONArray) {
// JSONArray遍历
JSONArray valArray = new JSONArray(val);
JSONArray newArray = new JSONArray();
for (Object o : valArray) {
if (o instanceof JSONObject) {
JSONObject valObj = (JSONObject) o;
JSONObject newObj = new JSONObject();
for (String keyObj : valObj.keySet()) {
if (valObj.get(keyObj) instanceof String && !StringUtils.isEmpty(valObj.get(keyObj).toString())) {
newObj.set(keyObj, HtmlUtil.filter(valObj.get(keyObj).toString()));
} else {
newObj.set(keyObj, valObj.get(keyObj));
}
}
newArray.put(newObj);
} else {
newArray.put(o);
}
}
result.put(key, newArray);
} else {
// 其他类型字段不做替换
result.put(key, val);
}
}
}
其中的一些类大家可以做自行替换,比如正则过滤的工具类我使用的是hutool的工具包HtmlUtil,大家也可以根据业务场景的不同用别的或者自定义。