- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
我们的API接口都是提供给第三方服务/客户端调用,所有请求地址以及请求参数都是暴露给用户的.
我们每次请求一个HTTP请求,用户都可以通过F12,或者抓包工具fd看到请求的URL链接,然后copy出来。这样是非常不安全的,有人可能会恶意的刷我们的接口,那这时该怎么办呢?防重放攻击就出来了.
我们以掘金文章点赞为例。当我点赞之后,H5会发送一个请求给到掘金后端服务器,我可以通过f12看到完整的请求参数,包括url,param等等,然后我可以通过copy把这个请求给copy出来,那么我就可以做到一个放重放攻击了.
具体如下。我们可以看到,服务端返回的是重复点赞,也就是掘金并没有做我们所谓世俗意义上的放重放攻击。掘金通过查询数据库(推测item_id是唯一索引值),来判断是否已经点赞然后返回前端逻辑.
那么什么是我们理解的放重放呢 。
简单来说就是,前端和客户端约定一个算法(比如md5),通过加密时间戳+传入字段。来起到防止重复请求的目的。 然后这个时间戳可以设定为30秒,60秒过期.
那么如果30秒,有人不断刷我们的接口怎么办。 我们还可以新加一个字段为nonceKey,30秒内随机不重复。这个字段存放在Redis,并且30秒过期。 如果下一次请求nonceKey还在redis,我就认为是重复请求,拒绝即可.
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String timestamp = request.getParameter("timestamp");
String token = request.getParameter("token");
if (timestamp == null || token == null) {
return false;
}
TreeMap<String, String> map = new TreeMap<>();
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String str = parameterNames.nextElement();
if (StringUtils.equals(str, "token")) {
continue;
}
map.put(str, request.getParameter(str));
}
return SecretUtils.extractSecret(redisService, timestamp, token, map);
}
}
public class SecretUtils {
private static final long NONCE_DURATION = 60 * 1000L;
private static final String SALT = "salt"; // 注意这块加盐
public static boolean extractSecret(StringRedisTemplate redisService, String timestamp, String token, TreeMap<String, String> map) {
if (StringUtils.isEmpty(timestamp) || StringUtils.isEmpty(token)) {
return false;
}
long ts = NumberUtils.toLong(timestamp, 0);
long now = System.currentTimeMillis();
if ((now - ts) > SecretUtils.NONCE_DURATION || ts > now) {
return false;
}
StringBuilder sb = new StringBuilder();
map.put(SALT, SALT);
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (sb.length() > 0) {
sb.append("&");
}
sb.append(key).append("=").append(value);
}
String targetToken = DigestUtils.md5DigestAsHex(sb.toString().getBytes());
if (!token.equals(targetToken)) {
return false;
}
String s = redisService.opsForValue().get(timestamp);
if (StringUtils.isNotEmpty(s)) {
return false;
} else {
redisService.opsForValue().set(timestamp, timestamp, NONCE_DURATION, TimeUnit.MILLISECONDS);
}
return true;
}
}
前端会通过我们事先约定好的算法以及方式,将字符串从小到大进行排序 + timestamp,然后md5进行加密生成token传给后端。后端根据算法+方式来校验token是否有效.
如果其中有人修改了参数,那么token就会校验失败,直接拒绝即可。如果没修改参数,timestamp如果大于60s,则认为是防重放攻击,直接拒绝,如果小于30s,则将nonceKey加入到redis里面,这里nonceKey用的是timestamp字段,如果不存在则第一次请求,如果存在,则直接拒绝即可.
通过这么简单的一个算法,就可以实现防重放攻击了.
Q:客户端和服务端生成的时间戳不一致怎么办 。
A:客户端和服务端生成的是时间戳,不是具体的时间,时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数 。
Q:HTTPS数据加密是否可以防止重放攻击 。
A:不可以。https是在传输过程中保证了加密,也就是说如果中间人,获取到了请求,他是无法解开传输的内容的.
举个最简单的例子,上课和同学传纸条的时候,为了不让中间给递纸条的人看到或者修改,可以在纸条上写成只有双方能看明白密文,这样递纸条的过程就安全了,传纸条过程中的人就看不懂你的内容了。但是如果给你写纸条的人要搞事情,那就是加密解决不了的了。这时候就需要放重放来解决了.
Q:防重放攻击是否有用,属于脱裤子放屁 。
A:个人感觉有一点点吧。比如防重放攻击的算法+加密方式其实大多数用的都是这些,其实攻击人很容易就能猜到token生成的方式,比如timestamp + 从小到大排序。因此我们加入了salt来混淆视听,这个salt需要前端、客户端安全的存储,不能让用户知道,比如js混淆等等。但其实通过抓包,js分析还是很容易能拿到的。但无形中增加了攻击人的成本,比如网易云登录的js加密类似.
Q:做了防重放,支付,点赞等是否不需要做幂等了 。
A:需要。最重要的幂等,一定要用数据库来实现,比如唯一索引。其他都不可相信.
以我个人的理解。防重放用处不大,其他安全措施,比如非对称的RSA验签更加有效。就算用户拿到了请求的所有信息,你的接口也一定要做幂等的,尤其是像支付转账等高危操作,幂等才是最有用的防线。而且防重发生成token的算法,大家都这样搞,攻击者怎么可能不知道呢?这点我不太理解.
现在面试也比较考验面试官的水平,下篇我会讲下最近的一些面试体验和感受,欢迎大家点赞收藏.
最后此篇关于面试官:你讲下接口防重放如何处理?的文章就讲到这里了,如果你想了解更多关于面试官:你讲下接口防重放如何处理?的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我想使用“SpeechSynthesizer”来朗读文本,但该文本包含一些 XML 标记。使用旧的 COM 对象,我使用了 S5Voice.Speak uString, SVSFDefault Or
我对 MIT OCW Python 第 3 课有疑问。 根据简单的数学计算,她使用的代码应该不会成功。 ## EXAMPLE: approximate cube root ##############
从 CSP193p 2015 第 15 讲开始,prepareForSegue 中的以下代码将弹出窗口的高度最小化为 iPad 上的两行标签和文本字段: let minimumSize =
我目前正在尝试完成 iTunes U 上的 Swift 类(class),我们正在构建一个计算器。我无法理解部分代码。 我在下面添加了我认为与文件相关的代码。 让我感到困惑的是:为什么 operati
无法理解 PHRASES 部分的代码。这是《Python 艰难之路》中的代码。那部分之后我很失落。有人可以解释一下吗? 书中没有对下面的代码进行任何解释。 import random from url
此问题来自 MIT Python 类(class) 6.00.1X,Lectrue 8。我尝试了两段略有不同的代码。两者都是为了计算每个学生的平均成绩。 第一个代码没有考虑到可能存在字母等级,第二个代
在第 6 讲 Stanford iOS9 2016 系列讲座中,程序中添加了一个名为 VCL.swift 的文件来演示 View Controller 生命周期。 我已经添加了文件,但是对于行 "pr
我是一名优秀的程序员,十分优秀!