百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

经验分享丨JAVA审计中常见的加密错误,你知道几个?

toyiye 2024-09-16 06:02 3 浏览 0 评论

在代码审计中,经常会发现开发人员由于密码学知识的欠缺,造成安全函数误用。本文是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开发人员工作中多注意安全编码技能的提高。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码