1、sql注入
解决方法:通过使用MyBits等框架避免;获取参数值得地方对特殊字符进行过滤;
2、XSS攻击
反射型解决方法:向文档输出动态内容时,应根据输出位置进行正确的编码或转义;
存储型解决方法:数据库存储前过滤特殊字符进行正确的编码或转义;
/*
* Xss特殊字符过滤类,正则表达式可根据具体业务需求调整。
*/
public class XssShieldUtil {
private static List<Pattern> patterns = null;
private static List<Object[]> getXssPatternList() {
List<Object[]> ret = new ArrayList<Object[]>();
ret.add(new Object[] { "<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE });
ret.add(new Object[] { "eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL });
ret.add(new Object[] { "expression\\((.*?)\\)",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL });
//ret.add(new Object[] { "(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE });
ret.add(new Object[] { "<(\"[^\"]*\"|\'[^\']*\'|[^\'\">])*>",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL });
ret.add(new Object[] {
"window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\(\\)*",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL });
ret.add(new Object[] {
"<+\\s*\\w*\\s*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=+",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL });
ret.add(new Object[] {"[\" _`~!@#$^*()+|{}':;',<>~!@#¥%……&*()——+|{}]|\\n|\\r|\\t|LF|CR",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
return ret;
}
private static List<Pattern> getPatterns() {
if (patterns == null) {
List<Pattern> list = new ArrayList<Pattern>();
String regex = null;
Integer flag = null;
int arrLength = 0;
for (Object[] arr : getXssPatternList()) {
arrLength = arr.length;
for (int i = 0; i < arrLength; i++) {
regex = (String) arr[0];
flag = (Integer) arr[1];
list.add(Pattern.compile(regex, flag));
}
}
patterns = list;
}
return patterns;
}
// 存在特殊字符时,字符串置空
public static String stripXss(String value) {
if (StringUtils.isNotBlank(value)) {
Matcher matcher = null;
for (Pattern pattern : getPatterns()) {
matcher = pattern.matcher(value.toLowerCase());
// 匹配
if (matcher.find()) { // 删除相关字符串
value = "";
}
}
// value = value.replaceAll("<", "<").replaceAll(">", ">");
}
return value;
}
// 存在特殊字符时,返回true
public static boolean strXss(String value) {
boolean bool=false;
if (StringUtils.isNotBlank(value)) {
Matcher matcher = null;
for (Pattern pattern : getPatterns()) {
matcher = pattern.matcher(value.toLowerCase());
// 匹配
if (matcher.find()) { // 删除相关字符串
bool = true;
}
}
}
return bool;
}
/**
* 验证url
* @param str
* @return
*/
public static String isSpecialChar(String str) {
String regEx = "[\" _`~!@#$^*()+|{}':;',<>~!@#¥%……&*()——+|{}]|\\n|\\r|\\t|LF|CR";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(str);
if (m.find()) {
str = "";
}
return str;
}
// 方法测试
public static void main(String[] args) {
String value = null;
value =XssShieldUtil.stripXss("<script language=text/javascript>alert(document.cookie);</script>");
System.out.println(value);
}
}
3、越权操作
水平越权,仅通过js验证,对用户权限的检查不完善,权限的凭证可以被用户控制。
解决方法:在服务端严格检查进行操作的用户权限;
垂直越权,低权限操作高权限功能。
解决方法:sessionID进行功能比对。
4、任意文件上传
解决方法:禁止上传asp、cgi、aspx等,推荐使用白名单;
5、检测到目标URL存在跨站漏洞
跨站脚本攻击(也称为XSS)指利用网站漏洞从用户那里恶意盗取信息。
解决方法:设置过滤器,获取request请求参数,循环读取判断是否包含XSS攻击字符,如果存在返回403,不存在正常处理请求。
web.xml文件,配置过滤器
<filter>
<filter-name>jspFilter</filter-name>
<filter-class>com.XXX.filter.JspFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>jspFilter</filter-name>
<url-pattern>指定路径</url-pattern>
</filter-mapping>
过滤器JspFilter,获取request请求参数,循环读取判断是否包含XSS攻击字符,如果存在返回403,不存在正常处理请求。
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import com.ivan.utils.XssShieldUtil;
public class JspFilter implements Filter {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
// TODO Auto-generated method stub
Enumeration enu=request.getParameterNames();
while(enu.hasMoreElements()){
String paramName=(String)enu.nextElement();
String[] values=request.getParameterValues(paramName);
for(int i=0;i<values.length;i++){
if(XssShieldUtil.strXss(values[i])){
((HttpServletResponse) response).setStatus(403);
System.out.println("["+i+"] "+paramName+" "+values[i]);
return;
}
}
}
filterChain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
6、检测到目标URL存在http host头攻击漏洞
为了方便的获得网站域名,开发人员一般依赖于HTTP Host header。例如,在php里用_SERVER["HTTP_HOST"]。但是这个header是不可信赖的,如果应用程序没有对host header值进行处理,就有可能造成恶意代码的传入。
解决方法:web应用程序应该使用SERVER_NAME而不是host header。Java项目通过设置访问白名单处理,host不在此名单内的所有请求,将返回403.
web.xml中配置过滤器
<filter>
<filter-name>SessionFilter</filter-name>
<filter-class>com.XXX.filter.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
过滤器内容。
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ivan.utils.ServerWhiteListUtil;
public class SessionFilter implements Filter {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 头攻击检测
String requestHost = request.getServerName();
if (requestHost != null && !ServerWhiteListUtil.isWhite(requestHost)){
System.out.println("响应头Code:403");
response.setStatus(403);
return;
}else {
chain.doFilter(request, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
白名单对比工具类。
import java.util.ArrayList;
import java.util.List;
public class ServerWhiteListUtil {
private static List<String> whiteList = new ArrayList<String>();
static {
try {
String[] strarr = {"localhost:8080"};
for(int i=0;i<strarr.length;i++) {
whiteList.add(strarr[i]);
}
/* // 读取白名单列表
whiteList = new Gson().fromJson(
new InputStreamReader(ServerWhiteListUtil.class.getResourceAsStream("/resources/serverWhiteList.json")),
new TypeToken<List<String>>() {}.getType());*/
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 判断当前host是否在白名单内
* @param host 待查host
* @return boolean 是否在白名单内
*/
public static boolean isWhite(String host) {
if (whiteList == null || whiteList.size() == 0) {
return true;
}
for (String str : whiteList) {
if (str != null && str.equals(host)) {
return true;
}
}
return false;
}
}
7、点击劫持:X-Frame-Options未配置
点击劫持(ClickJacking)是一种视觉上的欺骗手段。攻击者使用一个透明的、不可见的iframe,覆盖在一个网页上,然后诱使用户在该网页上进行操作,此时用户将在不知情的情况下点击透明的iframe页面。通过调整iframe页面的位置,可以诱使用户恰好点击在iframe页面的一些功能性按钮上。
HTTP 响应头信息中的X-Frame-Options,可以指示浏览器是否应该加载一个 iframe 中的页面。如果服务器响应头信息中没有X-Frame-Options,则该网站存在ClickJacking攻击风险。网站可以通过设置 X-Frame-Options 阻止站点内的页面被其他页面嵌入从而防止点击劫持。
解决方法:修改web服务器配置,添加X-Frame-Options响应头。赋值有如下三种:
1、DENY:不能被嵌入到任何iframe或者frame中。
2、SAMEORIGIN:页面只能被本站页面嵌入到iframe或者frame中。
3、ALLOW-FROM uri:只能被嵌入到指定域名的框架中。
web.xml 配置
<filter>
<filter-name>httpHeaderSecurity</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<init-param>
<param-name>antiClickJackingOption</param-name>
<param-value>SAMEORIGIN</param-value>
<!-- <param-value>DENY</param-value> -->
</init-param>
<async-supported>true</async-supported>
</filter>
8、CSRF跨站请求伪造
攻击者通过伪造来自受信任用户的请求,达到增加、删除、篡改网站内容的目的。
解决方法:请求地址中添加token并验证(token不放在cookie中,放在http请求参数中,服务端对其进行验证);将token加入http头属性中,避免了token出现在浏览器中,被泄露。
web.xml配置过滤器
<!-- 用于生成token值 -->
<filter>
<filter-name>cookieHttpFilter</filter-name>
<filter-class>com.XXX.filter.CookieHttpFilter</filter-class>
</filter>
<!-- 用于验证token值 -->
<filter>
<filter-name>csrfFilter</filter-name>
<filter-class>com.XXX.filter.CsrfFilter</filter-class>
</filter>
token值是根据系统当前时间,通过base64进行编码生成,存放在session中。
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class CookieHttpFilter implements Filter {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest req = (HttpServletRequest) request;
Cookie[] cookies = req.getCookies();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");//设置日期格式
HttpSession session = req.getSession();
if(cookies!=null){
final Base64.Encoder encoder = Base64.getEncoder();
byte[] tokenByte = df.format(new Date()).getBytes("UTF-8");
String token=encoder.encodeToString(tokenByte);
session.setAttribute("csrftoken", token);
}
filterChain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
通过request获取session值,通过session的key获取token值,通过token值进行验证,验证通过则继续处理请求,验证不通过,则返回403.
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class CsrfFilter implements Filter {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest req = (HttpServletRequest)request;
HttpSession s = req.getSession();
// 从 session 中得到 csrftoken 属性
String sToken = (String)s.getAttribute("csrftoken");
Cookie[] cookies = req.getCookies();
if(cookies != null){
if(sToken == null){
((HttpServletResponse) response).setStatus(403);
} else{
final Base64.Encoder encoder = Base64.getEncoder();
// 生成token值
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");//设置日期格式
byte[] tokenByte = df.format(new Date()).getBytes("UTF-8");
String xhrToken = encoder.encodeToString(tokenByte);
if(xhrToken != null && sToken.equals(xhrToken)){
chain.doFilter(request, response);
}else{
((HttpServletResponse) response).setStatus(403);
}
}
}else{
chain.doFilter(request, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
9、控制器不允许直接访问jsp
web.xml中配置
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint>
</auth-constraint>
</security-constraint>
参考资料:https://blog.youkuaiyun.com/tc2015/article/details/51774454
10、其他
信息泄露,使用合适的错误页面;
目录遍历,字符串拼接下载,利用../来遍历目录;
整数溢出,计算功能,对输入值进行合理验证,错误页面的信息进行处理。
11、图形验证码绕过
图形验证码绕过指用户可使用相同的图形验证码发送多次登录请求,便于进行暴力破解。
解决方法:后台生成图形验证码,每次登录请求后均刷新验证码。
-- 1. HTML代码
<img id="codeImg" alt="点击更换" title="点击更换" src="" />
<input type="text" id="code_input" value="" placeholder="图形验证码"/>
-- 2. JS代码
<script src="/${res}/js/jquery-3.3.1.min.js" type="text/javascript"></script>
<script>
$(document).ready(function() {
changeCode();
setInterval(function(){
changeCode();
},90*1000)
$("#codeImg").bind("click", changeCode);
});
function genTimestamp() {
var time = new Date();
return time.getTime();
}
function changeCode() {
$("#codeImg").attr("src", "/code.jspx?t=" + genTimestamp());
}
</script>
<script charset="utf-8" type="text/javascript" src="js/login.js" ></script>
<script>
$('#login-button').click(function (event) {
event.preventDefault();
//$('form').fadeOut(500);
$('.wrapper').addClass('form-success');
});
</script>
-- 3. JAVA代码-图形验证码生成(获取限制:验证码获取次数大于5次,锁定3分钟。)
@RequestMapping("/code.jspx")
public void generate(HttpServletRequest request,HttpServletResponse response){
String requestHost = request.getServerName();
//验证码获取次数大于5次,锁定3分钟。
Integer codeReqNum = (Integer)request.getSession().getAttribute(requestHost);// KEY使用访问者IP
if(codeReqNum==null) {
request.getSession().setAttribute(requestHost, 1);
generateCode(response);
}else {
long currentTime = System.currentTimeMillis();
Long jishi = (Long)request.getSession().getAttribute(requestHost+"jishi");
if(codeReqNum>=5) {
if(jishi!= null && currentTime-jishi<180000) {
try {
response.setContentType("text/html;charset=UTF-8");// 用于解决参数乱码
response.setCharacterEncoding("UTF-8");
response.getWriter().write("验证码获取次数大于5次,请5分钟后重试");
response.getWriter().close();
} catch (IOException e) {
e.printStackTrace();
}
}else {
request.getSession().setAttribute(requestHost+"jishi",System.currentTimeMillis());
request.getSession().setAttribute(requestHost, codeReqNum+1);
generateCode(response);
}
}else {
request.getSession().setAttribute(requestHost+"jishi",System.currentTimeMillis());
request.getSession().setAttribute(requestHost, codeReqNum+1);
generateCode(response);
}
}
}
private void generateCode(HttpServletResponse response){
ByteArrayOutputStream output = new ByteArrayOutputStream();
String code = drawImg(output);
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
session.setAttribute("codeStr", code);
try {
ServletOutputStream out = response.getOutputStream();
output.writeTo(out);
} catch (IOException e) {
e.printStackTrace();
}
}
private String drawImg(ByteArrayOutputStream output){
String code = "";
for(int i=0; i<4; i++){
code += randomChar();
}
int width = 70;
int height = 25;
//生成图片
BufferedImage bi = new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR);
Font font = new Font("Times New Roman",Font.PLAIN,20);
//修改图片
Graphics2D g = bi.createGraphics();
g.setFont(font);
Color color = new Color(66,2,82);
g.setColor(color);
g.setBackground(new Color(226,226,240));
g.clearRect(0, 0, width, height);
FontRenderContext context = g.getFontRenderContext();
Rectangle2D bounds = font.getStringBounds(code, context);
double x = (width - bounds.getWidth()) / 2;
double y = (height - bounds.getHeight()) / 2;
double ascent = bounds.getY();
double baseY = y - ascent;
g.drawString(code, (int)x, (int)baseY);
g.dispose();
try {
ImageIO.write(bi, "jpg", output);
} catch (IOException e) {
e.printStackTrace();
}
return code;
}
/**
* 获取单个字符(验证码)
* @return
*/
private char randomChar(){
Random r = new Random();
String s = "ABCDEFGHJKLMNPRSTUVWXYZ0123456789";
return s.charAt(r.nextInt(s.length()));
}
-- 4. 登录验证后,及时更新session中的验证码
String codestr=(String) request.getSession().getAttribute("codeStr");
if(code_input == null ? codestr == null :codestr.equalsIgnoreCase(code_input)) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
request.getSession().setAttribute("codeStr", drawImg(output));;// 登录后刷新图形验证码
// 登录代码
。。。。。。
}else {
log.error(new String(df.format(new Date()).getBytes("UTF-8"))+"PC端图形验证错误。");
msg="图形验证码错误,请重新获取。<a href='"+url+"'>返回登录页</a>";
response.setContentType("text/html;charset=UTF-8");// 用于解决参数乱码
response.setCharacterEncoding("UTF-8");
response.getWriter().write(msg);
return null;
}