记一次线上短信接口恶意被刷以及在修复过程中踩过的坑

818 阅读3分钟

前言

前段时间做了一个活动,活动需要用户进行短信验证码登录,短信接口是直接调用了基础公用的短信网关接口,短信网关接口会对手机号日发送量进行限制,上线两天后,发现短信获取验证码接口一直在被限流,但实际活动业务量并不是很大,远远没有到几乎每次请求进去就被限流的情况。

初始问题排查解决

定位问题

对接口的日志进行了排查,发现存在很多有规律的手机号在进行访问,而且都是来自同一个IP的请求,基本可以断定是有人在恶意刷接口。

解决问题

发现问题之后,及时对该IP进行黑名单限制,然后对我们的业务接口进行了改造,对收到的请求先获取它的IP,然后以IP做key进行缓存计数,若3分钟内超过10次则直接返回短信发送上限,设置过期时间30分钟,用户在30分钟内将无法再进行登录,改造灰度完之后以为肯定没有啥问题,在第二天排查日志时候发现被打脸了,还是有之前类似规律的手机号一直在调用接口,也还会出现限流的情况,到底哪里出了问题。

获取IP的坑

首先我们看一下获取IP的工具类

public static String getIpAdrress(HttpServletRequest request) {
        String ip = null;
        String unknown = "unknown";
        //X-Forwarded-For:Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");

        if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP:apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP:weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP:有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP:nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }

        //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }

        //还是不能获取到,最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

这个类在获取IP的时候是去取X-Forwarded-For里面IP串中的第一个IP,然后没有X-Forwarded-For再取代理IP等,那么如果用户每次请求都在请求头里伪造一个假的IP那么通过上面的限制就没有办法解决问题。

分析一波伪造IP情况下取到的X-Forwarded-For:伪造IP,真实IP,Nginx代理IP

代理IP的情况要根据各自的架构去判断,如果经过了多层代理会出现多个代理IP的情况,这个后面遇到的一个代理IP的坑也验证了可能会有问题。

获取IP解决方法

既然第一个IP可能是伪造的,那么久换个思路,从最后一个IP开始取,每经过一层代理去掉一个IP,那么取到的就是用户真实的IP。

新增安全扫描代理的坑

上面说了,在确认只是经过了一层代理的情况下,相当于我每次取的是倒数第二个IP,然而我们开发不知情的情况,公司运维在一次安全排查工作中在Nginx上面一层加了一个第三方公司的安全扫描插件,这直接导致我们整个接口取到的IP就是同一个,所有用户请求基本都直接报短信发送上限了。

小结

对于恶意刷非交易类接口的问题也是第一次遇到,以后还是需要留个心眼,有问题或者有比较好的方案的欢迎大家指正!