防参数篡改,防重放踩坑记录~

最近一段时间参与了公司的年度安全测试,我负责对接测试方,来修复安全测试发现的漏洞那么安全一顿操作下来,也是 发现了两个重大的漏洞,第一个就是重放,第二个就是参数篡改

简单介绍一下重放和参数篡改

重放?

就是别有用心的人呢,对你的请求进行抓包,拿到通信包后,伪装一下,拿着包不断地进行请求,这样就耗费掉很多服务器资源,比如恶意地区刷你的短信服务,或者如果是电商业务地话,恶意攻击者就可以恶意地去下单

我们怎么解决呢?

每个请求带个时间戳(加密后的时间戳),后端拿到时间戳之后去缓存中进行比对,如果没有发现这个时间戳,认为这个请求有效,若发现有这个时间戳,那么就认为请求无效。

这样有什么问题么?

对的,这样的话会导致缓存中存储的时间戳越来越多,

怎么办?

让缓存中存储的时间戳有个过期时间比如60s,请求到来之后先验时间戳和服务器时间,如果时间戳超时了,那么认为这个请求无效,如果时间戳和现在的服务器时间相比在60s 内,则继续去缓存中验证,走刚刚的逻辑。那么下面就是具体的流程图。

参数篡改

什么是参数篡改
就是比刚刚的重放更狠,拿到数据包之后,如果发现数据包没有加密,那么改改参数,1000的鞋子付款时改一下变成0.01,麻烦了,这不完蛋了么?

我们怎么解决的呢?

有种加密算法叫做杂凑算法,比如MD5 或者SM3,他们的特点就是同样的数据杂凑之后的结果相同,不同的数据杂凑之后的结果一定不同,
于是我们把数据(json串)进行杂凑,然后把杂凑后的结果再进行加密,最后把这个结果放到json串中,这样后端拿到请求后,先对json串去掉一个key(杂凑结果的那个key),然后进行杂凑,接着用同样的密钥进行加密,最后比较一下后端计算的结果和json中携带的结果是否一致?
这样如果攻击者修改了请求体(json串)中的数据,因为杂凑算法的特性,后端在对请求体进行杂凑时获取的结果就和前端携带的不一致,有人说攻击者可以在修改json后,对json串进行重新杂凑啊!,不好意思,我的杂凑结果是加过密的,他不知道密钥。
流程图长下面这样子

哇,好麻烦!

第一个坑来了!
我们的前端是个手机端的应用,在app内套个浏览器,所有的请求都用让app(我们称之为壳子)去发,而不是a壳子里的浏览器发。
我们的安卓应用呢,是Java写的,json用的是阿里巴巴的fastJson,原来好好的json串,给到fastJson 后,json中key的顺序就变了!原来可能是

{
    "A-name": "BeJson",
    "B-url": "",
    "C-page": 88
}

但是给到壳子之后,壳子发请求的时候呢,可能就变成了

{
    "B-url": "hello.com",
    "C-page": 88,
    "A-name": "BeJson"
}

那么,根据杂凑算法的特点呢,顺序不同,杂凑的值肯定也是不同的,于是,就是后端校验不通过了,

怎么办?

排序!
前端对接送串的key 按照字母顺序进行排序,然后再杂凑,后端拿到json后也进行排序,然后再杂凑,这样就规避掉了fastJson乱序的问题
上代码,
主要的思想就是利用TreeMap里面的树结构进行自动的排序,排序后再转为json串

package com.cloud.serviceapp.util;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import org.json.JSONTokener;

import java.util.*;

/**
* @author bigbo
* @description json key 排序工具类 按照 a-z排序
* @createTime 2023/10/26
*/
public class JsonKeySorter {
    public static Object sortJsonKeys(String json) {
        try {
            Object obj = new JSONTokener(json).nextValue();
            if (obj instanceof JSONObject) {
                JSONObject jsonObject = (JSONObject) obj;
                Map<String, Object> sortedMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
                Iterator<String> keys = jsonObject.keys();

                while (keys.hasNext()) {
                    String key = keys.next();
                    Object value = jsonObject.get(key);
                    // 如果是个jsonObject,递归调用自己
                    if (value instanceof JSONObject) {
                        value = sortJsonKeys(value.toString());
                        // 如果是个Josn中的列表,那么调用sortJsonArray
                    } else if (value instanceof JSONArray) {
                        value = sortJsonArray((JSONArray) value);
                    }
                    sortedMap.put(key, value);
                }
                com.alibaba.fastjson.JSONObject result = (com.alibaba.fastjson.JSONObject)com.alibaba.fastjson.JSONObject.toJSON(sortedMap);
                return result;
            }
            // 因为我们业务的特殊性,json串是一个对象而不是列表
            // else if (obj instanceof JSONArray) {
            // JSONArray jsonArray = (JSONArray) obj;
            // return sortJsonArray(jsonArray);
            // }
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return null;
    }
// 对json列表进行排序
    private static ArrayList<Object> sortJsonArray(JSONArray array) {
        ArrayList<Object> list = new ArrayList<>();
        for (int i = 0; i < array.length(); i++) {
            Object value = array.get(i);
            if (value instanceof JSONObject) {
                value = sortJsonKeys(value.toString());
            } else if (value instanceof JSONArray) {
                value = sortJsonArray((JSONArray) value);
            }
            list.add(value);
        }
        list.sort(new JsonValueComparator());
        return list;
    }
//重写compare方法实现按照字母大小写排序
    private static class JsonValueComparator implements Comparator<Object> {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof JSONObject && o2 instanceof JSONObject) {
                return sortJsonKeys(o1.toString()).toString().compareTo(sortJsonKeys(o2.toString()).toString());
            } else if (o1 instanceof JSONArray && o2 instanceof JSONArray) {
                return sortJsonArray((JSONArray) o1).toString().compareTo(sortJsonArray((JSONArray) o2).toString());
            } else {
                return o1.toString().compareTo(o2.toString());
            }
        }
    }
}

这样有什么问题呢?

我忘了,后来我们采取的是整个请求体全加密,一步到位,完美解决参数篡改,直接规避掉了json串的key乱序。

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

比巴卜果粒多

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值