1 XSS攻击
1.1 攻击方式
XSS攻击,比如当我们在某个网站的论台的文本输入框中输入了,一个脚本以后。当我们提交这个脚本保存,或者跳页面
展示这个脚本数据的时候,会发现jsp已经把这段你提交的脚本进行了执行。通过这种方式,黑客可以执行任何它么喜欢的js脚本
比如
<script>alert('sss')</script>
<script>window.location.href='http://www.baidu.com';</script>
比如上面的代码。当你把它当做数据展示在jsp页面的时候。浏览器就会把它当成脚本直接执行。严重的情况下。黑客可以通过
它盗取cookies,获取用户信息等等的操作。这个要看到效果,首先这段脚本数据要到服务端,再返回界面。这样就会弹出来了。
1.2 防御方式
xss攻击的防御。如何防止浏览器把你想要展示出来的js脚本。当成可执行脚本执行呢。首先我们要添加拦截器。或者过滤器。
网关等。通过拦截你输入。或者提交的请求参数或者查询参数进行html的转意。把>,<,& 等等的网页特殊服号。转意为>,<等浏览器识别符。还有一点值得提的是google浏览器好像对防止xss攻击做了容错。所以大家可以到火狐浏览器,360浏览器上进行测试。
1.3 解决方式
1 首先引入common-lang.jar包
2 重现request的getparamter方法
@Override
public String getParameter(String name) {
// 过滤getParameter参数 检查是否有特殊字符
String value = super.getParameter(name);
System.out.println("value:" + value);
if (!StringUtils.isEmpty(value)) {
// 将中文转换为字符编码格式,将特殊字符变为html源代码保存
value = StringEscapeUtils.escapeHtml(value);
System.out.println("newValue:" + value);
}
return value;
}
如上代码片段。这个通过在filter或者拦截器中加入,特殊服号转意的操作。接可以把提交的参数。进行html源数据转换。这个时候你在数据库中存放的就是转意过后的数据。展示到页面的时候就不会执行了。
2 sql注入
2.1 攻击方式
sql注入是由于我们的程序开发人员写代码的时候的程序漏洞。造成了黑客可以通过表单输入跨域数据权限。或者跨域访问权限的问题。那么具体怎么操作呢我们可以来看看sql注入的问题是怎么发生的
public interface UserMapper {
@Select(" SELECT * FROM user_info where userName='${userName}' and password='${password}'")
public UserEntity login(UserEntity userEntity);
}
如上代码片段是我们同构mybatis的$符号拼接的登陆用户查询的sql语句。正如大家对mybatis的了解。大家都知道$符号
就相当于我们在代码了写
StringBuffer sbt=new StringBuffer);
sbt.append(" SELECT * FROM user_info ");
sbt.append(" where userName='"+username+"' ");
sbt.append(" and password='"+password+"' ");
如上所示这样的一些sql代码段一样。那么好这个时候假设我们看一个正常的请求。
http://127.0.0.1:8080/login?userName=liusi&password=123
没错这个登录请求是没问题的。但是接下来我们来做一些操作
http://127.0.0.1:8080/login?userName=liusi&password=123 ' or 1='1
如上所示如果理解mysql的用户的话可以,通过在密码的输入框中进行特殊服号的拼接
"SELECT * FROM user_info where userName='liusi' and password='123 ' or 1='1'"
那么正如你所看到的上面这条sql一样。这条sql就是一条可以在任何情况下都可以正常执行成功的sql。
好的如上所示就是我们的sql注入。那么也许你会说我们可以用来干什么。对于黑客来说我没必要这样来登录你的
网站。我直接注册进去你的网站多方便。
那么这个时候每一个用户在一个网站来说都有自己有限的数据访问权限。但是你提供的查询框却没有做sql防止注入的处理
这个时候黑客通过拼接sql就可以发现他权限范围以外的数据了。这就是sql注入的危害。数据访问越权等等。当然越权删除数据
也自然不成问题了。
2.2 防御方式
首先就mybatis我们可以看到。可以把$这个符号换成#号,这样就可以把原生的sql编程预编译的sql了。通过占位符。加上
参数的方式就可以防止sql注入了
"SELECT * FROM user_info where userName=? and password=? "
通过如上所示的预编译方式。加上传递参数的方式。就可以预防sql注入的方式了。
3 普通工具请求伪造
[步骤]
例如经销商id =57029,在后台通过投放抓包到投放接口,并篡改接口数据,可以给经销商id =57024 新增厂商 = 东风乘用车 的投放需求(57024的经销商并没有主营 496的厂商车系);同时也可以通过接口随意篡改任何经销商的投放。
接口:http://dev60.pcauto.com.cn:8080/dealer/advertising/createOrUpdateDealerLaunch.do
参数:
dealerId=57024&startDate=2018-11-30&endDate=2018-12-08&budget=350.0&manuId=496&serialId=10821&serialId=11021&serialId=7679&serialId=12523&serialId=21223&serialId=14242&serialId=10811&serialId=11747&serialId=21603&serialId=4445&serialId=10856&serialId=3904&serialId=4565&open=0&open=0&open=1&open=1&open=1&open=1&open=1&open=1&open=1&open=1&open=1&open=1&open=1&serialName=%E4%B8%9C%E9%A3%8EA9&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9EA30&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9EA60&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9EAX3&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9EAX4&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9EAX5&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9EAX7&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9EE30&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9EE70&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9EH30&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9EL60&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9ES30(%E5%81%9C%E5%94%AE)&serialName=%E4%B8%9C%E9%A3%8E%E9%A3%8E%E7%A5%9ETai-Concept(%E6%A6%82%E5%BF%B5%E8%BD%A6)&launchId=305
解决思路:
主界面入口的时候后端生成加密的token绑定用户id,通过cookies把请求的token带到页面端存储。后面每一次请求或者说每一次核心的修改请求都把页面端从后台带过到服务端的token进行url的拦截,然后取出后端缓存中的token与用户Id做校验。其他数据篡改,要做相应的前后台校验。这种能够解决大部份的请求伪造。但是还解决不了csrf,跨站请求伪造问题。token的方式,同样适用于表单重复提交。
入口
public String addLoginDealer(long dealerId, HttpServletResponse response){
String token = T.genetateToken();
ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
opsForValue.set(String.valueOf(dealerId) + "-token", token, 1800, TimeUnit.SECONDS);
String salt = DealerTokenKey.getDealerSalt();
token = salt.charAt(1) + salt.charAt(3) + token + salt.charAt(6) + salt.charAt(4);
String mdToken = MD5Util.encrypt(token);
Cookie cookie = new Cookie(DealerTokenKey.getCookiNameToken(), mdToken);
cookie.setMaxAge(1800);
cookie.setPath("/");
response.addCookie(cookie);
return token;
}
拦截校验
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String dealerId = getDealerId(request);
if(T.isBlank(dealerId)) {
StringBuffer requestURL = request.getRequestURL();
logger.info("url:="+requestURL.toString()+": dealerId:="+dealerId);
response.sendRedirect(PropertiestTool.getInstance().get("local_domain")+"/dealer/index/prev.do");
return false;
}
ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
String token = opsForValue.get(String.valueOf(dealerId) + "-token");
logger.info("redis check login:="+token);
if(T.isBlank(token)){
response.sendRedirect(PropertiestTool.getInstance().get("local_domain")+"/dealer/index/prev.do");
return false;
}
Cookie cookie = getCookieValue(request, DealerTokenKey.getCookiNameToken());
if (cookie == null){
response.sendRedirect(PropertiestTool.getInstance().get("local_domain")+"/dealer/index/prev.do");
return false;
}
String cookieValue = cookie.getValue();
logger.info("dealerId:" + dealerId + " - cookie value:=" + cookieValue);
if(T.isBlank(cookieValue)){
response.sendRedirect(PropertiestTool.getInstance().get("local_domain")+"/dealer/index/prev.do");
return false;
}
String salt = DealerTokenKey.getDealerSalt();
String mdToken = MD5Util.encrypt(salt.charAt(1) + salt.charAt(3) + token + salt.charAt(6) + salt.charAt(4));
if (!cookieValue.equals(mdToken)){
response.sendRedirect(PropertiestTool.getInstance().get("local_domain")+"/dealer/index/prev.do");
return false;
}
opsForValue.set(String.valueOf(dealerId) + "-token", token, 1800, TimeUnit.SECONDS);
cookie.setMaxAge(1800);
cookie.setPath("/");
response.addCookie(cookie);
return true;
}
4 跨站请求伪造(CSRF)
CSRF(Cross-site request forgery),中文名称:跨站请求伪造.
因为这个不是用户真正想发出的请求,这就是所谓的请求伪造;因为这些请求也是可以从第三方网站提交的,所以前缀跨站二字。
CSRF发生的场景如下图所示:
4.1 攻击方式。
如上图所以叫做跨站请求伪造。就是因为他这个请求伪造跟我们上面的请求伪造不一样。上面普通的请求伪造已经解决不了这样的跨站请求伪造问题了。但是他们都是请求伪造。解决方式大体相同。一般这样的情况发生在黑客想攻击你的网站。借助一个跟你类似的网站。植入你网站相同的调用接口。用户在访问类似网站。未知的情况下。先登录了一个信任的网站。比如我喜欢上淘宝网。这个时候你下订单或者支付的接口。被类似京东这样一类的相同性质的网站植入了支付接口地址,或者下单接口地址。这个时候你不小心打开了京东的页面。点击了同地址的接口。就在淘宝上下了一个一模一样的订单。这个就是跨站请求伪造问题。它不同于普通的请求伪造。通过cookies中植入token的方式已经解决不聊了。只要是同样的请求地址。cookies一样会被带到服务器断。
4.2 防御方式
1,利用referer判断,
通过获取请求来源,获取到referer的地址,通过域名比较来决定是否允许请求。一般来说对于是跨站的访问自然请求的源域名肯定是不一致的。但是用户有可能设置浏览器使其在发送请求时不提供 Referer,这样的用户也将不能访问网站。
2,在请求中添加 token 并验证
关键在于在请求中放入黑客所不能伪造的信息,并且该不能通过 cookie 来传递,因为是同一个会话。一个绘画里面有相同
的cookies,相同的请求地址,cookies将会被带回到服务器中。可以在服务器端生成一个随机码,然后通过参数放进request,
session,application域对象中,再通过form的hidden元素中,form提交的时候在服务器端检查。token的方式同样适用于表单重复提交。
5 防盗链
5.1 防盗链攻击方式
故名思议 防盗链意思就是防止其他网站把放在我的网站的文件链接盗走了。那么什么意思呢。比如我把我的图片上传到
我的图片服务器。供给自己用。包括视频。类似的网站比如视频教程网站等的。如果不放置我把链接盗走。那么我只要拿到网站
jsp页面端视频的地址链接就可以在网页浏览器地址中进行播放了。或者说我看见某个网站上有票亮的图片就可以通过http请求抓
到我的网站上用了,那么要怎么做呢。直接通过jsp页面开发者方式访问获取图片地址就可以了。虽然这个影响并不太。但是这样如果大量的盗用的话,就会占用到你服务器的带宽,让你的服务器变慢等等。
5.2 防御方式
这个就是跨站攻击问题。一样的处理方式。通过设置过滤器。拿到请求源的referer就可以获取到访问的域名。
通过获取到的域名。进行域名与本地域名比较。如果不是你白名单允许访问的域名。那么就返回一个盗取图片的页面。
@WebFilter(filterName = "imgFilter", urlPatterns = "/imgs/*")
public class ImgFilter implements Filter {
@Value("${domain.name}")
private String domainName;
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String referer = req.getHeader("Referer");
if (StringUtils.isEmpty(referer)) {
request.getRequestDispatcher("/imgs/error.png").forward(request, response);
return;
}
String domain = getDomain(referer);
if (!domain.equals(domainName)) {
request.getRequestDispatcher("/imgs/error.png").forward(request, response);
return;
}
chain.doFilter(request, response);
}
/**
* 获取url对应的域名
*
* @param url
* @return
*/
public String getDomain(String url) {
String result = "";
int j = 0, startIndex = 0, endIndex = 0;
for (int i = 0; i < url.length(); i++) {
if (url.charAt(i) == '/') {
j++;
if (j == 2)
startIndex = i;
else if (j == 3)
endIndex = i;
}
}
result = url.substring(startIndex + 1, endIndex);
return result;
}
public void destroy() {
}
}
如上代码所示,就是放盗链问题解决方式的代码。
6 上传下载漏洞
6.1 攻击方式
上传,下载漏洞这个顾名思义,就是攻击者通过上传木马文件,直接得到WEBSHELL,危害等级超级高,现在的入侵中上传漏洞也是常见的漏洞。导致该漏洞的原因在于代码作者没有对访客提交的数据进行检验或者过滤不严,可以直接提交修改过的数据绕过扩展名的检验。比如黑客通过上传可执行的脚本文件。再通过特殊工具访问上传的脚本文件。就可以通过脚本随意的操作你的服务器了。
a.jsp
<%@page import="java.io.File"%>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
String root ="F:/";
File file = new File(root);
file.delete();
%>
如上图所示,是我们上传到图片服务器的jsp文件。这个脚本就是通过可执行脚本删除指定目录下面的文件。那么如果我们上传到
服务器的某个存储目录上。服务器给我们返回了存储的地址。我们再通过在浏览器中访问这个jsp调用他执行。不就可以通过代码删除执行目录下面的文件了吗。当然这个代码也必须在tomcat服务器下才能执行。那么如果我上传的是.sh,.exe.或者其他可以执行的脚本。或者通过工具终端调用可修改的呢。那么我就可以随意更改目录了。
还有如果我通过在文件路径中加上../../a.zip这样的地址路径进行上传。或者下载的测试。那么就会造成。跨目录下载或者跨目录上传。这样也是不安全的。这就是通过文件上传下载带给我们的攻击方式。
6.2 防御方式
前后台上传文件,都对文件的文件格式做出限制,限制它上传限定范围内的文件。当然这个用户通过更改后缀名也是可以绕过校验的所以这里尽量借助工具进行判断。
竟量使用图片服务器,或者视频服务器,进行存储。配置访问的黑名单,白名单。不要跟代码放在同一个路径下面。
严格进行文件大小的限制。防止黑客上传太大的文件。
通过replaceAll("../","") 对目录僭越进行控制
/**
* 文件上传
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
String root = request.getServletContext().getRealPath("/upload");
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
try {
List<FileItem> list = upload.parseRequest(request);
for (FileItem it : list) {
// 如果是file文件类型
if (!it.isFormField()) {
FileType fileType = getFileType(it.getInputStream());
if (fileType == null) {
// 非图片格式
response.getWriter().write("fail");
return;
}
String imgValue = fileType.getValue();
System.out.println("imgValue:" + imgValue);
// 是图片格式
it.write(new File(root + "/" + it.getName()));
response.getWriter().write("success");
}
}
} catch (Exception e) {
try {
response.getWriter().write("exception");
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
// 判断文件是图片格式
public static FileType getFileType(InputStream is) throws IOException {
byte[] src = new byte[28];
is.read(src, 0, 28);
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v).toUpperCase();
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
FileType[] fileTypes = FileType.values();
for (FileType fileType : fileTypes) {
if (stringBuilder.toString().startsWith(fileType.getValue())) {
return fileType;
}
}
return null;
}
7 白名单与黑名单
攻击方式。
有的网站开发了一些接口,给自己的手机应用用。或者开发了一些接口给指定的网站用。又或者开发的接口就是用来执行定时任务的,却没有做幂等性的问题(这里建议,凡是定时任务接口都要做幂等性处理,因为方便手动进行定时任务的不补跑),如果不对这些接口进行屏蔽。被黑客通过工具所描识别到了,就可以恶意调用了
解决方案
通过制定允许访问的IP做为白名单。或者通过屏蔽掉高频访问的IP,放入黑名单。对IP进行黑名单,或者白名单的拦截。通过为app创建appid来进行接口的数据交换或者。指定的报文头来作为自己app的唯一标识。防止恶意访问。
8 其他小漏洞
比如我们的应用报错信息,尽量不要把报错的脚本展示到界面上。包括sql脚本等的。比如哪个表缺了哪些字段等等的。让黑客钻了空子。竟量给一个统一的异常处理。并且在后台打印日志。通过这样的方式才能够帮助到错误的定位。
比如一些涉及到个人密码修改,一些进行支付,的核心涉及到钱的操作。可以通过调用短信接口,向客户发送验证码进行手机
验证码的验证识别。当然验证码不建议就是个几位的数字,这样容易被暴力破解。建议加上各种特殊字符。