作者:蔡玮
中间件dble测试成员,主要负责dble的日常测试工作,热衷于探索发现,学习新技术。
本文来源:原创投稿
*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
众所周知,如果我们在网络上传输数据时信息都是使用明文的话,会很容易出现数据被监听和窃取的情况,从而造成一定的安全性的问题,这对于一些个人敏感信息乃至公司的数据安全无疑造成了很大的风险。
基于此,势必有一定的需求,对网络上传输的数据进行“包裹化”处理,而SSL即在此背景之下应运而生。Netscape公司于1996年提出了安全协议SSL,其是工作于应用层和传输层之间的一款协议,设计即全面,其涉及的概念众多,不仅仅“包裹化”了数据【数据加密】,更是提供了身份验证和消息完整性验证机制,为网络数据传输安全性建设做出了巨大贡献,从而非常大程度上改善了互联网的安全性问题。
对于数据库层面,加密通信同样显得很重要,毕竟任何业务的数据存储最终都要落实到数据库上,其重要性不言而喻。所以对于MySQL而言,SSL已经是一个成熟的功能并广泛应用。对于协议实现原理以及加密算法不再是本文介绍的重点,在此就不再赘述,可参考历史公众号文章:MySQL : SSL 连接浅析
概述
作为一款数据库中间件产品,在使用 DBLE 时,将 MySQL 挂载到 DBLE 后端后,完全可以脱离 MySQL 而与 DBLE 进行直接建立连接。那么问题来了,如何确保与 DBLE 进行通信时数据的安全性呢?显然,在这方面 DBLE 需要向 MySQL 学习,使用 SSL 武装自己,以确保通信时用户数据的安全性。
在即将要发版的 DBLE 版本中,我们将会支持 SSL 加密连接,需要注意的是目前加密处理是处于 Client — DBLE 通信阶段,DBLE — MySQL 通信阶段暂未涉及。同时在已经发版的 DBLE 3.22.01.1 的小版本中也已率先支持了 SSL ,感兴趣的同学可以下载相关的版本进行试用。
使用说明
对于 DBLE 的 SSL 连接配置和 MySQL 有一定的相似性,但是并不尽相同,下面就 DBLE 对于 SSL 加密的使用进行简要的配置使用介绍。
熟悉 SSL 的同学应该知道,使用 SSL 的前提必然是各种证书【涉及各种密钥信息】,DBLE 也并不例外。MySQL 中使用的是自签名证书,自签名证书是由不受信的CA机构颁发的数字证书,也就是自己签发的证书。与受信任的CA签发的传统数字证书不同,自签名证书是由一些公司或软件开发商创建、颁发和签名的。DBLE 同样采用和 MySQL 一样的方式:使用自签名证书方式制作 SSL 证书。
证书制作
证书制作需要借助 OpenSSL 来进行,如果机器上并未安装可手动进行安装 OpenSSL 。
1、 制作CA自签名证书(包含公钥)和私钥
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 3600 -key ca-key.pem -out ca.pem
2、 创建私钥和签发服务端的数字证书
openssl req -newkey rsa:2048 -days 3600 -nodes -keyout server-key.pem -out server-req.pem
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 3600 -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
3、创建私钥和签发客户端的数字证书(与上相似)
openssl req -newkey rsa:2048 -days 3600 -nodes -keyout client-key.pem -out client-req.pem
openssl rsa -in client-key.pem -out client-key.pem
openssl x509 -req -in client-req.pem -days 3600 -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem
4、验证服务端和客户端数字证书是否可信,当输出的结果为OK,表示通过
openssl verify -CAfile ca.pem server-cert.pem client-cert.pem
值得一提的是,MySQL 自带一键生成证书的 mysql_ssl_rsa_setup 命令中内部也是大致按照以上生成证书的,所以更方便的做法是直接使用 mysql_ssl_rsa_setup 生成相应的证书文件【当然用于 DBLE 处也需要再进行证书类型转换,见下文】。
证书类型转换
由于 DBLE 是基于 JAVA 语言进行开发的,OpenSSL 生成的证书格式 pem 、crt 等格式,在 JAVA 语言并不能正确识别,需要额外使用 keytool 工具【java 原生自带,安装 java 后不需要再进行安装】转换成 p12 、jks格式,同时如果使用的客户端是 JDBC 时,相关的 URL 中用到的证书也需要使用格式转换后的证书文件,其他 Driver 则均适用于 pem 证书文件。
1、将 ca.pem 导入 Java 平台的密钥库中,java 支持密钥库类型有:JKS 、JCEKS 、PKCS12 、PKCS11 和 DKS ,这里生成 JKS 扩展名的 truststore.jks 密钥库,密码可自定义,此处定义为123456
keytool -import -noprompt -file ca.pem -keystore truststore.jks -storepass 123456
2、将 server-cert.pem 和 server-key.pem 转成 p12 类型的密钥库,然后在转成 JKS 类型的密钥库,密码可自定义,此处定义为123456
openssl pkcs12 -export -in server-cert.pem -inkey server-key.pem -out serverkeystore.p12 -passout pass:123456
keytool -importkeystore -srckeystore serverkeystore.p12 -srcstoretype PKCS12 -destkeystore serverkeystore.jks -srcstorepass 123456 -deststorepass 123456
3、同样,将客户端用到的证书文件转换为 JKS 类型的密钥库,密码可自定义,此处定义为123456
openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -out clientkeystore.p12 -passout pass:123456
keytool -importkeystore -srckeystore clientkeystore.p12 -srcstoretype PKCS12 -destkeystore clientkeystore.jks -srcstorepass 123456 -deststorepass 123456
至此,我们一共得到了以下密钥文件信息:
证书 | 说明 |
---|---|
ca.pem | 自签名CA证书;用于验证数字证书的可信度 |
server-cert.pem、server-key.pem | 服务端数字证书和私钥;作为服务端身份,适用于 除java以外的语言 |
client-vert.pem、client-key.pem | 客户端数字证书和私钥;作为客户端身份,适用于除java以外的语言 |
truststore.jks | 包含自签名CA证书的JKS密钥库;适用于java语言 |
serverkeystore.jks | 包含服务端数字证书和私钥的JKS密钥库;适用于java语言 |
clientkeystore.jks | 包含客户端数字证书和私钥的JKS密钥库;适用于java语言 |
服务端 DBLE 配置
在使用 SSL 时,DBLE 作为服务端需要手动进行配置相关的文件信息,并开启相关的功能。和 MySQL 一致,我们提供了一个开关 supportSSL ,用于标识 SSL 是否启用,默认值为 false ,如果需要使用 SSL 连接时,首先需要确保此开关处于打开的状态。同时需要配置使用到的一些证书信息,在 bootstrap.cnf 中进行如下配置:
-DsupportSSL=true
-DserverCertificateKeyStoreUrl=${path}/serverkeystore.jks
-DserverCertificateKeyStorePwd=123456
-DtrustCertificateKeyStoreUrl=${path}/truststore.jks
-DtrustCertificateKeyStorePwd=123456
配置完成之后,重启 dble 即可。
为了便于查询 SSL 的一些状态信息,我们在 DBLE 的管理端 dble_information 库中新增了一些用于维护相关的 SSL 的元数据信息,确保配置无误并重启 dble 之后,可在 DBLE 管理端查询到对应的 SSL 配置信息以及状态:
客户端连接配置
在使用 SSL 连接 MySQL 时区分了多种连接模式,此方式同样适用于 DBLE ,以下提供两种常见的 Client 加密连接时的客户端配置:
模式 | 连接参数配置 |
---|---|
DISABLED | MySQL client:mysql -uroot -proot --ssl-mode=DISABLED JDBC:jdbc:mysql://ip:port/schema?useSSL=false |
PREFERRED | MySQL client:mysql -uroot -proot --ssl-mode=PREFERRED JDBC:jdbc:mysql://ip:port/schema?requireSSL=false&useSSL=true&verifyServerCerti |
REQUIRED | MySQL client:mysql -uroot -proot --ssl-mode=REQUIRED |
VERIFY_CA | 单向认证(客户端会认证服务端的身份) MySQL client:mysql -uroot -proot --ssl-mode=VERIFY_CA --ssl-ca='${自签名CA证书}' JDBC:jdbc:mysql://ip:port/schema?requireSSL=true&useSSL=true&verifyServerCertificate=true&trustCertificateKeyStoreUrl=file:${自签名CA的JKS格式证书}&trustCertificateKeyStorePassword=${自签名CA证书的JKS密钥库的密码} 双向认证 MySQL client:mysql -uroot -proot --ssl-mode=VERIFY_CA --ssl-ca='${自签名CA证书}' --ssl-cert='${客户端数字证书}' --ssl-key='${客户端私钥}' JDBC:jdbc:mysql://ip:port/schema?requireSSL=true&useSSL=true&verifyServerCertificate=true&trustCertificateKeyStoreUrl=file:${自签名CA证书的JKS密钥库}&trustCertificateKeyStorePassword=${自签名CA证书的JKS密钥库password}&clientCertificateKeyStoreUrl=file:${客户端数字证书和私钥的JKS密钥库}&clientCertificateKeyStorePassword=file:${客户端数字证书和私钥的JKS密钥库password} |
VERIFY_IDENTITIY | 基于VERIFY_CA模式,追加了证书中服务器的主机验证;但上面自签名证书不适宜此模式,因此这里不推荐 |
disabled 模式
在使用 SSL 加密连接 DBLE 之前,让我们先借助抓包工具 wireshark 来看看未使用加密连接 DBLE 时,数据传输是怎么样的。在这里使用 JDBC 作为客户端为例。在进行查询之前,笔者已先行按照上述步骤在 DBLE 侧配置并开启了 SSL ,创建好了 user 表,并准备了相关的数据,在此不作为重点进行赘述。
1、非加密连接 DBLE ,以下为 JDBC Demo 可供参考,与 DBLE 建立连接并查询 user 表数据:
public class SslTest {
private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
public static void main(String[] args) throws SQLException, IOException, ClassNotFoundException {
List res = disabled();
System.out.println(res);
}
public static List disabled() throws ClassNotFoundException, IOException, SQLException {
List usersList = new ArrayList<>();
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("E:\\jdbc\\src\\main\\resources\\dble.properties");
pro.load(fis);
Class.forName(JDBC_DRIVER);
String url = "jdbc:mysql://" + pro.getProperty("host") + ":" + pro.getProperty("port") + "/" + pro.getProperty("db");
String fullUrlString = url + "?useSSL=false"; // 非加密连接
Connection conn = DriverManager.getConnection(fullUrlString, pro.getProperty("user"), pro.getProperty("password"));
PreparedStatement ps = conn.prepareStatement("select username from user");
ResultSet rs = ps.executeQuery();
while(rs.next()){
String name = rs.getString("username");
usersList.add(new User(name));
}
ps.close();
rs.close();
conn.close();
return usersList;
}
}
2、开启抓包后,执行相关 demo 进行查询,将数据包过滤、解析后如下所示:
可以发现,传输的数据包括登录信息、SQL 以及返回的数据信息,都是能够透过 wireshark 经过解析后可以以明文的信息查询到。
required 模式
在此仅以某一种 SSL 加密模式为例进行测试演示——required ,在以上的 JDBC Demo 中稍加修改,将 URL 参数变更为相应的模式参数【如下所示】,即可进行加密通信:
String fullUrlString = url
+ "?useSSL=true&requireSSL=true&verifyServerCertificate=false";
然后再次抓包并执行 Demo 进行查询,解析数据包并过滤得到:
可以发现在建立 TCP 连接之后,SSL 协议随之进行双方的认证过程,具体协议分析可参考:https://www.jianshu.com/p/8028bcbc4e05 ,经过认证之后,随即以 TLS 加密协议的标准将数据包进行加密后传输,即便经过初步的解析后也无法得到传输的数据信息,最终确保了数据的安全性。当然,如果我们有服务端的 SSL 密钥文件,在 wireshark SSL 协议设置中添加相关的密钥信息,也是可以成功解析出传输的具体数据包信息的,在此不再过多演示,感兴趣的读者可自行测试。
凡是都有两面性,加密连接虽然确保了数据的安全性,但是另一方面无疑是牺牲了部分性能。从 SSL 实现方式来看,建立连接时需要进行握手、加密、解密等操作。所以耗时基本都在建立连接的阶段,这对于使用短连接的应用程序可能并不太友好,因为会产生较大的性能损耗。不过对于使用连接池或者长连接的应用程序可能会好许多。所以,对于要求高性能的应用,或者不产生核心敏感数据的应用,性能及可用性才是首要,建议不要采用 SSL 方式。
同时需要注意区别的是,DBLE 侧在进行 SSL 设置时,并没有像 MySQL 一样设置了【require_secure_transport】 类似的强制要求使用安全连接参数设置,也没有按照用户去区分 SSL 配置的适用对象,只要 DBLE 服务端开启并正确配置了 SSL 选项,所有用户与 DBLE 建立连接时均可自主选择是否需要采用 SSL 加密连接。