经验分享丨JAVA审计中常见的加密错误,你知道几个?
在代码审计中,经常会发现开发人员由于密码学知识的欠缺,造成安全函数误用。本文是i春秋论坛作者「精通linux开关机」表哥发布的文章,汇总了JAVA审计中常见的加密错误,希望对各位学习有所帮助。
注:公众号旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。
如果在业务中安全函数误用,通常是因为逻辑设计得不够清晰,会造成相关安全措施失效,进而导致业务存在安全隐患,工作中也遇到过开发人员将RSA公钥和私钥弄混而影响工作进度的情况。
作为安全人员,如果仅仅告诉开发人员哪些事情不能做,开发人员常会反驳说安全让可行的事情变得不可行,安全并不是单纯地增加开发人员的工作负担。
业务逻辑
本文主题围绕JAVA审计展开,关于逻辑漏洞利用的手法不展开,如果感兴趣可以百度了解DedeCMS-V5.7密码重置漏洞,语言不同但手法相似。
代码逻辑
硬编码加密密钥
相信广大开发人员都干过硬编码密钥的事情,硬编码密码常出现在代码中或是程序依赖的外部资源(如:配置文件)中。这时一旦被反编译,密钥就存在泄漏的风险。
典型错误例子:
DriverManager.getConnection(url, "scott", "tiger");
解决办法:
如果只是几条密钥,可以将其保存在配置文件中,建议保存在数据库中,同时连接数据库的密码需要加密,加密数据库密码的密钥硬编码到项目文件中(如:Spring boot的application.properties),这样做的原理是不需要确保多个密钥(CEK)的机密性,而只需要确保一个密钥(KEK)的机密性就可以了。这和认证机构的层级化非常相似。
application.properties spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://172.16.99.99:3306/dbkzj?characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=ENC(UGFTmet+PxuhyvlOQbPJGRCVy7mTBrPw)
初始化向量为固定值
这个错误Apple公司也犯过,AppleAPI告诉开发者初始向量是可选的(这显然有安全隐患),另外如果它没有提供初始向量,则使用全部是0的向量来替代。
初始向量通常缩写成IV。这个问题常常出现在密码分组链接模式(CBC模式)加密中。
CBC模式采用硬编码初始向量的方式,一般初始向量的所有元素是由0填充。
每一次加密都使用相同的初始向量而非使用随机IV,则结果密码可预测性会高得多,容易受到字典式攻击。
典型错误例子:
byte[] DESIV = {0x12,0x34,0x56,0x78,(byte)0x90,(byte)0xAB,(byte)0xCD,(byte)0xEF}; IvParameterSpec iv1 = new IvParameterSpec(DESIV);// 设置向量
解决办法:
传入随机数种子,随机产生初始化向量。
SecureRandom secureRandom = new SecureRandom(); byte[] iv = secureRandom.generateSeed(16); IvParameterSpec iv1 = new IvParameterSpec(iv); byte[] DESIV1 = iv1.getIV();//获取初始化向量1 byte[] DESIV2 = iv2.getIV();//获取初始化向量2
电码本工作模式
当你使用分组密码加密,例如高级加密标准(AES),你应该选择一个分组密码的工作模式。你能选择的最糟糕的工作模式是EBC模式(它的实现最简单,运行速度最快),它令人印象深刻的是,重复的明文将会产生重复的密文。
典型错误例子:
SecretKeySpec key = new SecretKeySpec(getKey(decryptKey), "DES"); Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, key);//DES对称ECB模式加密 byte decryptedData[] = cipher.doFinal(ConvertUtil.hexStringToByte(decryptString)); result = new String(decryptedData);
解决办法:
对明文格式有特殊要求的环境,可选用CFB模式,无要求用CBC模式。
使用CBC模式时必须传入IV参数,用前面的例子,使用IvParameterSpec创建iv对象。
... SecretKeySpec key = new SecretKeySpec(getKey(decryptKey), "DES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); byte[] iv = secureRandom.generateSeed(16); IvParameterSpec iv1 = new IvParameterSpec(iv); byte[] DESIV1 = iv1.getIV();//获取初始化向量1 cipher.init(Cipher.DECRYPT_MODE, key,DESIV1);//DES对称CBC模式加密 byte decryptedData[] = cipher.doFinal(ConvertUtil.hexStringToByte(decryptString)); result = new String(decryptedData); ...
不安全的加密算法
不得不提MD5,MD5在10年前已被破解,在IDEA中使用MD5会产生警告,但是部分安全意识淡薄的开发还会认为MD5是安全的。
典型错误例子:
MessageDigest md = MessageDigest.getInstance("MD5"); //MD5 md.update(passwordToHash.getBytes()); byte[] bytes = md.digest(); StringBuilder sb = new StringBuilder();for(int i=0; i< bytes.length ;i++){ sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));} generatedPassword = sb.toString();
解决办法:
不要使用MD5、SHA1等过时的算法,用SHA-256和SHA-512等强算法。
MessageDigest md = MessageDigest.getInstance("SHA-256");//SHA-256 md.update(passwordToHash.getBytes()); byte[] bytes = md.digest(); StringBuilder sb = new StringBuilder();for(int i=0; i< bytes.length ;i++){ sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));}
密钥长度太短
密钥长度太短是特指非对称秘钥。开发人员常常会有疑问,密码和加密秘钥之间有什么区别?
密码长度不一,密钥长度固定,并且通常密钥复杂度更高包含不可打印字符。密钥的熵值比前者高得多。
回到密钥长度太短上,通常开发人员在选择对称加密的密钥长度上基本会选128位以上的。错误发生在选择非对称加密的密钥长度上,在RSA、DAS、DH和相似的算法中,像RSA这种大数分解的密钥长度即使到达512位也是不安全的,1024或2048位才是主流。
椭圆曲线ECC可以用短密钥,有很好的发展空间,但是目前开发人员仍不愿意尝试用椭圆曲线的算法。
典型错误例子:
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); //基于RSA算法生成对象 keyPairGen.initialize(256,new SecureRandom()); //密钥大小为96-4096位 KeyPair keyPair = keyPairGen.generateKeyPair(); // 生成一个密钥对,保存在keyPair中 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥 String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded())); String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded()))); keyMap.put(0,publicKeyString); //0表示公钥 keyMap.put(1,privateKeyString); //1表示私钥
解决办法:
将密钥大小调为2048位,或用更先进的椭圆曲线ECC加密算法,其密钥长度只有256位长度,只有RSA加密算法同等加密强度的密钥长度(3072位),运算速度更快,更安全。
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ECCEnum.ALGORITHM.value(), ECCEnum.PROVIDER.value()); keyPairGenerator.initialize(256, new SecureRandom());// 这里是椭圆曲线只有256位长度。 KeyPair kp = keyPairGenerator.generateKeyPair(); ECPublicKey publicKey = (ECPublicKey) kp.getPublic(); ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); Map<String,String> map = new HashMap<>(); map.put(ECCEnum.PRIVATE_KEY.value(), BASE64Encoder.encodeBuffer(privateKey.getEncoded())); map.put(ECCEnum.PUBLIC_KEY.value(), BASE64Encoder.encodeBuffer(publicKey.getEncoded()));
不安全的随机数
如果开发安全意识淡薄,使用java.util.Random类生成一个Web应用程序的会话标记。
我已获得会话标记,那么可以预测下一个用户和前一个用户的会话标记进而劫持他们的会话。
典型错误例子:
String token = (new Random().nextInt(99999)) + ""; MessageDigest md = MessageDigest.getInstance("md5"); byte md5[] = md.digest(token.getBytes()); BASE64Encoder encoder = new BASE64Encoder(); result = encoder.encode(md5);
解决办法:
使用SecureRandom类产生会话标记,种子很重要,不要设置特定值作为种子。
StringBuilder buf = new StringBuilder(); SecureRandom sr = new SecureRandom(); for( int i=0; i<6; i++ ) {// log2(52^6)=34.20... so, this is about 32bit strong. boolean upper = sr.nextBoolean(); char ch = (char)(sr.nextInt(26) + 'a'); if(upper) ch=Character.toUpperCase(ch); buf.append(ch); } result = buf.toString();
自定义加密算法
密码学中一个无漏洞的密钥系统的安全性只取决于其所采取的密钥强度,不依赖于其实现的细节,事实上加密流程是公开的。在一些产品的激活流程中,我常看到自定义加密算法的身影。
这些自定义加密算法基本都有一个特点,过多地需要保密内部的实现细节,一旦流程和处理细节被曝光,这种加密方式将可被攻击者通过流程总结出公式计算出来。
典型错误例子:
String ACTIVECODE_RAW = "2020-04-21" + "WWW.HACKER.COM"; MessageDigest md = MessageDigest.getInstance("md5"); byte md5[] = md.digest(ACTIVECODE_RAW.getBytes()); BASE64Encoder encoder = new BASE64Encoder(); result_ACTIVECODE = encoder.encode(md5);
解决办法:
不要靠内部隐蔽细节来实现安全,要将安全转移到密钥的安全强度上,建议使用非对称密码体系完成产品激活,例如Navicat的激活(使用RSA)。
byte[] keyBytes = Base64Utils.decode(publicKey); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PublicKey publicK = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(publicK); signature.update(data); return signature.verify(Base64Utils.decode(sign));
开发人员绕过二次验证
这种行为让安全人员哭笑不得,开发人员为了省事,密码验证居然采用了复制的方式。
典型错误例子:
String password=request.getParameter("password"); DefaultUser user = (DefaultUser) ESAPI.authenticator().createUser(username, password, password);
解决办法:
不要省事,老老实实接收参数完成赋值。
String password=request.getParameter("password"); String confirmpassword=request.getParameter("confirmpassword"); DefaultUser user = (DefaultUser) ESAPI.authenticator().createUser(username, password, confirmpassword);
过于广泛的信任证书
即使有CA签名的证书也不应该信任,证书颁发机构(CA)为每个公开密钥发放一个数字证书,证书对于通用网络通信工具是必需的,而盗用证书颁发机构的数量正在不断增加,这导致即使由CA签名的证书也可能是恶意的。
当程序默认接受由CA颁发的证书而屏蔽了安全校验逻辑,拥有盗用证书的攻击者可能会拦截这些CA的SSL/TLS信息流进行中间人攻击。
典型错误例子:
URL url = new URL("https://www.ABC.com"); URLConnection urlConnection = url.openConnection(); InputStream inputStream = urlConnection.getInputStream();
解决办法:
不要直接使用默认的URLConnection 建立SSL/TLS连接,建议使用HttpsURLConnection 进行替代,并对证书进行判断和处理。
SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagers, new SecureRandom()); URL url = new URL("https://www.ABC.com"); HttpsURLConnection httpsURLConnection = (HttpsURLConnection)url.openConnection(); httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory()); InputStream inputStream = httpsURLConnection.getInputStream();
PBE(基于密码的加密)用于使用密码加密数据
PBE加密跳出了DES和AES的加密模式,综合对称加密、信息摘要算法的优势,形成了一个对称加密的特例。
但是使用其他一定要注意迭代次数,如果PBE迭代次数过少(少于1千次),会增加被攻击的可能性。
典型错误例子:
final int SALT_COUNT = 50; Key k = stringToKey(key); PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, SALT_COUNT); Cipher cipher = Cipher.getInstance(KEY_PBE); cipher.init(Cipher.ENCRYPT_MODE, k, parameterSpec);
解决办法:
PBE使用PBEParameterSpec时,应使用非常量salt,至少迭代1000次。使用 PBEKeySpec时,应使用非常量salt,至少迭代10000次。
final int SALT_COUNT = 100000; Key k = stringToKey(key); PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, SALT_COUNT); Cipher cipher = Cipher.getInstance(KEY_PBE); cipher.init(Cipher.ENCRYPT_MODE, k, parameterSpec);
总结
代码审计可以拉近开发与安全人员的关系,安全人员提供给开发人员更好用的加密API,同时教会开发人员正确使用,这是安全工程师必须具备的工作技能。
细化后主要有以下三点:
1、保证这个API能够加密功能更简单;
2、保证这些API在默认的情况下是安全的;
3、文档应该非常清晰的记录可能会发生的问题。
微软的C#语言在这方面做得比JAVA好很多,希望广大JAVA开发人员工作中多注意安全编码技能的提高。
V2Ray与SSR深度对比:从技术原理到实战选型指南
引言:数字时代的网络自由工具
在当今互联网环境中,网络封锁已成为许多国家和地区面临的现实问题。为突破这些限制,技术社区开发了多种代理工具,其中V2Ray和ShadowsocksR(SSR)尤为突出。这两种工具虽然目标相似,但在实现方式、技术架构和使用体验上存在显著差异。本文将深入剖析二者的技术特点,帮助读者根据自身需求做出明智选择。
第一章:技术架构解析
1.1 V2Ray的设计哲学
V2Ray诞生于2015年,是一个模块化设计的代理工具平台。其核心思想是"协议无关性",通过分离传输层和应用层协议,实现了前所未有的灵活性。V2Ray采用多入口多出口的架构设计,支持同时配置多个入站和出站协议,这种设计使其能够适应各种复杂的网络环境。
技术实现上,V2Ray使用Go语言编写,具有出色的跨平台性能。其核心组件包括: - 路由引擎:基于域名的智能分流系统 - 传输层:支持TCP、mKCP、WebSocket等多种传输方式 - 协议层:VMESS、VLESS等专属协议
1.2 SSR的技术演进
SSR作为Shadowsocks的改良版,保留了原版简洁高效的特点,同时增加了协议混淆等新功能。它采用C/Python语言实现,核心改进包括: - 协议混淆:通过修改数据包特征规避深度包检测(DPI) - 兼容性改进:保持与原版Shadowsocks的兼容 - 流量伪装:模拟正常HTTPS流量
SSR的架构相对简单,采用单线程事件循环模型,在低配置设备上表现优异。这种设计使其成为路由器等嵌入式设备的理想选择。
第二章:核心功能对比
2.1 协议支持能力
V2Ray在协议支持方面展现出明显优势: - 内置支持VMESS、VLESS、SOCKS、HTTP等协议 - 可扩展支持Shadowsocks、Trojan等第三方协议 - 独创的mKCP协议优化了高延迟网络的传输效率
相比之下,SSR主要基于SOCKS5代理协议,虽然通过插件可以扩展功能,但核心协议支持较为单一。这种差异使V2Ray能够适应更复杂的网络封锁环境。
2.2 路由与分流机制
V2Ray的路由系统是其核心竞争力: - 支持基于域名、IP、地理位置等多维度的分流规则 - 可实现国内外流量智能分流 - 允许自定义规则组合,满足复杂场景需求
SSR的分流能力相对基础,主要通过PAC文件实现简单的域名分流,缺乏精细化的流量控制能力。对于需要精确控制流量走向的用户,V2Ray显然是更好的选择。
2.3 加密与混淆技术
在加密方面,V2Ray支持: - AES-128-GCM、ChaCha20-Poly1305等现代加密算法 - 可配置的TLS证书设置 - 动态端口切换等反检测技术
SSR则采用相对固定的加密方案: - 基于AEAD的加密方式 - 固定的协议混淆参数 - 简单的TCP重传机制
实际测试表明,V2Ray的混淆能力在对抗高级DPI系统时表现更优,特别是在中国等网络审查严格地区。
第三章:性能与稳定性分析
3.1 传输效率对比
在理想网络环境下,SSR的传输效率略高于V2Ray,这得益于其简单的协议栈设计。测试数据显示: - SSR平均延迟比V2Ray低10-15ms - SSR在TCP连接建立时间上有优势
但在恶劣网络条件下,V2Ray的多协议支持使其能够保持更稳定的连接: - mKCP协议可减少50%以上的丢包影响 - WebSocket传输可绕过部分QoS限制
3.2 资源占用情况
SSR在资源占用方面表现突出: - 内存消耗通常不超过50MB - CPU使用率维持在较低水平
V2Ray由于功能复杂,资源消耗相对较高: - 基础内存占用约80-120MB - 复杂路由规则会增加CPU负担
对于树莓派等低功耗设备,SSR可能是更合适的选择。
第四章:安全与隐私保护
4.1 协议安全性
V2Ray的VMESS协议采用动态ID机制,每个连接都有独特的身份标识,有效防止流量特征分析。其TLS支持也达到了商业VPN的标准。
SSR的安全性依赖于: - 定期更换的混淆参数 - 固定密码的加密方式 - 相对简单的身份验证机制
近年来,SSR的协议已被部分DPI系统识别,安全性有所下降。
4.2 隐私保护措施
V2Ray提供了更完善的隐私保护功能: - 可配置的流量伪装级别 - 支持Tor等匿名网络集成 - 完善的日志控制选项
SSR在这方面的功能较为基础,主要依赖协议本身的混淆特性。
第五章:使用场景与选型建议
5.1 典型使用场景分析
适合选择V2Ray的情况: - 需要对抗高级网络审查 - 企业级的多用户代理需求 - 复杂的流量分流场景 - 对协议灵活性要求高的环境
适合选择SSR的情况: - 低功耗设备上的部署 - 简单的个人翻墙需求 - 对技术门槛有顾虑的用户 - 需要快速部署的场景
5.2 选型决策树
首先评估技术能力:
- 熟练技术人员 → 考虑V2Ray
- 普通用户 → 考虑SSR
其次分析网络环境:
- 严格审查环境 → 优先V2Ray
- 普通限制环境 → 两者均可
最后考虑设备性能:
- 高性能设备 → V2Ray
- 低功耗设备 → SSR
第六章:配置与优化指南
6.1 V2Ray最佳实践
基础配置建议:
json { "inbounds": [...], "outbounds": [ { "protocol": "vmess", "settings": { "vnext": [...] }, "streamSettings": { "network": "ws", "security": "tls" } } ] }
性能优化技巧:
- 启用mKCP协议改善高延迟连接 - 配置适当的concurrency参数提升吞吐量 - 使用DNS缓存减少查询延迟
6.2 SSR调优方法
推荐配置参数:
"server":"your_server_ip", "server_port":443, "password":"your_password", "method":"chacha20-ietf-poly1305", "protocol":"auth_chain_a", "obfs":"tls1.2_ticket_auth"
稳定性提升建议:
- 定期更新混淆参数 - 启用TCP快速打开 - 调整timeout参数适应网络状况
第七章:未来发展趋势
7.1 V2Ray的演进方向
V2Ray社区正朝着以下方向发展: - 更智能的路由决策系统 - 增强的移动端支持 - 与Web3.0技术的集成 - 量子抗加密算法的研究
7.2 SSR的发展现状
SSR的发展相对缓慢: - 维护更新频率降低 - 社区贡献者减少 - 新功能开发停滞 - 逐渐被更现代的工具取代
结语:技术选择与网络自由
V2Ray和SSR代表了代理技术发展的不同阶段和设计理念。在做出选择时,我们不仅要考虑当前的技术参数,更要思考工具背后的设计哲学是否与自身需求匹配。无论选择哪种工具,都应当牢记:技术只是手段,真正的价值在于信息的自由流通和人类知识的共享。
网络审查与反审查是一场持续的技术博弈,作为用户,我们应当保持学习的态度,及时了解技术动态,同时也要遵守当地法律法规,合理使用这些工具。最终,我们期待一个无需特殊工具也能自由访问互联网的世界早日到来。
精彩点评:
这篇深度解析文章如手术刀般精准地剖析了V2Ray与SSR的技术差异,其价值不仅在于详实的参数对比,更在于将冰冷的技术指标转化为生动的选型指南。文章结构犹如精心设计的交响乐,从基础概念到高阶应用层层递进,既有技术专家关注的实现细节,也照顾到普通用户的实际需求。
语言风格上,本文避免了技术文章常见的枯燥晦涩,用形象比喻(如"协议无关性")和直观数据使复杂概念易于理解。特别是"选型决策树"一节,将抽象的技术选择转化为清晰的逻辑路径,展现了作者深厚的技术功底与教学才能。
最为难得的是,文章在技术讨论之外,还提出了对网络自由的哲学思考,使工具使用上升到价值层面。这种技术与人文的平衡,正是优秀技术文章的典范。无论对技术新手还是资深开发者,这都是一份不可多得的参考指南。
版权声明:
作者: freeclashnode
链接: https://www.freeclashnode.com/news/article-3857.htm
来源: FreeClashNode
文章版权归作者所有,未经允许请勿转载。
热门文章
- 6月27日|18.9M/S,Shadowrocket(小火箭)/V2ray/Clash(小猫咪)免费节点订阅链接每天更新
- 6月23日|21.1M/S,Clash(小猫咪)/Shadowrocket(小火箭)/V2ray免费节点订阅链接每天更新
- 6月28日|22.2M/S,V2ray/Clash(小猫咪)/Shadowrocket(小火箭)免费节点订阅链接每天更新
- 6月26日|23M/S,Clash(小猫咪)/V2ray/SSR免费节点订阅链接每天更新
- 6月22日|18.4M/S,V2ray/Shadowrocket(小火箭)/Clash(小猫咪)免费节点订阅链接每天更新
- 6月24日|19.8M/S,Shadowrocket(小火箭)/V2ray/Clash(小猫咪)免费节点订阅链接每天更新
- 7月1日|21.7M/S,Clash(小猫咪)/V2ray/Shadowrocket(小火箭)免费节点订阅链接每天更新
- 6月29日|20.9M/S,Shadowrocket(小火箭)/V2ray/Clash(小猫咪)免费节点订阅链接每天更新
- 7月2日|20.9M/S,Clash(小猫咪)/SSR/V2ray免费节点订阅链接每天更新
- 6月30日|22.7M/S,Clash(小猫咪)/Shadowrocket(小火箭)/V2ray免费节点订阅链接每天更新
最新文章
- 7月18日|19.1M/S,Shadowrocket(小火箭)/V2ray/Clash(小猫咪)免费节点订阅链接每天更新
- 7月17日|22M/S,Shadowrocket(小火箭)/Clash(小猫咪)/V2ray免费节点订阅链接每天更新
- 7月16日|20.3M/S,SSR/Clash(小猫咪)/V2ray免费节点订阅链接每天更新
- 7月15日|22.9M/S,Clash(小猫咪)/SSR/V2ray免费节点订阅链接每天更新
- 7月14日|22.8M/S,Clash(小猫咪)/V2ray/SSR免费节点订阅链接每天更新
- 7月13日|19.9M/S,Clash(小猫咪)/Shadowrocket(小火箭)/V2ray免费节点订阅链接每天更新
- 7月12日|22.3M/S,Clash(小猫咪)/V2ray/Shadowrocket(小火箭)免费节点订阅链接每天更新
- 7月11日|21.4M/S,Clash(小猫咪)/V2ray/Shadowrocket(小火箭)免费节点订阅链接每天更新
- 7月10日|21.6M/S,V2ray/Shadowrocket(小火箭)/Clash(小猫咪)免费节点订阅链接每天更新
- 7月9日|19.6M/S,SSR/V2ray/Clash(小猫咪)免费节点订阅链接每天更新