虫虫的技术博客 技术 生活

Monday, March 25, 2019

XXE漏洞解决记录

        内网漏洞平台报了一个XXE漏洞,是我们一个服务域名的根路径,刚一看还以为是弄错了,查了一下代码,果然存在这样的跟路径Controller。看了下逻辑,该服务是用于接收微信的事件通知的,微信是以xml格式提交过来的请求,代码中对XML的解析没有做特殊处理,于是乎。。。
        接到工单后,搜了一下xxe漏洞,简单作个了解,原因XML协议中允许引入外部实体声明。这个外部实体的引用就给了不法分子很大的想象空间。外部的实体引用是个url,写成内部文件路径,可以扫描系统任意路径的文件,写成黑各自己服务的路径,可以收集服务器的信息,写成内网服务地址,可以攻击内网服务,再随意更换ip和端口,可以扫描端口等等。小小一个漏洞,可以利用的地方居然这么多,赶紧修复。


先在本地用postman构造注入的xml内容,模拟注入请求,服务B按预期收到了服务A的请求。

SAXReader reader = new SAXReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities", false); 
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 
Document document = reader.read(xml);

加上以上红色的代码后,再次发送注入请求,服务B不再收到服务A的请求。
自测没问题后,开始上线流程,上完线后,自己用微信关注,取关公众号,服务均能正常接收微信上报过来的事件报文,回归完毕。


参考:
https://blog.csdn.net/zhengpeitao/article/details/82142869
https://www.cnblogs.com/r00tuser/p/7255939.html
https://www.jianshu.com/p/77f2181587a4

Wednesday, March 20, 2019

关于redis分布式锁的几个疑问点

我们在分布式场景下,需要用到分布式锁,分布式锁的实现方案可以有redis实现,zk实现等,这里只讨论redis的方案
先稍微铺垫一下两个redis的不成熟方案,

一,
Get,判断是否存在,如存在直接返回失败,不存在继续
Set,设置锁,key对应的value为1,并设置过期时间
Del,业务执行完毕释放锁
code如下:
    private static long expireTime = 20000;

    public static boolean getLock(String key) {
        String lock = RedisUtil.get(key);
        if (!"1".equals(lock)) {
            RedisUtil.set(key, "1");
            RedisUtil.expire(key, expireTime);
            return true;
        } else {
            return false;
        }
    }

    public static void releaseLock(String key) {
        String check = RedisUtil.get(key);
        if (check != null) {
            RedisUtil.del(key);
        }
    }
此方案明显存在的问题就是,多个线程同时请求get,不存在,同时set,所以会有多个线程进入的可能

二,
Setnx,key对应的value为1,判断返回结果,0为设置失败,已经存在key,直接返回失败,1设置成功,继续
Expire,设置过期时间
Del,业务执行完毕释放锁
code如下:
    private static long expireTime = 20000;

    public static boolean getLock(String key) {
        long lock = RedisUtil.setNX(key, "1");
        if (lock == 1) {
            RedisUtil.expire(key, expireTime);
            return true;
        } else {
            return false;
        }
    }

    public static void releaseLock(String key) {
        String check = RedisUtil.get(key);
        if (check != null) {
            RedisUtil.del(key);
        }
    }
此方法,如果在setnx后expire前宕机,那key就不会被删除也没有过期,别的线程永远也进不来了

然后说下目前同事用到的,以及网上找到的比较被认可的解决方案
Setnx,key对应的value为过期时间戳,判断设置结果,1设置成功,返回成功,0设置失败,key存在内容,继续
Get,取出key的值,判断过期,如没过期,返回失败,如已过期,或者key已经不存在了,继续
Getset,设置锁,同时拿到返回值为设置之前的值,判断是否与刚才取出的值相等,如相等,证明是自己设置的,返回成功,如不相等,证明不是自己设置的,返回失败
Del,业务执行完毕释放锁
code如下:
    private static long expireTime = 20000;

    public static boolean getLock(String key) {
        long lock = RedisUtil.setNX(key, "" + System.currentTimeMillis() + expireTime);
        if (lock == 1) {
            return true;
        } else {
            String check = RedisUtil.get(key);
            if (check != null && System.currentTimeMillis() < Long.valueOf(check)) {
                //key还没有过期
                return false;
            } else {
                //key过期或者已经不存在了
                String old = RedisUtil.getSET(key, "" + System.currentTimeMillis() + expireTime);
                if (old == null) {
                    //说明此时已经key已经不存在,获取锁成功
                    return true;
                } else {
                    // TODO 疑问点1,这里同事用的是否过期的判断,会不会存在风险?
                    if (check.equals(old)) {
                        //获取锁成功
                        return true;
                    } else {
                        //在getSET之  前已经被别的线程获取了锁,获取锁失败
                        return false;
                    }
                }
            }
        }
    }

    public static void releaseLock(String key) {
        String check = RedisUtil.get(key);
        if (check != null) {
            if (System.currentTimeMillis() < Long.valueOf(check)) {
                // TODO 疑问点2,有没有可能删除别的线程设置的key
                RedisUtil.del(key);
            } else {
                // TODO 疑问点3,过期了是否需要删除
            }
        } else {
        }
    }

    // TODO 疑问点4,expire正常来说是需要设置成一个业务一定在此时间内能执行完成的时间,那么,业务如果万一没有执行完成的时候,redis锁还安全么?
    // TODO 疑问点5,redis锁真的安全吗,多个线程错综复杂的执行顺序,如何能比较好的理解呢?
    // TODO 疑问点6,这里还有个疑问,是不是理论上每两行代码之间都可能会中断,比如宕机,比如卡死过了很长时间之后继续执行?这个疑问明显的体现出了基础的薄弱

