web系统测试工具:APPScan
1.不充分账户封锁
(一) 安全问题描述
发送了两次合法的登录尝试,并且在其间发送了几次错误的登录尝试。最后一个响应与第一个响应相同。这表明存在未充分实施帐户封锁的情况,从而使登录页面可能受到蛮力攻击
解决方案
效果,当用户登录错误3次,则提醒30分钟后才可再次登录
if(!checkLock(session, username)) {
System.out.println("该账号已经被锁定");
return "该账号已经被锁定";
}
if (username=="小一"&&password=="1234"){
//登录成功
System.out.println("登录成功");
User user=new User();
user.setUsername(username);
user.setPassword(password);
System.out.println("登录成功");
cleanFailNum(request.getSession(),username);//清空该用户之前登录的错误信息
return "登录成功";
}else {
addFailNum(session,username);//错误信息加一
return "用户名或者密码错误";
}
/**
* 校验用户登录失败次数
* @param session
* @param username
* @return
*/
public boolean checkLock(HttpSession session,String username) {
Object o = session.getServletContext().getAttribute(username);
if(o==null) {
return true;
}
HashMap<String,Object> map = (HashMap<String, Object>) o;
int num = (int) map.get("num");
Date date = (Date) map.get("lastDate");
long timeDifference = ((new Date().getTime()-date.getTime())/60/1000);
if(num>=3&&timeDifference<30) {
return false;
}
return true;
}
/**
* 新增用户登录失败次数
* @param session
* @param username
*/
public void addFailNum(HttpSession session, String username) {
Object o = session.getServletContext().getAttribute(username);
HashMap<String,Object> map = null;
int num= 0;
if(o==null) {
map = new HashMap<String,Object>();
}else {
map = (HashMap<String, Object>) o;
num = (int) map.get("num");
Date date = (Date) map.get("lastDate");
long timeDifference = ((new Date().getTime()-date.getTime())/60/1000);
if(timeDifference>=30) {
num=0;
}
}
map.put("num", num+1);
map.put("lastDate", new Date());
session.getServletContext().setAttribute(username, map);
}
/**
* 清理用户登录失败的记录
* @param session
* @param username
*/
public void cleanFailNum(HttpSession session, String username) {
session.getServletContext().removeAttribute(username);
}
2.会话标识未更新
安全规范要求
COOKIE中的登陆前JSESSIONID与登陆后JESSIONID不能相同。(只有J2EE应用服务器为JESSIONID,其他应用服务器可能不同)
简单理解就是登录前和登录后的sessionId要更新
方式一:在登录页面操作
<%
//登录之后更改会话标识符
request.getSession().invalidate();//清空session
Cookie cookie = request.getCookies()[0];//获取cookie
cookie.setMaxAge(0);//s让cookie过期
%>
方式二:登录方法中头加入操作
session.invalidate();
3.跨站点请求伪造
import org.apache.logging.log4j.core.config.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lhl
* @version 1.0
* @date 2020/5/6 16:47
* @description TODO 拦截跨站点请求伪造
*/
@Order(1)
@WebFilter(filterName = "CSRFFilter", urlPatterns = "/*")
public class CSRFFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("==============CSRFFilter=================");
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (checkReferer(req, resp)) {
// 拦截
response.setContentType("text/html;charset=utf-8");
// resp.sendRedirect("/login.jsp");
throw new ServletException("非法请求!");
// return;
}
// 放行
chain.doFilter(req, resp);
}
private boolean checkReferer(HttpServletRequest request, HttpServletResponse response) {
String referer = request.getHeader("referer");
String serviceName = request.getServerName();
if (null == referer) {
return false;
}
if (referer != null && referer.matches("^(http|https)+://(" + serviceName + ")(.*)")) {
// 本系统的访问 直接放行
return false;
}
if (referer != null && referer
.matches("^[(http)|(https)]+://(localhost|10\\.|172\\.|127\\.0\\.0\\.1)+(.*)")) {
// 内网的访问 直接放行
return false;
}
return true;
}
@Override
public void destroy() {
}
}
4.“Content-Security-Policy”头缺失或不安全
解决方案1
中间件为IIS,网站根目录下找到“web.cofig”文件,没有则新建该文件。复制以下代码,粘贴到web.cofig文件中(新建全部复制,已有复制system.webserver)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Content-Security-Policy" value="default-src 'self';" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
解决方案2
HTML前端解决方法:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'"/>
5.“X-Content-Type-Options”头缺失或不安全
解决方案1.
中间件为IIS,网站根目录下找到“web.cofig”文件,没有则新建该文件。复制以下代码,粘贴到web.cofig文件中(新建全部复制,已有复制system.webserver)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="X-Content-Type-Options" value="nosniff"/>
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
解决方案2.
HTML前端解决方法:
<meta http-equiv="X-Content-Type-Options" content="nosniff" />
6.“X-XSS-Protection”头缺失或不安全
解决方案1.
中间件为IIS,网站根目录下找到“web.cofig”文件,没有则新建该文件。复制以下代码,粘贴到web.cofig文件中(新建全部复制,已有复制system.webserver)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="X-XSS-Protection" value="1;mode=block"/>
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
HTML前端解决方法:
<meta http-equiv="X-XSS-Protection" content="1; mode=block" />
检测到目标URL存在电子邮件地址模式
Spambot 搜寻因特网站点,开始查找电子邮件地址来构建发送自发电子邮件(垃圾邮件)的邮件列表。
如果检测到含有一或多个电子邮件地址的响应,可供利用以发送垃圾邮件。
而且,找到的电子邮件地址也可能是专用电子邮件地址,对于一般大众应是不可访问的。
解决方案
从 Web 站点中除去任何电子邮件地址,使恶意的用户无从利用。
关于内部IP暴露漏洞的解决
项目扫描遇到“内部IP暴露”漏洞,关于该漏洞的含义大家自行百度。
考虑的解决方法就是限制IP直接访问项目本身。具体做法如下:
项目是部署在Tomcat中,直接通过修改Tomcat配置文件中的server.xml实现。
初始配置为:
<pre name="code" class="html"><Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps/test1"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
</Host>
</Engine>
修改为
<Host name="项目实际域名" appBase="webapps/test1"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
</Host>
</Engine>
defaultHost是默认的,随便写点都行,应该是在域名无法发生作用的时候访问的。
通过上述配置,就可以实现禁止IP直接访问项目。
若完善一些,可以再配置一个Host,将IP访问指向特定的页面进行说明。
潜在文件上载
文件上传漏洞是指网络攻击者上传了一个可执行的文件到服务器上,当开发者没有对该文件进行合理的校验及处理的时候,很有可能让程序执行这个上传文件导致安全漏洞。大部分网站都会有文件上传的功能,例如头像、图片、视频等,这块的逻辑如果处理不当,很容易触发服务器漏洞
防御方法
防范文件上传漏洞常见的几种方法。
1、文件上传的目录设置为不可执行
2、判断文件类型
在判断文件类型时,可以结合使用MIME Type、后缀检查等方式。在文件类型检查中,强烈推荐白名单方式,黑名单的方式已经无数次被证明是不可靠的。此外,对于图片的处理,可以使用压缩函数或者resize函数,在处理图片的同时破坏图片中可能包含的HTML代码。
3、使用随机数改写文件名和文件路径
文件上传如果要执行代码,则需要用户能够访问到这个文件。在某些环境中,用户能上传,但不能访问。如果应用了随机数改写了文件名和路径,将极大地增加攻击的成本。再来就是像shell.php.rar.rar和crossdomain.xml这种文件,都将因为重命名而无法攻击。
4、单独设置文件服务器的域名
由于浏览器同源策略的关系,一系列客户端攻击将失效,比如上传crossdomain.xml、上传包含Javascript的XSS利用等问题将得到解决。
已解密的登录请求
【原因】
经过实验测试,认为是页面input 标签type="password" 导致的,
如果 改成type="text"是可以绕过漏洞扫描的,但是密码框的内容要自己隐藏,出现诸多业务bugs。
【解决】在原来的<input type="password" />上面加多一个
`<input id="fp" type="password" style="display:none;" autocomplete="off">
<input id="password" name="password" placeholder="请输入密码" type="password" autocomplete="off">
文件参数Shell命令注入
可能会在 Web 服务器上运行远程命令。这通常意味着完全破坏服务器及其内容
原因:未对用户输入正确执行危险字符清理
![](https://i-blog.csdnimg.cn/blog_migrate/d1324e92665c730591c8941d0d70a4d3.png)
跨站点脚本编制
XSS跨站脚本攻击:两种情况。一种通过外部输入然后直接在浏览器端触发,即反射型XSS;还有一种则是先把利用代码保存在数据库或文件中,当web程序读取利用代码并输出在页面上时触发漏洞,即存储型XSS。DOM型XSS是一种特殊的反射型XSS。
危害:前端页面能做的事它都能做。(不仅仅盗取cookie、修改页面等)
解决方案
(1) 特殊字符HTML实体转码。最好的过滤方式是在输出和二次调用的时候进行加HTML实体一类的转码,防止脚本注入。
(2) 标签事件属性黑名单。特殊字符容易被绕过,所以还得加标签事件得黑名单或者白名单,这里推荐使用白名单的方式,实现规则可以直接使用正则表达式来匹配,如果匹配到的事件不在白名单列表,就可以直接拦截,而不是过滤为空。
特殊字符XSS攻击
在过滤器中,Http所有请求参数,过滤,去除特殊字符对系统的影响;
(1)sql注入
(2)文件参数注入
private String cleanXSS(String value) {
//You'll need to remove the spaces from the html entities below
value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("e-xpression\\\\((.*?)\\\\)\"", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
//防止sql语句字符注入
String[] sqlStr = {"and", "exec", "execute", "insert", "select", "delete", "update", "count", "drop", "chr", "mid", "master", "truncate", "char", "declare", "sitename", "net user", "xp_cmdshell", "like", "and", "exec", "execute", "insert", "create", "drop", "table", "from", "grant", "use", "group_concat", "column_name", "information_schema.columns", "table_schema", "union", "where", "select", "delete", "update", "order", "by", "count", "chr", "mid", "master", "truncate", "char", "declare", "or"};
for (int i = 0; i < sqlStr.length; i++) {
value = value.replaceAll(sqlStr[i], "");
}
//文件参数注入
String[]cmdParameters={
"cmd.exe","/k","bin","sh",".sh","shell","sleep"
};
for (int i = 0; i < cmdParameters.length; i++) {
value = value.replaceAll(cmdParameters[i], "");
}
return value;
}
更多博客和源码请到微信公众号:源码plus 探索和领取。