首页 应用程序使用AK的最佳方案

应用程序使用AK的最佳方案

更新时间: 2025-04-23 10:25:03

客户可以使用AK或STS作为访问凭证,用程序访问阿里云。但密钥一旦泄露会导致严重的安全风险,因此有极高的安全性要求。本方案介绍应用程序使用AK的最佳方案。

方案简介

客户场景

单账号程序访问密钥防泄漏管理(AK防泄漏)

场景描述

客户可以使用AK或STS作为访问凭证,用程序访问阿里云。但密钥一旦泄露会导致严重的安全风险,因此有极高的安全性要求。

适用客户

  • 所有需要使用程序访问阿里云资源的客户,均为此场景的目标客户。

客户痛点

客户痛点包括:

  • AK/SK配置信息分散在多个应用程序中,无法统一管理
  • AK/SK硬编码在代码中,无法及时更新AK/SK
  • AK/SK以明文方式存在,易发生泄露问题,存在安全隐患
  • 无法追踪AK/SK的使用情况

客户价值包括:

  • 治理当前的AK使用方式,使用多种方式降低AK泄露风险
  • 降低AK泄露一旦发生的损失
  • 提高AK审计能力,从而能够更好的做到泄露事件的处置
  • 在保证安全性的同时降低AK管理成本

架构设计

AK防泄漏管控管理全景

客户使用AK的最优原则和推荐路径

推荐方案:使用无AK方案,避免程序直接接触AK

如果必须使用固定AK,则参考以下方案

对于固定方案中的前两种,即

  • 使用KMS 凭据管家-RAM托管凭据 来做AK配置中心
  • 使用Apollo来做AK配置中心

以下会详细介绍这些方案的具体操作步骤。

实施步骤

实施时长

在实施准备工作完成的情况下,本方案实施预计时长:2小时。

使用KMS凭据管家-托管RAM凭据 来做AK配置中心

方案概览

适用场景

  • 倾向于使用云原生方案,避免自行维护 Vault 带来的运维成本
  • 业务主要部署在阿里云(部署在其他环境也适用,但无法使用 RAM Role可信认证方式和 VPC 来源验证)

方案优势

  • 云原生方案,无需单独部署和维护 Vault
  • 安全性优,对于专属 KMS 标准版,支持独享 HSM 资源池;支持多种可信验证方式,对于部署在阿里云上的应用,可以限制请求来源为某个 VPC,自建 Vault 无法实现
  • 方案可靠性 99.9%,KMS 凭据管家客户端通过客户端缓存等多种措施,支持高并发和大吞吐的访问

前序工作

费用说明

目前新客户只能购买专属KMS版本,根据企业自身需要选择合适的版本进行购买。具体计费方案参考文档。需要注意,除了实例费用外,专有网络个数也会对最终费用有较大影响。目前暂不支持首次购买KMS实例时未添加凭据数量的 KMS 实例升级扩容凭据数量,因此使用该方案时,需要注意添加凭据数量。

对于有合规需求或者高安全性需求的客户,推荐购买标准版。标准版中,密钥安全存储于用户独享的硬件安全密码模块(HSM)资源池。

操作步骤

1、在KMS-“凭据”页面创建新的凭据。根据不同服务需要,可以使用托管RDS凭据、RAM凭据和ECS凭据,这三类凭据和阿里云服务相连接。本着安全的考虑原则,建议使用RAM凭据,可以使用自动轮转AK的功能,无需管理员手动定时更换凭据。

注意!设置凭据值这里请输入这个RAM用户对应的一个合法的AK值。KMS是用这个AK值去做轮转判断的。

具体配置,请参考官方帮助手册。查看链接

2、信息确认后,可以在凭据页面看见我们刚刚创建的凭据。

3、管理员配置完了之后,应用程序侧访问需要先配置应用接入点AAP(Application Access Point),控制应用程序如何使用凭据。

应用接入点最佳实践:

  • 为不同的应用的不同环境创建不同的AAP
  • 根据业务部署环境/系统改造成本,确定可信验证方案是 RAM Role 还是 Client Key。对于 RAM Role 方案,一般需要改造应用资源申请/扩容流程或系统,对于 Client Key 方案,一般需要改造代码发布系统。具体区别和适用场景如下。

应用接入点可信认证方式:

可信认证方式

RAM Role

Client Key

适用场景

所有环境均在阿里云上(尤其是开发环境)

  • 多云部署
  • 开发同学需要在本地电脑测试

优点

  • 应用侧代码改动少
  • 无需改造发布流程
  • 方案通用,适用于云上、云下、多云场景
  • 开发同学本地电脑测试方便