由于没有找到合适的人讨论,所以这里暂时把自己的疑问记录下来,待以后有机会再理解透彻 


Thursday, March 14, 2019

ThreadLocal理解

        关于ThreadLocal,网上已经有很多文章讲的也是很清楚了,至于应用场景,大多也就是使用demo的方式进行了讲解,达不到让人印象深刻的目的。虽然以前也多次遇到过别人讨论这个东西,大多是面试场景吧,只是直至目前,我也几乎没有在工作中用到过(做开发这么久了,没用过这个,实属汗颜啊),根据理解,用我自己的话把ThreadLocal来描述一遍,以此记录。

        ThreadLocal,是线程本地变量,ThreadLocal为多线程编程提供了一个另一个思路,不用锁,不用共享变量等特点。它的实现思路是在每个线程中存放一个变量的map,这个map用于存放属于线程的变量,这个map的使用就是由ThreadLocal来管理。ThreadLocal有三个方法,get,set,remove,分别是获取变量,设置变量,移除变量。还有一个初始化方法,是给继承类用的。

        对于它的应用场景,按上面的思路来说,就是某个变量是希望在多线程下使用,但又是可以允许不同线程之间互不影响的情况(按线程多例,每个线程一个实例)。比如在spring的web工程体系中,从接收到用户的一个请求到service到dao层都是同一个线程在为其服务,每个线程持有一个数据库连接的实例,这样对于每一个实例来说,都不存在线程安全的问题了

        若以后有机会实际用到的时候,再来补充场景。

Tuesday, March 12, 2019

关于cookie是否能跨域的解读

        这篇文章目的是对CORS跨域携带cookie的理解作一个解释,之前一直对这里有一个误解,以为跨域是可以携带不同域名下的cookie,事实上,cookie的传递是要遵循同源策略的
        问题引出
        同事遇到了一个问题,希望在跨域ajax请求的时候,带上原域的cookie,希望被访问的目标域服务端能访问到原域的cookie,问到了我,我还比较自信的表示跨域这点小事肯定能搞定,因为之前也多次处理过跨域的问题,经过多次尝试,均以失败告终,很是汗颜,最后不断的找资料以及问大牛,得知,这种方式无法实现。

        究其根本&原理说明
        本次的两个域名涉及到跨域。
说到跨域就要说到同源策略,而同源策略,同源策略限制cookie的访问以及ajax请求。
本次的跨域AJAX请求是用的CORS实现方式,CORS是w3c的标准。
CORS方式,默认不允许携带cookie,如果允许携带,是需要客户端和服务端同时设置。CORS方式,携带的cookie,也是要遵循同源策略的,cookie只能携带被访问域名以及其父域下的,其它域的cookie是不被允许访问的
        白话总结
        两个域名下的cookie,是不能相互访问和传递的

对于有相同根域名(二级及以上),可以把cookie设置到根域上才能实现跨子域名的cookie访问(实际上,cookie并没有跨域)

相关问题,跨域的几种方式,每种方式的具体实现,适用场景,针对不同的语言的实现细节,这里就暂不讨论了

CORS,同源策略等名词,直接百科即可

对于在尝试解决问题的过程中,做的一些测试代码,附录如下


Node,原生XHR,document写cookie
zcmtestop.58corp.com进行xhr请求zcmtestop1.58corp.com

var xmlHttp=new XMLHttpRequest();
xmlHttp.open("GET","http://zcmtestop1.58corp.com/",false);
xmlHttp.withCredentials = true;
xmlHttp.send();
result = xmlHttp.responseText;
alert(result);

写cookie
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";

服务端设置header
response().setHeader("Access-Control-Allow-Credentials", "true");
response().setHeader("Access-Control-Allow-Origin", "http://zcmop.58corp.com");

Xhr请求,后端无法拿到zcmtestop.58corp.com域下的cookie

Java,jquery,jquery写cookie
http://zcmop.58corp.com/
$.ajax({type: 'GET',url:'http://zcmop1.58corp.com/scftest',data: {},success:function(data){alert(data);},dataType:"json",xhrFields: {withCredentials: true},crossDomain:true});
$.cookie('the_cookie','the_value',{expires:7,path:'/',domain:'zcmop.58corp.com'});

服务端设置header
    response.writeHead(200,{
        'Content-Type': 'text/plain',
        'Access-Control-Allow-Origin': 'http://zcmtestop.58corp.com',
        'Access-Control-Allow-Credentials': true
        });

Xhr请求,后端无法拿到zcmop.58corp.com域下的cookie
以上两次尝试均失败

同源理解参考

Search This Blog

Categories

Popular Posts

Translate

Copyright © 虫虫的成长历程 | Powered by Blogger Design by PWT | Blogger Theme by NewBloggerThemes.com