资讯 小学 初中 高中 语言 会计职称 学历提升 法考 计算机考试 医护考试 建工考试 教育百科
栏目分类:
子分类:
返回
空麓网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
空麓网 > 计算机考试 > 软件开发 > 后端开发 > Java

【学习笔记】原来SpringSession的SessionID是不能直接改的

Java 更新时间: 发布时间: 计算机考试归档 最新发布

【学习笔记】原来SpringSession的SessionID是不能直接改的

今天在研究如何防止Cookie篡改,

防止篡改Cookie,最好的方法当然是利用非对称加密,通过私钥加密这个cookie的值,进行一个数字签名的大动作,然后再将cookie的值是签名的内容+要被签名的内容+公钥组成,再次返回的时候验证,就验证这个签名的内容解出来之后是否与要被签名的内容相同,相同则这个cookie没有被篡改

[Signature 类]

public final boolean verify(byte[] signature) throws SignatureException {        if (state == VERIFY) {            return engineVerify(signature);        }        throw new SignatureException("object not initialized for " +                                     "verification");    }protected boolean engineVerify(byte[] sigBytes)                throws SignatureException {            try {                byte[] out = cipher.doFinal(sigBytes);                byte[] dataBytes = data.toByteArray();                data.reset();                return MessageDigest.isEqual(out, dataBytes);            } catch (BadPaddingException e) {                // e.g. wrong public key used                // return false rather than throwing exception                return false;            } catch (IllegalBlockSizeException e) {                throw new SignatureException("doFinal() failed", e);            }        } public static boolean isEqual(byte[] digesta, byte[] digestb) {        if (digesta == digestb) return true;        if (digesta == null || digestb == null) {            return false;        }        int lenA = digesta.length;        int lenB = digestb.length;        if (lenB == 0) {            return lenA == 0;        }        int result = 0;        result |= lenA - lenB;        // time-constant comparison        for (int i = 0; i < lenA; i++) {            // If i >= lenB, indexB is 0; otherwise, i.            int indexB = ((i - lenB) >>> 31) * i;            result |= digesta[i] ^ digestb[indexB];        }        return result == 0;    }

可以看到下面验签(也就是sigBytes与data进行比较,data就是传入的原内容)的过程其实就是逐位做异或,只要有1位为1,result 就为1,然后就匹配失败

(异或 :1^0 = 1 , 1^1 = 0 , 0^1 = 1 , 0^0 = 0)

Java支持数字签名的算法有三种:

RSA 将两个大素数相乘十分容易,但反过来想要对它们的乘积进行因式分解会比较困难

DSA 只用签名而不能加密或解密,比RSA要快,主要就是两个素数公开,这样,当使用别人的p和q时,即使不知道私钥,你也能确认它们是否是随机产生的,还是作了手脚。

ECDSA 椭圆曲线签名算法:椭圆曲线上的离散对数问题,主要就是知道基点(椭圆曲线上的点)和某个数的乘积 以及 基点 很难推出 这个 某个数 来,难度相较于上面两个都要难很多;除了一个个遍历没有更快的办法,但如果是大数就会需要遍历很久

那么我利用用户信息对这个Cookie加密,那岂不是就安全了?

然后当我乐呵乐呵地尝试去修改sessionId的生成方法时,发现我使用的是Spring-session-redis,而在RedisHttpSessionConfiguration 中有个RedisOperationSessionRepository 的session仓库,

而这个RedisOperationSessionRepository 就是存放各种session 的地方

内部有个方法是CreateSession,这里就是session 生成的地方

public RedisOperationsSessionRepository.RedisSession createSession() {        Duration maxInactiveInterval = Duration.ofSeconds(this.defaultMaxInactiveInterval != null ? (long)this.defaultMaxInactiveInterval : 1800L);        RedisOperationsSessionRepository.RedisSession session = new RedisOperationsSessionRepository.RedisSession(maxInactiveInterval);        session.flushImmediateIfNecessary();        return session;    }

关键是这里有个RedisSession 

而这个RedisSession 就是这个仓库中的session,也就是Spring-session 用来替换原生的session 的session

再往下看会发现,这个RedisSession是final修饰的!

 final class RedisSession implements Session {        private final MapSession cached;

这意味着什么? 是的!final修饰的类不可继承,final修饰的方法不可重写

看到上面还有个MapSession 是干啥的?它不仅是private 修饰的还是final修饰的,private意味着即使redissession是可继承的,你也拿不到它,private修饰的成员变量是让你可以拥有,只是可以拥有,final 修饰意味着它 ! 也!不 ! 可! 以! 改!写!

再往下看:这个就是RedisOperationSessionRepository createSession时提到的方法!

 RedisSession(Duration maxInactiveInterval) {            this((MapSession)(new MapSession()));            this.cached.setMaxInactiveInterval(maxInactiveInterval);            this.delta.put("creationTime", this.getCreationTime().toEpochMilli());            this.delta.put("maxInactiveInterval", (int)this.getMaxInactiveInterval().getSeconds());            this.delta.put("lastAccessedTime", this.getLastAccessedTime().toEpochMilli());            this.isNew = true;        }

内部是不是有个MapSession的实例?

跳到MapSession里看看,这个到底和Redissession 有什么关系?

public final class MapSession implements Session, Serializable {        public MapSession() {        this(generateId());    }        public MapSession(String id) {        this.sessionAttrs = new HashMap();        this.creationTime = Instant.now();        this.lastAccessedTime = this.creationTime;        this.maxInactiveInterval = Duration.ofSeconds(1800L);        this.id = id;        this.originalId = id;    }    private static String generateId() {        return UUID.randomUUID().toString();    }}

可以看到其实就是生成了一个Session,还包括SessionId的生成,也就是说这个RedisSession就是个大session,内部有个小Session,而这个session就是真正的Session,毕竟SessionID也就是在这生成的

最后我的修改SessionID的方法宣告失败~不过或许有其他可以修改SessionID的“曲线救国”的方式 还有待寻找

参考:

奇妙的安全旅行之DSA算法 - 知乎

ECDSA(椭圆曲线数字签名算法) - 简书

转载请注明:文章转载自 http://www.konglu.com/
本文地址:http://www.konglu.com/it/1096403.html
免责声明:

我们致力于保护作者版权,注重分享,被刊用文章【【学习笔记】原来SpringSession的SessionID是不能直接改的】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理,本文部分文字与图片资源来自于网络,转载此文是出于传递更多信息之目的,若有来源标注错误或侵犯了您的合法权益,请立即通知我们,情况属实,我们会第一时间予以删除,并同时向您表示歉意,谢谢!

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2023 成都空麓科技有限公司

ICP备案号:蜀ICP备2023000828号-2