需要注意

  • ECS等实例新建/扩容需要绑定对应角色,因此可能需要改造应用实例资源申请/扩容系统,确保实例角色正确绑定
  • 开发同学本地电脑测试时无法使用实例角色,需要开发环境也在云上或寻求其他替代方案。
    • 替代方案:
      • 自建Token Vending Machine模拟实例角色
      • 只能访问测试环境密钥的固定AK(不推荐,如果确实需要使用,则务必注意限制权限,并增加访问来源限制)
  • 为不同应用程序/不同环境/不同地域创建不同的私钥,尤其是给开发同学本地测试用的私钥,不要和生产环境用同一个
  • 私钥一旦创建无法再次在控制台/API获取,需要妥善保管
  • 私钥有有效期,注意设置提醒进行更新

最佳实践

  • 根据部署资源类型确定运维方案

基于ECS部署应用:通过自动化流程(如服务目录/ESS)生产/扩容ECS,自动绑定实例角色用于访问KMS凭据管家

基于ACK部署应用:使用RRSA组件,通过OIDC方式获取临时Token用于访问KMS凭据管家

基于FC部署应用:在FC服务设置中配置对应角色

  • 实例角色仅需要授权kms:GetSecretValue权限,不要授权KMSFull或者KMSReadOnly,建议最小化权限,不同应用只能获取自己的 Secret
  • 为不同应用程序/不同环境/不同地域创建不同的私钥
  • 生产环境,应用构建时通过发布系统动态将私钥以及私钥密码(写入文件)打包,因此会涉及到发布/代码构建系统的改造。因为私钥一旦创建无法再次获取,因此建议在发布/代码构建系统中统一维护,使用 KMS 加密存储
  • 对于在阿里云上部署的应用,推荐限制访问来源为指定 VPC

4、 创建应用接入点

  1. 登录密钥管理服务控制台
  2. 在页面左上角的地域下拉列表,选择应用接入点所在的地域。
  3. 在左侧导航栏,单击应用管理
  4. 单击创建应用接入点
  5. 创建应用接入点对话框,设置基本信息。
    1. 输入名称描述信息
    2. 认证方式区域,选择认证方式。
    3. 单击下一步
  1. 设置权限策略
    1. 单击可选策略右侧的图标。
    2. 创建权限策略对话框,设置以下参数,然后单击创建

参数名称

参数说明

权限策略名称

权限策略的名称。

作用域

权限策略的适用范围。

取值:共享KMS。

RBAC权限

权限管理模板,表示权限策略对具体资源的操作。

取值:SecretUser,表示可操作的接口为GetSecretValue。

允许访问资源

权限策略被授权的具体对象。可以通过以下两种方法设置:

  • 方法一:在可选资源区域,选择已有资源,然后单击图标。
  • 方法二:在已选资源区域,单击图标,然后手动输入资源,最后单击添加说明 资源支持通配符(*)作为后缀。

网络控制规则

这条控制非常重要。能够从网络层面直接拦截。

权限策略允许访问的网络类型和IP地址。

您可以在可选规则区域,选择已有规则,或者按照以下步骤创建并添加新规则。

  1. 单击图标。
  2. 创建网络访问规则对话框,设置以下参数:
    • 名称:网络访问规则的名称。
    • 网络类型:应用访问KMS的网络类型。取值:
      • Public:适用于应用程序访问KMS的公网Endpoint。
      • VPC:适用于应用程序访问KMS的VPC Endpoint。如果应用部署在阿里云VPC内,则推荐使用该规则。
      • Private:适用于应用程序访问部署到VPC内的专属服务。
    • 描述信息:网络访问规则的详细信息。
    • 允许地址:允许应用程序访问的网络地址。取值:说明 多个IP地址间用半角逗号(,)分隔。
      • 当网络类型为Public时:公网IP地址。
      • 当网络类型为VPC时:VPC ID,以及VPC内的IP地址或者网段。
      • 当网络类型为Private时:私网IP地址或者网段。
  1. 单击创建
  2. 选择已有规则,然后单击图标。

c. 选择已有策略,然后单击图标。

d. 单击下一步

  1. 检查应用接入点信息,然后单击创建

5、如果 AAP 选择了可信认证方案为 Client Key,则需要为 AAP 绑定 Client Key:

  1. 单击应用接入点名称。
  2. Client Key区域,单击创建Client Key
  3. 创建Client Key对话框,设置以下参数。
    • Client Key加密口令在您使用Client Key访问KMS时需要使用该口令对Client Key文件进行解密,请妥善保管。
    • 有效期有效期时间范围外使用Client Key会访问失败。
  1. 单击确定
  2. Client Key对话框,单击下载,保存Client Key私钥文件。

