开发中对敏感和隐私数据的保护是重要的,没做好很容易就发生数据泄露事件,比如最近发生的上海公安数据库泄露事件 ,泄露了大量的个人数据。
日常开发中接触到的敏感数据包括但不限于数据库和其他中间件及其他系统的账号密码,日志中的个人数据,比如手机号、身份证号和住址等等信息......这里我们只讨论在代码层面如何加密保护敏感数据,主要是配置文件中的敏感数据处理和推送到git仓库平台如何隐藏敏感数据。
对对敏感数据的加密保护方法多种多样,不同层面上有不同的方法。对于数据库密码这类信息,常用的连接池比如Druid 带有加密工具,但是比较局限,如果是Spring Cloud很多服务的项目,可以用配置中心,统一管理各个服务的配置;如果是想加密整个配置文件,可以使用git-encrypt 工具配置git使用。
Spring Cloud 配置中心 这里先列出一些基本项目概念和作用:
注册中心:服务发现治理。
配置文件git仓库:微服务项目各个服务的配置(这些配置也可以不放在git仓库,放本地也行)
配置中心服务端:拉取git仓库的配置文件,提供给其他微服务使用。
客户端微服务:通过注册中心(或链接地址)找到配置中心服务端,根据{application name}-{profiles-active}.yml找到该服务的配置文件。
配置中心的作用就是统一管理微服务配置,微服务中一般一个服务会有多个节点,个节点大部分配置都是相同的,使用配置中心可以避免重复写配置,并且发生修改时可以批量地修改多个节点。
配置中心服务端、客户端和配置存储 主要看三个jar包配置,对应三个功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-config-server</artifactId > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-bus-amqp</artifactId > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-config-monitor</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-security</artifactId > </dependency >
存储配置文件可以用git仓库,或者直接放在本地。
git仓库 如果用GitHub的仓库可能会碰到一些问题,GitHub私有库已经不支持账号密码方式访问,需要用git协议。用git仓库根据Spring Cloud 文档生成密钥:
1 ssh-keygen -m PEM -t rsa -b 4096 -f ~/config_server_deploy_key.rsa
根据文档 写配置,然后你就会看到启动有错误:
1 ERROR: You're using an RSA key with SHA-1, which is no longer allowed. Please use a newer client or a different key type. Please see https://github.blog/2021-09-01-improving-git-protocol-security-github/ for more information.
GitHub不再支持SHA-1的加密方式了,需要换一种加密方法生成密钥,根据stackoverflow 的解决方法,用ecdsa签名算法生成密钥:
1 2 ssh-keygen -m PEM -t ecdsa -b 256
具体的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 spring: cloud: config: server: git: uri: https://gitee.com/zdshen/lin-config-spring.git default-label: master bus: trace: enabled: true security: user: name: nathan password: 123 rabbitmq: host: localhost port: 5672 username: guest password: guest encrypt: key: ${ENCRYPT_KEY}
本地 本地的配置就简单多了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 spring: cloud: config: server: native: search-locations: file:///project/gitFile/lin/lin-config-spring bus: trace: enabled: true security: user: name: nathan password: 123 rabbitmq: host: localhost port: 5672 username: guest password: guest encrypt: key: ${ENCRYPT_KEY}
启动后就可以访问各服务的配置,接口访问规则:
1 2 3 4 5 6 7 8 /{application}-{profile}.yml /{application}-{profile}.properties /{application}/{profile}[/{label}] /{label}/{application}-{profile}.yml /{label}/{application}-{profile}.properties
最后到git平台上,在存储配置的仓库去设置Webhook的回调接口 http://{ip:port}//monitor?path=*(path用于定位哪些服务应该更新配置。这里使用 * 来通知所有服务),当有新的提交push到仓库时就会出发回调请求服务端接口,然后再通过MQ发消息通知有变动的客户端重新拉取新的配置。
客户端比较简单,引入config和mq等依赖就行:
1 2 3 4 5 6 7 8 9 <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-config</artifactId > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-bus-amqp</artifactId > </dependency > ...
客户端的配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 spring: application: name: lin-config-client profiles: active: dev cloud: config: uri: http://localhost:9000 label: master profile: dev username: nathan password: 123
直接写服务端的uri地址,多个的话写discovery service-id,也就是在Eureka注册的服务id名。
存储库则是直接写yml配置文件,比如有个服务名称是spring-test-api,配置文件的文件名根据dev和prd或其他环境可写成spring-test-api-dev.yml和spring-test-api-prd.yml等。
配置的加密 在上面服务端和客户端的配置文件中可以看到一项配置是:
1 2 encrypt: key: ${ENCRYPT_KEY}
这是用于加密的加密密钥,这里用的是变量,在idea测试的时候可以写在Configurations的Environment variables中,在机器上运行的话就写在机器的环境变量中,比如Linux用zsh的话就是 在~/.zshrc 文件加上:
1 export ENCRYPT_KEY=nathan2022
在配置中心服务端,访问 http://{ip:port}/encrypt/status get请求返回ok就表示正常,
http://{ip:port}/encrypt post请求将数据加密,其中--user nathan:123 是spring security的basic认证:
1 2 3 4 5 6 7 8 9 10 11 12 13 $ curl -u nathan:123 http://localhost:9000/encrypt/status {"status" :"OK" } $ curl --user nathan:123 -i -XPOST --data 'nathan' http://localhost:9000/encrypt/ HTTP/1.1 200 Content-Type: text/plain;charset=UTF-8 Content-Length: 64 Date: Wed, 27 Jul 2022 08:54:24 GMT cab819603b5cb0e87fc7597c9948b33270a9d16954b3113ca14741a3adbd5a09
cab819603b5cb0e87fc7597c9948b33270a9d16954b3113ca14741a3adbd5a09 就是‘nathan’字符串加密生成的字符串。
比如有个客户端服务用到数据库,可以对账户密码进行加密:
1 2 3 4 5 6 7 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/lin?characterEncoding=UTF8&useSSL=false&serverTimezone=Asia/Shanghai username: '{cipher}cab819603b5cb0e87fc7597c9948b33270a9d16954b3113ca14741a3adbd5a09' password: '{cipher}6a74f568c18e7d926bf8ab0427a03d8fb8f307f58088a196b7450cac5add41c0'
加密的字段值要以{cipher}开头,后面接的就是通过 http://{ip:port}/encrypt 接口生成的加密串。这样,我们就实现了对敏感数据的加密。
git-crypt 上面我们看到的是Spring Cloud配置中心的使用以及如何对具体的配置字段进行加密,如何想对整个配置文件加密可以用工具git-crypt 。
Ubuntu下的安装,执行命令:
1 sudo apt install git-crypt
在仓库根目录新建.gitattributes文件,加密的规则就写在这个文件中。
1 2 3 4 5 6 7 8 # 需要被加密的文件,可以用通配符 #config/*.yml filter=git-crypt diff=git-crypt #*.config filter=git-crypt diff=git-crypt spring-client-prd.yml filter=git-crypt diff=git-crypt # Making sure that .gitattributes is never encrypted. DON'T TOUCH THAT LINE AND ONE BELOW .gitattributes !filter !diff
如果在未成功加密之前就进行了提交,历史文件是不会自动加密的,需要有变动重新提交才会加密,或者运行 git-crypt status -f
然后提交并 push 远端。
查看加密的文件有哪些:
重新clone仓库到另一个文件夹。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # nathan @ nathan-tp in ~/Documents/lin-config-spring on git:master o [9:54:52] $ cat spring-client-prd.yml GITCRYPT1=f��L6��I��?��6Q$ S� V�N����4��=6��W? ��2RH>*_ �o7�e�ȑ҃I���T�����-D!�o&l�ո|���H���ͷ�� ��L��p��(y� )�w��)ي��(k�$�ɵCP�ɻ%F���,p)��f�ps ���f3 � 딇<A�V"T?������̟�A������>��MVC(�g����c $ާ�ZϨ!�!�W�O% # nathan @ nathan-tp in ~/Documents/lin-config-spring on git:master o [9:54:56] $ git-crypt unlock /home/nathan/app/backup/key # nathan @ nathan-tp in ~/Documents/lin-config-spring on git:master o [9:57:26] $ cat spring-client-prd.yml config: name: lin-config-client active: prd version: 1.0.0 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/lin?characterEncoding=UTF8&useSSL=false&serverTimezone=Asia/Shanghai username: nathan password: 123456%
可以看到文件被加密了,都是乱码,GitHub上面看到的是一个raw文件。用导出的密钥解密之后就能看到原始文件了。
本文涉及的代码可以在GitHub 找到,包含lin-config-spring、lin-config-server和lin-config-client三个工程。
参考