发现问题
最近在梳理登录流程的代码,发现了这样一个问题。
在登录成功后,有一步想当前线程ThreadLocal中塞入个人登录信息的操作。紧接着,向下直接返回这次请求,一次请求结束,但是由于当前线程保存了此次登录的用户信息,并且回到tomcat线程池,所以这个信息是不会被释放掉的。如果后续非登录态的操作,有机会复用到该条线程,则能成功获取到线程中的用户信息,从而能以一个登录态的方式访问接口。理论情况看来,这样是有道理的,但是如何能够复用到该条线程呢,后续filter不会对该次请求防卫过滤?就成为了这个问题的核心。
通过代码可以发现,当前我们系统逻辑使用一套过滤器链来检验一次请求的安全性以及合法性。并且有一个专门构建 认证主体信息的filter。每次请求都会根据该次请求的cookie中的qid,去缓存中加载改qid所对应的主体,存入当前线程中。然后这中间有一个细节地方,能够使得上面的漏洞成功的通过这个filter。即在通过缓存加载token信息时,有一步判空操作,如果该qid对应的token不存在时,就不会向当前线程中塞入。那么,接着上面的问题,如果能够成功复用到处理登录的线程,则在进入到这个filter时,我即使此次请求不带任何qid信息。拿到一个为空的token。他就不会去覆盖这个线程,那么我线程中的token依然是存在的。从而在请求到达 访问控制filter,进行最后一步校验是否允许访问接口时,只判断了当前线程中是否token,并且完成整个认证,就能支持访问。
通过上面的分析,就能够避免后续filter过滤掉该请求的问题,那么现在就还有怎么复用该线程这一个问题了,如果能很容易复用到该线程,那么任何人都能以非登录态的状态访问系统,导致大的安全危机发生。 其实,想办法复用该线程也不难,需要一个条件就是请求量比较小的时候(比如晚上夜间)使得 该线程不会被其他请求进来使用到,从而覆盖线程中的值,最后清除掉。所以,等到该线程回到线程池中,再随机模拟一次并发请求,那么这条线程的复用概率也是非常高的。
复现问题
首先通过postman发起一次登录请求,通过调试得到当前处理线程名为 http-nio-9180-exec-6。并且线程中也存在该用户信息。
然后通过jmeter,模拟一次下并发,Jmeter配置如下
此时我们调用系统内需要登录才能访问的接口试试。
发现是同一条线程,并且在随意设置qid的情况下也能够访问到。
从而证实上述问题是存在发生情况的。并且经过多次试验,发现请求量小的情况下,成功复现的概率也是非常高的,且这个时候jmeter只并发10条线程,就能访问。如果线程数设置更高,那么概率也是随之增加。
修改
删除登录成功后向线程中塞主体信息,或者SecurityContextFilter中塞登录信息操作不进行判空
评论区