可以参考官方操作步骤。参考链接

6、配置完AAP后,可以在代码中实现了。在代码中需要进行一定改造,需要通过凭据管家客户端(SecretsManager Client)或者其他方式获取该凭据。目前支持的 Client/SDK 如下:

接入方式

机制描述

需要注意

通用 K8S Secrets插件

  • 将 SecretsManager 的 Secret定期刷新到 K8S 保密字典
  • 集群管理人员(系统)为容器应用配置对应的 Secret
  • 容器应用按照 K8S 原生保密字典方式使用

虽然 K8S 插件或者 Client 均会刷新缓存的本地 Secret,但是,在动态轮转AK、DB帐密的情形下,业务开发仍然可能需要改造代码,保证业务中使用AK时,每次都做一次读取

通用客户端 SDK

SecretsManager Client

  • 将 SecretsManager 的 Secret 定期刷新到 Client 的内存
  • 支持配置不同的读取 Secret 的重试策略

针对动态 AK 优化的 SDK

在使用普通阿里云SDK的基础上,加上 remote credential provider

获得普通的阿里云 SDK 的开发体验

业务开发对AK、帐密是否自动轮转几乎无感

  • 提升安全水位,降低泄露后的利用时间窗口
  • 降低应急处置对业务连续性影响

针对动态 DB 帐密优化的 SDK

在使用 JDBCDriver 的基础上,加上 remote credential provider

获得 JDBC Driver 的开发体验

对于 Java 语言,推荐使用针对动态 AK 优化的 SDK。业务开发对AK、帐密是否自动轮转几乎无感,参考文档

对于非 Java 的客户端代码,推荐使用凭据管家客户端

凭据管家客户端基于KMS凭据管家API封装了业务逻辑、最佳实践和设计模式,更易于开发者在业务系统中集成。主要适用于应用中动态使用托管在凭据管家中的凭据,告别对敏感信息的硬编码。

  • 支持开发者在应用中快速集成凭据管家能力,一行代码读取凭据信息。
  • 封装凭据在应用中缓存和刷新的功能。
  • 封装API错误的重试机制,智能处理服务端错误。
  • 开放插件式设计模式,支持开发者自定义扩展缓存、错误重试等功能模块。

使用 SecretsManager Client,推荐根据企业设置的AK轮转周期和访问 KMS QPS(GetSecretValue),设置合理的 Cache TTL。业务开发需要改造代码,保证业务中使用AK时,每次都做一次读取。

Python语言实现

pip install aliyun-secret-manager-client

示例代码

通过配置文件(secretsmanager.properties)构建客户端

建议您采用基于Client Key的应用接入点,通过凭据管家Python SDK使用Client Key。关于如何创建Client Key,请参见为AAP绑定Client Key

凭据管家Python客户端在0.0.4及以上版本支持基于Client Key应用接入点访问凭据管家,需要配置以下配置文件:

## 配置访问方式。
credentials_type=client_key
## 读取Client Key的解密密码:支持从环境变量或者文件读取。
client_key_password_from_env_variable=#your client key private key password environment variable name#
client_key_password_from_file_path=#your client key private key password file path#
## 读取Client Key的私钥文件。
client_key_private_key_path=#your client key private key file path#
## 配置关联的KMS地域。
cache_client_region_id=[{"regionId":"#regionId#"}]

解释一下:

client_key_password_from_env_variable 与 client_key_password_from_file_path 这两个参数只需要填写其中的一个即可。表示的是

可以将这个加密口令写到一个文件里面或者设置到环境变量。

client_key_private_key_path 表示的

下载下来的文件。

相应的Python代码

from alibaba_cloud_secretsmanager_client.secret_manager_cache_client_builder import SecretManagerCacheClientBuilder

if __name__ == '__main__':
    secret_cache_client = SecretManagerCacheClientBuilder.new_client()
    secret_info = secret_cache_client.get_secret_info("#secretName#")
    print(secret_info.__dict__)


Go语言

安装SDK

凭据管家客户端支持Python语言,您可以访问SecretsManager Client for Go开源代码仓库了解更多代码信息。

您可以执行如下安装命令,在项目中使用凭据管家Go客户端。

go get -u github.com/aliyun/alibabacloud-sdk-client-go

示例代码

通过配置文件(secretsmanager.properties)构建客户端

建议您采用基于Client Key的应用接入点,通过凭据管家Go SDK使用Client Key。关于如何创建Client Key,请参见为AAP绑定Client Key

凭据管家Go客户端在v1.0.1及以上版本支持基于Client Key应用接入点访问凭据管家,需要配置以下配置文件:

