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

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

更新时间: 2024-04-02 14:13:25

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

该方案即将下线,请参考《第三方配置中心Apollo集中管控AccessKey》、《阿里云KMS全托管RAM凭据集中管控AK/SK》。

一、方案简介

二、客户场景

客户场景:单账号程序访问密钥防泄漏管理(AK防泄漏)

场景描述

客户可以使用AK或STS作为访问凭证,用程序访问阿里云。但密钥一旦泄露会导致严重的安全风险,因此有极高的安全性要求。我们除了提供AK防泄漏产品能力和最佳实践,考虑到已有很多客户在错误的使用密钥,还应该提供足够友好的迁移方案。

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

三、客户痛点

3.1 客户痛点包括:

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

3.2 客户价值包括:

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

四、架构设计

AK防泄漏管控管理全景

客户使用AK的最优原则

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

AK存储

AK使用

AK审计

1、ECS实例角色

2、程序SSO(用SAML/OIDC Provider实现)

3、AK Vault (KMS凭据管家 或第三方Vault)

仅针对AK Vault方案

AK最后使用时间

方案内容包括

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

以下会详细介绍这三种方式的具体操作步骤。

五、部署指导书

5.1 使用KMS凭据管家来做AK配置中心

  1. 在KMS-“凭据”页面创建新的凭据。根据不同服务需要,可以使用托管RDS凭据、RAM凭据和ECS凭据,这三类凭据和阿里云服务相连接,可以使用自动轮转的功能,无需管理员手动定时更换凭据。为简便操作,这里我们使用其他凭据,且暂时不配置自动轮转。

  1. 信息确认后,可以在凭据页面看见我们刚刚创建的凭据。凭据会自动加密存储,并且我们无法在页面上查看凭据的内容;在凭据下发时,会自动进行解密,并用HTTPS协议传输凭据,无需用户手动加解密。

  1. 我们使用一个简单的Java程序作为示例,获取KMS中凭据的值。
    1. 添加maven依赖
            com.aliyun
            aliyun-java-sdk-core
            4.5.16
        
        
            com.aliyun
            aliyun-java-sdk-kms
            2.12.0
        
        
            com.alibaba
            fastjson
            1.2.9
        
    1. 为了访问KMS服务,最简单的做法时在阿里云控制台RAM中创建拥有访问KMS权限的角色(不推荐这么做,建议从ECS实例维度安全访问KMS,可参考文档
      1. 权限策略包括:只读访问密钥管理服务(KMS)的权限、获取KMS中的凭据的权限
    1. 编写Java代码
public class RdsSecretSampleCode {
    private static KmsClient kmsClient;
    // 拥有访问KMS权限的角色的AK和SK
    private static String accessKey = "x";
    private static String secretKey = "xx";

    // 配置KMS Client
    static {
        kmsClient = KmsClient.getKMSClient("cn-hangzhou", accessKey, secretKey);
    }

    static class KmsClient {
        private DefaultAcsClient acsClient;

        private KmsClient(DefaultAcsClient acsClient) {
            this.acsClient = acsClient;
        }

        private static KmsClient getKMSClient(String regionId, String accessKeyId, String accessKeySecret) {
            IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
            DefaultAcsClient client = new DefaultAcsClient(profile);
            return new KmsClient(client);
        }
    }

    // 通过凭据信息获取指定的AK
    private static String getAK(String secretName) throws ClientException {
        // 配置获取AK的请求
        final GetSecretValueRequest request = new GetSecretValueRequest();
        // 配置凭据名称
        request.setSecretName(secretName);

        GetSecretValueResponse response = kmsClient.acsClient.getAcsResponse(request);
        JSONObject secretDataJSON = JSON.parseObject(response.getSecretData());
        return secretDataJSON.getString("AK");
    }

    public static void main(String[] args) throws ClientException, InterruptedException {
        while (true) {
            System.out.println(getAK("test"));
            TimeUnit.SECONDS.sleep(5);
        }
    }
}
  1. 控制台能够正确地读取AK信息

  1. 接下来我们需要更新KMS中的凭据信息。进入凭据详情页面,可以看见凭据拥有版本状态。只有版本状态为 ACSCurrent 的凭据信息会被获取。
    1. 点击“存入凭据值”,设置版本号为v2,并且填入新的AK值。

b. 存入完成后,v1和v2版本号都会出现。但此时只有v2版本被标记为 ACSCurrent ,因此只能读取v2的凭据信息。

c. 测试3.c中的代码能够成功接受到凭据更新。

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

前序工作
费用说明
  • 密钥托管费用

密钥创建者

计费项

公共云地域的单价(元/天)

用户

软件密钥的密钥版本

0.014

基础硬件密钥的密钥版本

0.237

高级硬件密钥的密钥版本

  • 版本数小于等于2000的部分:0.593
  • 版本数大于2000的部分:0.237

保护级别为Software的用户主密钥。

密钥规格为Aliyun_AES_256Aliyun_SM4RSA_2048,保护级别为HSM的用户主密钥。

密钥规格为Aliyun_SM2RSA_3072EC_P256EC_P256K,保护级别为HSM的用户主密钥。

  • API调用费用: 每月20,000次的免费调用额度。超出此免费额度的服务密钥调用,以及对您自己创建的密钥的调用,按照以下规则进行计费。

密钥类别

适用于中国内地公共云地域的单价(元/万次)

适用于中国内地之外公共云地域的单价(元/万次)

基础密钥

0.6

0.214

高级密钥

1.8

1.068

密钥规格为Aliyun_AES_256Aliyun_SM4RSA_2048的用户主密钥。

密钥规格为Aliyun_SM2RSA_3072EC_P256EC_P256K的用户主密钥。

操作步骤

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

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

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

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

3、管理员配置完了之后,如何让应用程序访问拿到这个凭据呢。推荐使用凭据管家客户端。

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

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

建议您采用基于Client Key的应用接入点来构建凭据管家客户端。能够控制应用程序更安全地使用凭据。

4、管理应用接入点

您可以创建应用接入点AAP(Application Access Point),控制应用程序如何使用凭据。

4.1 创建应用接入点

  1. 登录密钥管理服务控制台
  2. 在页面左上角的地域下拉列表,选择应用接入点所在的地域。
  3. 在左侧导航栏,单击应用管理
  4. 单击创建应用接入点
  5. 创建应用接入点对话框,设置基本信息。
    1. 输入名称描述信息
    2. 认证方式区域,选择认证方式。
      1. RAMRole: 如果您为应用程序的运行环境(例如:ECS实例、ACK集群、函数计算)绑定了RAM角色,可以使用RAMRole的认证方式. (一般企业还没习惯这个用法。)
      2. Client Key: 您可以使用Client Key的认证方式,为AAP绑定客户端证书。AAP使用证书的公钥,对应用程序进行身份认证。
    1. 单击下一步
  1. 设置权限策略
    1. 单击可选策略右侧的图标。
    2. 创建权限策略对话框,设置以下参数,然后单击创建

参数名称

参数说明

权限策略名称

权限策略的名称。

作用域

权限策略的适用范围。

取值:共享KMS。

RBAC权限

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

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

允许访问资源

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

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

网络控制规则

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

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

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

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

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

d. 单击下一步

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

4.2 为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私钥文件。

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

5、有了Client key之后,就可以在代码中实现了。

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客户端,需要添加的依赖信息如下:

  com.aliyun
  alibabacloud-secretsmanager-client
  1.1.7


  com.aliyun
  aliyun-java-sdk-core
  4.5.9



  org.slf4j
  slf4j-jdk14
  1.7.9

示例代码

通过配置文件(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=
export client_key_private_key_path=
export cache_client_region_id=[{"regionId":""}]
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是否近期活跃。

5.3 使用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依赖
    
    
    
		
			com.github.ulisesbocchio
			jasypt-spring-boot-starter
			2.0.0
		

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上改变时,应用也能成功获取最新数据。

六、注意事项

6.1 AK轮转/替换

1) 静默期

a.遵循安全管理规范,进行AK排查,定位;

b.相关业务通告;

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

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

2) 重启策略

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

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

七、参考材料