防盗链技术html
CSRF(模拟请求)前端
分析防止伪造Token请求攻击java
互联网API接口幂等性设计mysql
忘记密码漏洞分析nginx
好比A网站有一张图片,被B网站直接经过img标签属性引入,直接盗用A网站图片展现。web
若是别人的项目频繁引用个人图片的话 别人请求放访问的是个人 服务器 也会浪费个人宽带redis
判断http请求头Referer域中的记录来源的值,若是和当前访问的域名不一致的状况下,说明该图片可能被其余服务器盗用。spring
Referer字段中记录了访问的来源(浏览器访问连接地址)sql
http协议中: 请求头 请求体 请求 数据库
至关于限制资源(图片、文字) 只能在某个域名(限制某个服务器)来源进行访问
在互联网本质通信底层socket技术,socket技术里面二进制文件流传输,不管是图片仍是视频都是玩的二进制文件流进行传输
补充若是看不到请求头:
从哪一个访问地址过来的信息 如上图所示
A项目(服务器A):
maven:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <!-- SpringBoot 对lombok 支持 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- SpringBoot web 核心组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <!-- SpringBoot 外部tomcat支持 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <!-- springboot-log4j --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> <version>1.3.8.RELEASE</version> </dependency> <!-- springboot-aop 技术 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies>
Controller:
@Controller public class JspController { private static final Logger logger = LoggerFactory.getLogger(JspController.class); @RequestMapping("/jspIndex") public String jspIndex() { logger.info("springboot 集成logger 日志成功!!!"); return "jspIndex"; } }
jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h1>我是A项目....</h1> <img alt="" src="http://toov5.test1.cc:8080/imgs/01.png"> </body> </html>
能够看到 静态资源访问的路径:
http://toov5.test1.cc:8080/imgs/01.png
B项目(服务器B): 过滤器 + 静态资源
maven:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <!-- mysql 依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- SpringBoot 对lombok 支持 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- SpringBoot web 核心组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <!-- SpringBoot 外部tomcat支持 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <!-- springboot-log4j --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> <version>1.3.8.RELEASE</version> </dependency> <!-- springboot-aop 技术 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang --> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> </dependencies>
Filter:
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.annotation.WebFilter; import org.apache.catalina.servlet4preview.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Value; // 图片防盗链 @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 { System.out.println("filter"); // 1.获取请求头中的来源字段 HttpServletRequest req = (HttpServletRequest) request; String referer = req.getHeader("Referer"); if (StringUtils.isEmpty(referer)) { request.getRequestDispatcher("/imgs/error.png").forward(req, response); return; } // 2.判断请求头中的域名是否和限制的域名一致 String domainUrl = getDomain(referer); System.out.println(domainUrl); // 正常状况 黑名单 白名单接口 if (!domainUrl.equals(domainName)) { request.getRequestDispatcher("/imgs/error.png").forward(req, response); //实际项目中这里是 从数据库查询出来的结果 不是写死的这样的 return; } // 直接放行图片 chain.doFilter(req, response); } /** * 获取url对应的域名 */ 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() { // TODO Auto-generated method stub } }
须要过滤处理的域名:
spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp domain.name=toov5.test1.cc:8080
过滤器打印: 此时的访问url:http://toov5.test1.cc:9090/jspIndex 与配置中的 domain.name=toov5.test1.cc:8080 不符合
B项目实际上就是 过滤器 + 静态资源 有A服务器想要的资源
实际开发中设置白名单 就是只能某某某服务器的才能够哈哈
玩的就是请求头哈哈 关键词 Referer
(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一,也被称为“One Click Attack”或者Session Riding,一般缩写为CSRF或者XSRF,是一种对网站的恶意利用也就是人们所知道的钓鱼网站。尽管听起来像跨站脚本(XSS),但它与XSS很是不一样,而且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则经过假装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击每每不大流行(所以对其进行防范的资源也至关稀少)和难以防范,因此被认为比XSS更具危险性。
会话信息,可能使用Token方式进行保存。
API接口幂等设计 : 保证数据惟一性 不容许有重复的 (防止表单重复提交 )
互联网API幂等接口设计解决方案:
网络延迟 重复提交屡次 ~~ 防止
或者恶意的攻击 重复提交~~ 防止
因此我要防止别人模拟恶意请求啊
多版本并发控制,该策略主要使用 update with condition(更新带条件来防止)来保证屡次外部请求调用对系统的影响是一致的。在系统设计的过程当中,合理的使用乐观锁,经过 version 或者 updateTime(timestamp)等其余条件,来作乐观锁的判断条件,这样保证更 新操做即便在并发的状况下,也不会有太大的问题。例如
select * from tablename where condition=#condition# // 取出要跟新的对象,带有版本 versoin
update tableName set name=#name#,version=version+1 where version=#version#
在更新的过程当中利用 version 来防止,其余操做对对象的并发更新,致使更新丢失。为了不失败,一般须要必定的重试机制。
乐观锁 无锁机制,经过版本字段判断,若是多线程下。只能有一个操做成功
but若是全部程序都这么玩儿 会影响效率的!
在插入数据的时候,插入去重表(额外添加一张表),利用数据库的惟一索引特性,保证惟一的逻辑。
select for update,整个执行过程当中锁定该订单对应的记录。注意:这种在 DB 读大于写的状况下尽可能少用。
业务要求:页面的数据只能被点击提交一次
发生缘由:因为重复点击或者网络重发,或者 nginx 重发等状况会致使数据被重复提交
解决办法:
集群环境:采用 token 加 redis(redis 单线程的,处理须要排队)
单 JVM 环境:采用 token 加 redis 或 token 加 JVM 内存
处理流程:
数据提交前要向服务的申请 token,token 放到 redis 或 jvm 内存,token 有效时间
提交后后台校验 token,同时删除 token,生成新的 token 返回
token 特色: 要申请,一次有效性,能够限流
客户端每次在调用接口的时候,须要在请求头中,传递令牌参数,每次令牌只能用一次。
一旦使用以后,就会被删除,这样能够有效防止重复提交。
步骤:
1.生成令牌接口
2. 接口中获取令牌验证
令牌方式防止Token重复提交:
补充下:
1.什么Token(令牌) 表示是一个零时不容许有重复相同的值(临时且惟一)
2.使用令牌方式防止token重复提交。
使用场景:
在调用第API接口的时候,须要传递令牌,该Api接口 获取到令牌以后,执行当前业务逻辑,让后把当前的令牌删除掉。
在调用第API接口的时候,须要传递令牌 建议15-2小时
代码步骤:
1.获取令牌
2.判断令牌是否在缓存中有对应的数据
3.若是缓存没有该令牌的话,直接报错(请勿重复提交)
4.若是缓存有该令牌的话,直接执行该业务逻辑
5.执行完业务逻辑以后,直接删除该令牌。
调用接口以前先生成令牌,调用接口时候把令牌存放在请求头中传递过去。而后进行处理
首先获取token:
不带token请求:
再次携带token:(此时服务器端生成的token被删除了) 再次携带这个token 会执行相应的提示逻辑
执行一次成功以后 会删除token 下次携带tonken会与服务器对应不上而错误
tokenUtils:
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang.StringUtils; public class TokenUtils { private static Map<String, Object> tokenMaps = new ConcurrentHashMap<String, Object>(); // 获取令牌 public static synchronized String getToken() { // 如何在分布式场景下使用分布式全局ID实现 String token = "token" + System.currentTimeMillis(); // hashMap好处能够附带 关联值 这里没有附带其余的 tokenMaps.put(token, token); //好比附带值 //tokenMaps.put(token, "userId") return token; } public static boolean findToken(String tokenKey) { // 判断该令牌是否在tokenMap 是否存在 String token = (String) tokenMaps.get(tokenKey); if (StringUtils.isEmpty(token)) { return false; } // token 获取成功后 删除掉 tokenMaps.remove(token); return true; } }
实体类:
public class OrderEntity { private int id; private String orderName; private String orderDes; /** * @return the id */ public int getId() { return id; } /** * @param id * the id to set */ public void setId(int id) { this.id = id; } /** * @return the orderName */ public String getOrderName() { return orderName; } /** * @param orderName * the orderName to set */ public void setOrderName(String orderName) { this.orderName = orderName; } /** * @return the orderDes */ public String getOrderDes() { return orderDes; } /** * @param orderDes * the orderDes to set */ public void setOrderDes(String orderDes) { this.orderDes = orderDes; } }
Controller:
import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.itmayeidu.utils.TokenUtils; import com.itmayiedu.entity.OrderEntity; import com.itmayiedu.mapper.OrderMapper; @RestController public class OrderController { @Autowired private OrderMapper orderMapper; @RequestMapping("/getToken") public String getToken() { return TokenUtils.getToken(); } // 验证Token @RequestMapping(value = "/addOrder", produces = "application/json; charset=utf-8") public String addOrder(@RequestBody OrderEntity orderEntity, HttpServletRequest request) { // 代码步骤: // 1.获取令牌 存放在请求头中 String token = request.getHeader("token"); if (StringUtils.isEmpty(token)) { return "参数错误!"; } // 2.判断令牌是否在缓存中有对应的令牌 // 3.如何缓存没有该令牌的话,直接报错(请勿重复提交) // 4.如何缓存有该令牌的话,直接执行该业务逻辑 // 5.执行完业务逻辑以后,直接删除该令牌。 if (!TokenUtils.findToken(token)) { //若是返回false 就提示 请勿操做 return "请勿重复提交!"; } orderEntity.setOrderName("黄焖鸡米饭"); orderEntity.setOrderDes("美味"); int result = orderMapper.addOrder(orderEntity); return result > 0 ? "添加成功" : "添加失败" + ""; } }
下次来查询时候 因为执行成功以后会删除token 因此查询的结果是false 提示"请勿重复提交"
若是用户提早屡次生成好token 再恶意重复提交的状况,如何进行处理?
好比HttpClient去获取到token而后拿来使用
使用图形验证码防止机器模拟接口请求攻击,在调用核心业务接口时,好比支付、下单、等接口,最好使用手机短信验证验证或者是人脸识别,防止其余用户使用token伪造请求。
也能够经过Nginx实现限流(1分钟以内接受1000个请求),配置黑名单白名单(若是发现某人恶意请求攻击,屏蔽他的IP)
市面上没有百分百的彻底识别验证码的工具~
在实际项目中,会话信息使用令牌方式保存。若是黑客利用抓包技术分析到令牌。黑客技术使用令牌伪造支付下单等核心业务。
绑定ip (4g网络的ip是不固定的)
在互联网上没有绝对防止伪造请求。 可是能够在调用接口时候,确认是本人的操做。使用发送短信验证码或者图像识别的方式。
在核心接口上,必定要确认是本人操做,好比密码修改,支付下单等等操做
黑客使用抓包工具分析Http请求,在忘记密码找回时,须要发送一套短信验证码,若是验证码数字比较短的话,很容易使用暴力破解方式攻击破。
防护手段:
忘记密码验证码最好在6-8位。
一旦频繁调用接口验证时,应该使用图形验证码拦截,防止机器模拟。
使用黑名单和白名单机制,防护攻击。
关于:
一、忘记密码漏洞暴力破解找回密码
二、使用上传文件漏洞格式化服务器硬件 注入个rm -rf * 就完蛋了
三、常见其余攻击和漏洞(ErrorCode, Html注释流动,路径遍历漏洞)
使用短信验证码能够被破解,在忘记密码短信找回中有一个code(短信验证码)。
提交时候 Java程序 HttpClient技术开启多线程,暴力破解生成对应的4位数字之内验证码进行验证,若是一旦验证成功,成功破解。
防止的话就是 找回验证码中加入 字母 字母和数字混合使用 若是找回密码接口重试五次以上仍是错误(出现图形验证码)防止继续模拟
配置防止DDOS,限制IP访问,配置黑名单白名单。
在作值传递时候 慎重隐藏域 <input type="hidden" >
使用上传文件漏洞格式化服务器硬盘: 对于上传的文件 要判断文件流 而不是名字后缀啥的
上传文件时候 没有限制格式 致使任意上传文件 若是黑客上传木马文件的(可执行程序)状况。可能会致使服务崩溃
案例: 上传木马文件 删除某个文件 (jsp exe bat)
解决方案:
方式一: 在上传文件时候 必定要使用判断文件流的方式 肯定是图片 不要判断后缀方式获取图片
方式二: 静态资源与动态资源分开服务器 Nginx+Tomcat实现 动静分离 Nginx存放静态资源 没有tomcat环境
方式三: 服务器硬盘上不能作删除操做
方式四: 权限设置 对于目录的操做权限没有
方式五: 前端作后缀限制
方式六:服务器上不要有热部署功能。若是我上传class文件。 Java程序就能获取到了。限制 jsp exe 等可执行程序。
上传文件漏洞原理: jsp里面有操做文件的代码 我上传后 而后访问这个jsp 在tomcat环境下 执行这个文件 执行了 就完蛋了
用第三方工具类去判断流
XSS : JS脚本注入 获取用户信息(token等等) 防护方式 使用特殊字符转换的方式 +过滤器拦截处理
CSRF: 跨站模拟请求
保证接口幂等性 : token方式
防止机器识别: token+图形识别 尽力而为 没有百分百破解验证码 图形验证码至少能有效阻止 token+图形识别+限流 有次数限制的
如何防止CSRF 模拟请求 最好使用图形验证码+若是调用核心业务接口 好比支付等安全相关 最好发送短信验证+人脸识别
若是抓包分析到toeken 能够伪造下单
直接异常信息,会给攻击者以提示。 可使用mvc中的工具,把错误码异常等进行封装
HTML注释, 会暴露功能,方便攻击。 上线时去除注释
文件上传, 若是自己功能就是上传文件去执行,那么就有可能执行很是危险的命令。 解决方式是,设置文件白名单,限制文件类型,另外还能够从新命名文件,更名为不可执行的
路径遍历, 使用相对路径来遍历未开放的目录。 方式是将JS,CSS部署在独立的服务器,使用独立域名。 其余文件不使用静态URL访问,动态参数不包含文件路径信息。