配置文件(secretsmanager.properties

## 配置访问方式。
credentials_type=client_key
## 读取Client Key的解密密码:支持从环境变量或者文件读取。
client_key_password_from_env_variable=#your client key private key password environment variable name#
client_key_password_from_file_path=#your client key private key password file path#
## 读取Client Key的私钥文件。
client_key_private_key_path=#your client key private key file path#
## 配置关联的KMS地域。
cache_client_region_id=[{"regionId":"#regionId#"}]

示例代码

package main

import (
    "fmt"
    "github.com/aliyun/aliyun-secretsmanager-client-go/sdk/service"
)

func main() {
    client, err := service.NewClient()
    if err != nil {
        // Handle exceptions
        panic(err)
    }
    secretInfo, err := client.GetSecretInfo("#secretName#")
    if err != nil {
        // Handle exceptions
        panic(err)
    }
    fmt.Printf("SecretValue:%s\n",secretInfo.SecretValue)
}

Java语言实现

安装SDK

凭据管家客户端支持Java语言,您可以访问SecretsManager Client for Java开源代码仓库了解更多代码信息。

您可以通过Maven在项目中使用凭据管家Java客户端,需要添加的依赖信息如下:

<dependency>
  <groupId>com.aliyun</groupId>
  <artifactId>alibabacloud-secretsmanager-client</artifactId>
  <version>1.1.7</version>
</dependency>
<dependency>
  <groupId>com.aliyun</groupId>
  <artifactId>aliyun-java-sdk-core</artifactId>
  <version>4.5.9</version>
</dependency>
<!--视当前应用包的情况而定,注意包冲突-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-jdk14</artifactId>
  <version>1.7.9</version>
</dependency>

示例代码

通过配置文件(secretsmanager.properties)构建客户端

建议您采用基于Client Key的应用接入点,通过凭据管家Java SDK使用Client Key。关于如何创建Client Key,请参见为AAP绑定Client Key

凭据管家Java客户端在1.1.8及以上版本支持基于Client Key应用接入点访问凭据管家,需要配置以下配置文件:

## 配置访问方式。
credentials_type=client_key
## 读取Client Key的解密密码:支持从环境变量或者文件读取。
## 可以定义一个环境变量用于保存私钥密码。再将这个环境变量值赋给这个值
client_key_password_from_env_variable=#your client key private key password environment variable name#
client_key_password_from_file_path=#your client key private key password file path#
## 读取Client Key的私钥文件。
client_key_private_key_path=#your client key private key file path#
## 配置关联的KMS地域。
cache_client_region_id=[{"regionId":"#regionId#"}]

通过配置文件(secretsmanager.properties)构建客户端的示例代码如下:

配置环境变量

export credentials_type=client_key
export app_client_key=<设置的私钥值>
export client_key_password_from_env_variable=app_client_key
# 可选跟client_key_password_from_env_variable 二选一
export client_key_password_from_file_path=<your client key private key password from file>
export client_key_private_key_path=<your client key private key file path>
export cache_client_region_id=[{"regionId":"<your region id>"}]
import com.aliyuncs.kms.secretsmanager.client.SecretCacheClient;
import com.aliyuncs.kms.secretsmanager.client.SecretCacheClientBuilder;
import com.aliyuncs.kms.secretsmanager.client.exception.CacheSecretException;
import com.aliyuncs.kms.secretsmanager.client.model.SecretInfo;

public class CacheClientEnvironmentSample {

    public static void main(String[] args) {
        try {
            SecretCacheClient client = SecretCacheClientBuilder.newClient();
            SecretInfo secretInfo = client.getSecretInfo("#secretName#");
            System.out.println(secretInfo);
        } catch (CacheSecretException e) {
            e.printStackTrace();
        }
    }
}

可以参考官方指南,参考链接

  1. AK最后使用时间

身份管理 -> 用户 -> 认证管理。找到指定的ak就可以看到当前这个ak的最后使用时间。通过分析最后使用时间来判断这个AK是否近期活跃。

使用Apollo+开源加密工具来做AK配置中心

注意事项

在使用Apollo作为配置中心时,管理员和开发人员需要拥有一个统一的密钥,用于对敏感数据(如AK/SK)加密存储与配置中心,以及在应用程序中对数据解密。该密钥在实际生产环境中,出于安全考虑,不应写在代码中或和敏感数据存放于同一配置中心之中,需要使用其他方式单独存放。(比如使用Jasypt-spring-boot-starter,在mvn打包时传入密钥)

前序工作

  1. 已经部署apollo,部署步骤可参考官网

操作步骤

  1. 登录apollo页面,根据应用创建项目。建议为每一个应用单独创建项目,实现应用之间配置的隔离。
  2. 进入项目详情页面,点击左下方的“管理密钥”,为项目添加密钥。只有持有密钥的客户端才能访问apollo中该项目的配置信息。(注意:需要apollo在1.6.0版本以上)

  1. 为了达到明文不落盘的目的,需要先使用加密工具对AK信息加密之后,再放入apollo中存储。这里我们选择 jaspty 作为加密工具,它能和spring-boot完美结合,在placeholder中自动解密。这里提供一个简单的使用jaspty的加密工具(关于更多jaspty-spring-boot用法请参考网站
    1. 在maven中添加jaspty依赖
    <!-- jasypt-->
    <!-- 注意jasypt版本不能超过2.0.0,超过后和apollo存在冲突 -->
    <!-- 高于2.0.0版本,jaspty会从缓存中读取数据,导致apollo配置更改时应用无法及时更新 -->
    <!-- 详见https://github.com/ctripcorp/apollo/issues/2162 -->
		<dependency>
			<groupId>com.github.ulisesbocchio</groupId>
			<artifactId>jasypt-spring-boot-starter</artifactId>
			<version>2.0.0</version>
		</dependency>

b. JasptyUtils加密解密工具

import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;

public class JasyptUtils {

    // String key, could not store as Plaintext in code
    private static String PASSWORD = "password";
    private static String ALGORITHM = "PBEWithMD5AndDES";

    // Custom StringEncryptor. Jasypt uses an StringEncryptor to encrypt and decrypt properties.
    private final static PooledPBEStringEncryptor pooledPBEStringEncryptor = new PooledPBEStringEncryptor();
    static {
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        config.setPassword(PASSWORD);
        config.setAlgorithm(ALGORITHM);
        config.setKeyObtentionIterations("1000");
        config.setPoolSize("1");
        config.setProviderName("SunJCE");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setStringOutputType("base64");

        pooledPBEStringEncryptor.setConfig(config);
    }

    public static String encrypt(String message) {
        return pooledPBEStringEncryptor.encrypt(message);
    }

    public static String decrypt(String message) {
        return pooledPBEStringEncryptor.decrypt(message);
    }

    public static void main(String[] args) {
        String EncryptedMessage = JasyptUtils.encrypt("Hello World!");
        System.out.println("Encrypted Message: " + EncryptedMessage);
        System.out.println("Decrypted Message: " + JasyptUtils.decrypt(EncryptedMessage));
    }
}
  1. 我们将所有AK/SK等敏感数据加密后,使用 ENC() 包裹,并在apollo中新增并发布配置。不使用 ENC() 包裹的数据不会被解密

  1. 我们以SpringBoot项目为例,测试在SpringBoot项目中使用apollo获取AK,并用jaspty自动解密。
    1. SpringBoot项目中引入apollo和jaspty依赖
    2. 添加apollo和jaspty配置信息,这里为介绍清晰将apollo的访问密钥和jasypt的加密算法和密钥均硬编码在配置文件中,在实际开发环境时不推荐这样操作。可以使用操作系统环境变量等其他方式添加密钥。(这里配置的算法和密钥必须和之前加密操作配置的一样)
# apollo configuration
apollo.bootstrap.enabled=true
app.id=apollo-access
apollo.meta=http://localhost:8080
apollo.accesskey.secret=d91462f532604b169bc594bb90eb10b6

# jasypt configuration
jasypt.encryptor.password=password
jasypt.encryptor.algorithm=PBEWithMD5AndDES
    1. 获取AK的测试代码
@SpringBootApplication
public class ApolloDemoApplication implements CommandLineRunner {

	public static void main(String[] args) {
		SpringApplication.run(ApolloDemoApplication.class, args);
	}

    # 使用placeholder动态获取,并且jaspty自动解密
	@Value("${AK}")
	String AK;

	@Override
	public void run(String... args) throws Exception {
		while(true) {
			System.out.println("AK: " + AK);
			TimeUnit.SECONDS.sleep(5);
		}
	}
}
  1. 代码运行后能够成功获取AK,并且当AK在apollo上改变时,应用也能成功获取最新数据。

注意事项

AK轮转/替换

1) 静默期

a.遵循安全管理规范,在代码/配置中心/Vault等环境中进行AK排查,定位;

b.相关业务通告;

c.替换后测试/日常环境验证;

c.预发环境最小范围灰度;

2) 重启策略

a.fail fast原则,线上环境分批发布,异常快速回滚;

b.非核心应用优先重启;

参考材料