在真实网络攻击场景中,应用程序或系统本身可能隐藏着一些漏洞,尽管这些漏洞单独来看可能并不构成极其严重的威胁。然而,当这些漏洞与不当的安全配置相结合时,就可能产生灾难性的后果。特别地,当数据库连接信息(包括数据库的连接地址、账号和密码)在配置文件中以明文形式存储时,一旦应用程序存在文件读取、文件包含或者系统被植入Webshell,数据库的安全便荡然无存。
掌握了数据库连接信息后,攻击者可以直接通过数据库连接工具进行连接,进而执行恶意操作,如数据窃取(即“脱库”)。这种攻击不仅会导致企业数据的泄露,还可能对企业声誉、业务运营和财务安全造成难以估量的损害。因此,企业在高度重视应用程序和系统安全性的同时,也在应用程序开发和管理中需要采取合理的配置文件的管理,降低遭受此类攻击的风险。
根据开发习惯的不同,开发人员也会选择特有的配置信息管理方式,而不同的开发框架都有个性化的配置文件,基于开发框架的设定,开发人员往往会默认选择框架的配置文件作为配置信息的存储方式。
application.properties是Spring Boot框架中的一个核心配置文件,它主要用于配置数据库连接、日志设置、自定义属性以及其他与应用程序运行相关的参数,通常位于Spring Boot项目的src/main/resources目录下。例如:
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/abc?useUnicode=true&characterEncoding=utf-8&useTimezone=true&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=chzkwk@135
其中,
spring.datasource.driver-class-name表示数据库驱动类的全限定名,
spring.datasource.url代表数据库的连接URL,包含数据库的地址、端口、数据库名以及可能的连接参数,
spring.datasource.username表示数据库连接的用户名,
spring.datasource.password表示数据库连接的密码。
db.properties配置文件是Java项目中存放数据库连接信息的资源文件,会存储数据库连接所需的各项参数、数据库连接URL、用户名和密码等,这些参数是应用程序与数据库建立连接所必需的,通常位于Java项目中src/main/resources目录下。例如:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/ssm
jdbc.username = root
jdbc.password = xhsy@1lh
其中,
jdbc.driver指定数据库驱动类的全限定名,如com.mysql.dbc.Driver,
jdbc.url指定数据库的连接URL,如jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false,
jdbc.username指定数据库连接的用户名,
jdbc.password指定数据库连接的密码。
上述的示例均是将数据库连接信息(包括用户名、密码等敏感信息)明文存储在配置文件中,一旦该文件被未经授权的人员获取,他们便能够轻松的使用数据库客户端工具进行连接,进而访问和篡改数据库当中的数据信息。配置文件自身的安全便是整体安全防护的重点之一,这涉及到应用安全和系统安全两个大方面的防护,但从纵深防御的角度,这样的配置文件管理已然不是当前应用程序配置管理的主流办法。
配置加密存储
该方法的思路是将配置文件中的配置信息加密存储,并严格管理密钥。即便攻击者获取了配置文件,也只能看到加密后的信息,无法直接用于数据库连接。为实现这一目标可以采用加密工具进行加密配置,从而提升安全性。比如Jasypt。
Jasypt(Java Simplified Encryption)是一个专注于简化Java加密操作的开源工具。它提供了一种简单而有效的方式来加密和解密数据,使开发者能够轻松地保护应用程序中的敏感信息,如数据库密码、API密钥等,而无需深入了解复杂的加密算法细节。
以下面application.properties的配置信息为例
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/abc?useUnicode=true&characterEncoding=utf-8&useTimezone=true&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
加密内容是spring.datasource.username和spring.datasource.password两个属性,下面是该工具在SpringBoot框架开发中的应用。
1. 在POM文件中引入jasypt组件,版本是1.18。
<!--springboot整合jasypt-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>1.18</version>
</dependency>
2. 编写配置信息加密的工具类
publicclass JasyptUtil {
publicclass JasyptUtil {
public static void main(String[] args) {
String account = "root";
String password = "root";
BasicTextEncryptor encryptor = new BasicTextEncryptor();
// 设置秘钥
encryptor.setPassword("eug83f3gG");
// 加密账户名和密码
String newAccount = encryptor.encrypt(account);
String newPassword = encryptor.encrypt(password);
System.out.println("加密后账号:" + newAccount);
System.out.println("加密后密码:" + newPassword);
}
}
因为要得到加密后的密文,所以上述代码中先需要根据原始账号密码以及秘钥来生成加密后的密文,这里假设本地和测试环境的秘钥均为eug83f3gG,通过上面的工具类可以生成密文。
3. 将 application.properties 配置文件中的账号密码用上面的密文替换,如下所示,使用 ENC()包住密文
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/abc?useUnicode=true&characterEncoding=utf-8&useTimezone=true&serverTimezone=GMT%2B8
spring.datasource.username=ENC(/YnTvPH7WnnqMHu+wKeccA==)
spring.datasource.password=ENC(Xv829RzVs7pd2sv72/wsbg==)
需要注意的是,配置文件中的密文需要使用ENC()标记,除此 之外,项目启动参数只需要添加密钥参数-Djasypt.encryptor.password=eug83f3gG即可正常启动,而无需在代码中额外编写解密代码。
环境变量存储
利用环境变量来存储数据库密码可以避免在配置文件中直接以明文形式暴露敏感信息,同时这些环境变量应在操作系统层面或容器化环境(例如Docker)中进行配置,并严格限制仅授权用户才能访问。这种做法显著增强了安全性,因为环境变量不会直接嵌入到源代码或配置文件中,而是被保存在受保护的配置管理系统中,减少了泄露风险。
例如,在Linux系统中设置如下的环境变量:
export DB URL=jdbc:mysql://localhost:3306/ssm
export DB USER=root
export DB PASSWORD=xhsy@llh
之后,再通过代码读取环境变量值,从而获得配置信息:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnection {
// 从环境变量中读取数据库连接信息
private static final String DB_URL = System.getenv("DB_URL");
private static final String DB_USER = System.getenv("DB_USER");
private static final String DB_PASSWORD = System.getenv("DB_PASSWORD");
// 验证所有必要的环境变量是否都已设置
static {
if (DB_URL == null || DB_URL.isEmpty() ||
DB_USER == null || DB_USER.isEmpty() ||
DB_PASSWORD == null || DB_PASSWORD.isEmpty()) {
throw new IllegalStateException("One or more required environment variables are not set.");
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
}
public static void main(String[] args) {
try (Connection conn = getConnection()) {
if (conn != null) {
System.out.println("Connected to the database!");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
配置管理工具
配置管理工具是集中管理配置的工具,可以大大提高系统可维护性和扩展性,常见的配置管理工具有Apollo、Nacos和Spring Cloud Config等,有些工具还内置加密功能保障敏感数据安全,使团队能安全存储和受控访问配置,增强整体安全性和管理效率。使用安全的配置文件管理工具来存储配置文件,可以防范因配置文件泄露而引发的安全风险。
下面以Nacos为例说明该工具的基本使用,Nacos是阿里巴巴开源的动态配置管理与服务发现平台,旨在简化微服务架构下的配置管理,提升服务治理的灵活性和可维护性。它解决了集中化配置管理、动态更新和服务发现等难题,并安全存储配置文件中的敏感信息,防止泄露。
Nacos从2.1版本起,支持配置文件加密存储,以保护敏感信息如数据库连接等。它通过插件机制实现AES加密,确保配置以密文存储于数据库,而在控制台则以明文展示。另外安装Nacos后,应及时修改Nacos控制台默认密码,防止配置文件信息泄露。
Nacos控制台配置列表可创建多个配置文件,在项目中根据配置文件的名称、分组和文件扩展名,进行调用。
配置的编辑中可以设置dataId和group,这两者的定义如下:
dataId:配置文件的名称,通常是配置文件的实际内容,如application.properties、application.yml等。
group:配置分组,用于区分不同环境或不同业务的配置,如DEFAULT_GROUP、TEST_GROUP等。
应用程序调用Nacos中配置信息的步骤如下:
1. 添加Nacos配置管理依赖
在Spring Boot项目的pom.xml文件中添加Nacos配置管理的依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2. 配置Nacos服务地址
在application.properties文件中配置Nacos服务的地址。
spring.application.name=my-application
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
3. 指定调用的配置文件
在application.properties文件中,可以指定要加载的配置文件的名称、分组和文件扩展名。
spring.cloud.nacos.config.file
spring.cloud.nacos.config.file-extension=yaml
spring.cloud.nacos.config.group=DEFAULT_GROUP
spring.cloud.nacos.config.data-id=user-service-test.yaml-extension=yaml
综上,传统的直接在配置文件中写入配置信息的做法(更差的是硬编码配置)存在诸多研发管理和安全管理的不足,比如配置文件提交到代码仓库增加了配置信息泄露的暴露面,配置文件在服务器上可能被非授权人员访问后可直接访问数据库造成数据泄露或篡改,多环境部署时配置文件的管理和同步困难,无法满足快速交付和快速部署,配置变更后需要修改文件并重启应用,降低了应用系统的稳定性。
配置信息加密存储虽然可以解决明文存储配置信息的问题,但在开发和运维过程中依然存在配置文件管理的不足,因此除了明文配置之外,传统的配置文件的劣势依然在加密存储中存在,并不是工程项目最佳的选择。
在DevOps的理念已经被广泛接受和采纳的情况下,配置信息也已经广泛被视同如代码一般进行管理,即配置文件和代码都需要进行代码的版本管理,不同环境(开发环境、测试环境、预生产环境、生产环境)下可以方便、快捷地做代码部署,而无需对于配置信息的差异进行额外修改,这样不仅方便了开发和部署,也方便的配置信息管理,因此采用环境变量或配置管理中心是当前项目开发中主流的配置管理方式。
但即便采用环境变量或配置管理中心也并非意味着配置管理的安全已经万无一失,比如Nacos的不恰当配置也会因为弱口令和暴露被攻击者攻陷,从而获得所有的配置信息,所以安全配置和管理不仅需要考虑研发效能,也需要考虑安全纵深的防御,包括系统安全和应用安全,通过采用现代化的配置管理方案,结合多层次的安全防护措施,才可以大大提高配置信息的安全性。
参考资料
https://cloud.tencent.com/developer/article/2185635
洞源实验室
安全工程师:刘川
2024 年 10 月 28 日