漏洞描述
攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
漏洞验证
通过在页面中的留言板,提交框,录入信息框等能够提交参数的地方,尝试注入相关xss代码测试,能够成功执行对应代码功能则存在问题,可能造成流量劫持,篡改、删除页面信息,获取用户cookie信息,盗取账号等不良影响。
修复建议
- 过滤输入的数据,包括"’" “”" “<” “>” “on*” 等非法字符。
- 对输入到页面的数据进行相应的编码转换,包括HTML实体编码,js编码等。
修复方法
验证方法:
在可以提交数据到后台的功能中,在表单中的输入框输入< script>alert(“1”)< /script >(此处应去掉script左右的空格),当成功保存到后台后,刷新并查询该条数据,前端会把它当成是js代码,就会弹框显示“1”这个提示。
解决方法:
xss拦截器类:
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.util.ValueStack;
/**
* Created by Administrator on 2019/8/15 0015.
* xss拦截器
*/
public class XssIntecepter extends AbstractInterceptor {
/**
* 将容易引起xss漏洞的半角字符直接替换成全角字符
*
* @param s
* @return
*/
public static String xssEncode(String s) {
if (s == null || s.isEmpty()) {
return s;
}
StringBuilder sb = new StringBuilder(s.length() + 16);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '>':
sb.append('>');// 全角大于号
break;
case '<':
sb.append('<');// 全角小于号
break;
case '(':
sb.append('(');// 全角
break;
case ')':
sb.append(')');// 全角
break;
case '&':
sb.append('&');// 全角
break;
case '\\':
sb.append('\');// 全角斜线
break;
case '#':
sb.append('#');// 全角井号
break;
case '%': // < 字符的 URL 编码形式表示的 ASCII 字符(十六进制格式) 是: %3c
processUrlEncoder(sb, s, i);
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
private static void processUrlEncoder(StringBuilder sb, String s, int index) {
if (s.length() >= index + 2) {
if (s.charAt(index + 1) == '3' && (s.charAt(index + 2) == 'c' || s.charAt(index + 2) == 'C')) { // %3c, %3C
sb.append('<');
return;
}
if (s.charAt(index + 1) == '6' && s.charAt(index + 2) == '0') { // %3c (0x3c=60)
sb.append('<');
return;
}
if (s.charAt(index + 1) == '3' && (s.charAt(index + 2) == 'e' || s.charAt(index + 2) == 'E')) { // %3e, %3E
sb.append('>');
return;
}
if (s.charAt(index + 1) == '6' && s.charAt(index + 2) == '2') { // %3e (0x3e=62)
sb.append('>');
return;
}
}
sb.append(s.charAt(index));
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// 通过核心调度器invocation来获得调度的Action上下文
ActionContext actionContext = invocation.getInvocationContext();
// 获取Action上下文的值栈
ValueStack stack = actionContext.getValueStack();
// 获取上下文的请求参数
Map valueTreeMap = actionContext.getParameters();
// 获得请求参数集合的迭代器
Iterator iterator = valueTreeMap.entrySet().iterator();
// 遍历组装请求参数
while (iterator.hasNext()) {
// 获得迭代的键值对
Map.Entry entry = (Map.Entry) iterator.next();
// 获得键值对中的键值
String key = (String) entry.getKey();
// 原请求参数,因为有可能一键对多值所以这里用的String[]
String[] oldValues = null;
// 对参数值转换成String类型的
if (entry.getValue() instanceof String) {
oldValues = new String[] { entry.getValue().toString() };
} else {
oldValues = (String[]) entry.getValue();
}
// 处理后的请求参数
String[] newValueStr = new String[oldValues.length];
// 对请求参数过滤处理
if (oldValues.length >= 1) {
for (int i = 0; i < oldValues.length; i++) {
// 替换掉非法参数,这里只替换掉了',如有其他需求,可以专门写一个处理字符的类
newValueStr[i] = xssEncode(oldValues[i].toString());
}
} else {
newValueStr = null;
}
// 处理后的请求参数加入值栈中
stack.setValue(key, newValueStr);
}
String result = null;
try {
// 调用下一个拦截器,如果拦截器不存在,则执行Action
result = invocation.invoke();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
struts.xml中拦截器的配置:
<interceptors>
<!--xss攻击拦截器-->
<interceptor name="xssIntecepter" class="com.xmgrid.view.common.XssIntecepter" />
<!-- 定义拦截栈 -->
<interceptor-stack name="mydefault">
<!-- 指定引用的拦截器 -->
<interceptor-ref name="xssIntecepter" />
</interceptor-stack>
</interceptors>
<!-- 定义默认拦截器 -->
<default-interceptor-ref name="mydefault" />
以上就是xss攻击解决的办法,思路主要是获取前端传到后端的一些参数值并替换里面一些比较特殊的字符比如说“<”、“>”等等,并将替换后的数据保存到数据库中,这样前端获从后台获取这些数据的时候,就不会将它们判定为js代码。
总结
试过了CSDN的好几个方法,都没能成功的解决xss漏洞,其中占多数的有filter用过滤器拦截的方法,在我亲身尝试中,重写了filter中的方法比如说getParam等,都没能获取到表单里传到后端的数据,最后才采用了拦截器的方法。
在过程中,还遇到了拦截器类中给代码过滤特殊值的stack.setValue(key, newValueStr);方法报错的问题:
由于没有找到该属性,在设置值的时候抛出了异常。最开始因为系统报错会直接跳转到一个错误页面,没有直接的报错,所以也在这个问题上耽搁了挺长时间。最后的解决办法是<!-- 是否为开发模式 --> <constant name="struts.devMode" value="true" />
将是否为开发模式改成<constant name="struts.devMode" value="false" />
,把模式设置为非开发模式即可解决这个问题。网上还有的说可以用get、set来解决这个办法,这个方法暂时还没有去尝试过。