1 -【 常见 web 安全漏洞 】- 4 CSRF 攻击

1 CSRF 攻击产生的原因

CSRF(Cross Site Request Forgery,跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一,也被称为“One Click Attack”或者Session Riding,通常缩写为 CSRF 或者XSRF,是一种对网站的恶意利用也就是人们所知道的钓鱼网站。尽管听起来像跨站脚本(XSS),但它与 XSS 非常不同,并且攻击方式几乎相左。XSS 利用站点内的信任用户,而 CSRF 则通过伪装来自受信任用户的请求来利用受信任的网站。与 XSS 攻击相比,CSRF 攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比 XSS 更具危险性。

2 API 接口幂等性设计

2.1 API 接口幂等性设计方案

方案一:MVCC 方案

多版本并发控制,该策略主要使用 update with condition(更新带条件来防止)来保证多次外部请求调用对系统的影响是一致的。

在系统设计的过程中,合理的使用乐观锁,通过 version 或者 updateTime(timestamp)等其他条件,来做乐观锁的判断条件,这样保证更新操作即使在并发的情况下,也不会有太大的问题。

例如:

// 取出要跟新的对象,带有版本 versoin
select * from tablename where condition=#condition# 

update tableName set name=#name#,version=version+1 where version=#version#

在更新的过程中利用 version 来防止,其他操作对对象的并发更新,导致更新丢失。为了避免失败,通常需要一定的重试机制。

方案二:去重表

在插入数据的时候,插入去重表,利用数据库的唯一索引特性,保证唯一的逻辑。

方案三:悲观锁

select for update,整个执行过程中锁定该订单对应的记录。

注意:这种在 DB 读大于写的情况下尽量少用。

方案四:Token 机制,防止页面重复提交

业务要求:

页面的数据只能被点击提交一次

发生原因:

由于重复点击或者网络重发,或者 nginx 重发等情况会导致数据被重复提交

解决办法:

  • 集群环境:采用 tokenredis(redis 单线程的,处理需要排队)
  • 单 JVM 环境:采用 tokenredistokenjvm 内存

处理流程:

数据提交前要向服务的申请 token,token 放到 redis 或 jvm 内存,token 有效时间,提交后后台校验 token,同时删除 token,生成新的 token 返回。

token 特点:要申请,一次有效性,可以限流。

2.2 基于 Token 方式防止 API 接口幂等

客户端每次在调用接口的时候,需要在请求头中,传递令牌参数,每次令牌只能用一次。

一旦使用之后,就会被删除,这样可以有效防止重复提交。

步骤:

  1. 生成令牌接口
  2. 接口中获取令牌验证

使用场景:

在调用 API 接口的时候,需要传递令牌,该 Api 接口 获取到令牌之后,执行当前业务逻辑,然后把当前的令牌删除掉。

2.2.1 生成令牌接口

package com.snow.utils;

import java.lang.annotation.Retention;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;

/**
 * Token 工具类
 */
public class TokenUtils {

    // 这里为了方便,使用 Map 集合代替缓存
    private static Map<String, Object> tokenMaps = new ConcurrentHashMap<String, Object>();

    // 代码步骤:
    // 1.获取令牌
    // 2.判断令牌是否在缓存中有对应的数据
    // 3.如何缓存没有该令牌的话,直接报错(请勿重复提交)
    // 4.如何缓存有该令牌的话,直接执行该业务逻辑
    // 5.执行完业务逻辑之后,直接删除该令牌。

    /**
     * 获取令牌
     *
     * @return
     */
    public static synchronized String getToken() {
        // 如果在分布式场景下需要使用分布式全局ID实现
        String token = "token" + System.currentTimeMillis();
        // hashMap好处可以附带
        tokenMaps.put(token, token);
        return token;
    }

    /**
     * 查找令牌
     *
     * @param tokenKey
     * @return
     */
    public static boolean findToken(String tokenKey) {
        // 判断该令牌是否在tokenMap 是否存在
        String token = (String) tokenMaps.get(tokenKey);
        if (StringUtils.isEmpty(token)) {
            return false;
        }
        // token 获取成功后 删除对应tokenMaps里面的token
        tokenMaps.remove(token);
        return true;
    }
}

2.2.2 接口中获取令牌验证

package com.snow.controller;

import com.snow.entity.OrderEntity;
import com.snow.mapper.OrderMapper;
import com.snow.utils.TokenUtils;
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 javax.servlet.http.HttpServletRequest;

@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)) {
            return "请勿重复提交!";
        }
        int result = orderMapper.addOrder(orderEntity);
        return result > 0 ? "添加成功" : "添加失败" + "";
    }

}

测试:

获取 token:
在这里插入图片描述

在这里插入图片描述

2.3 防御 CSRF 攻击手段

如何防止机器模拟请求?

使用图形验证码防止机器模拟接口请求攻击,在调用核心业务接口时,比如支付、下单、等接口,最好使用手机短信验证验证或者是人脸识别,防止其他用户使用Token伪造请求。

发布了687 篇原创文章 · 获赞 229 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/weixin_42112635/article/details/105098920