最后更新:2026-04-18
硬编码 API 密钥(Hardcoded API Key)指将 LLM 服务的 API 密钥(如 OpenAI API Key、Anthropic API Key)直接硬编码在 Java 源代码、配置文件或 Docker 镜像中。这是最常见但也最危险的安全问题之一——一旦代码仓库泄露或被访问,API 密钥即被暴露,攻击者可利用这些密钥冒用身份、消耗 API 额度、访问敏感数据。
| 维度 | 评级 |
|---|---|
| OWASP Top 10 | A07:2025 - Identification and Authentication Failures |
| CWE | CWE-798 |
| 严重程度 | 严重 |
代码仓库(GitHub/GitLab)公开或被攻击后,硬编码的 API 密钥直接暴露。
// 源代码中硬编码 OpenAI API Key
String apiKey = "sk-proj-abc123def456ghi789jkl012mno345";
application.properties/yml 中包含明文 API 密钥,随应用包或 Docker 镜像一起分发。
# application.yml 中明文存储 API Key
spring:
ai:
openai:
api-key: sk-proj-abc123def456ghi789jkl012mno345
Java 应用编译后的 class 文件中仍然包含硬编码的字符串常量,攻击者通过反编译即可获取密钥。
# 反编译 class 文件后可直接看到硬编码的密钥
javap -c -p AppConfig.class
# String apiKey = "sk-proj-abc123def456ghi789jkl012mno345";
应用日志中意外打印了 API 密钥(如调试日志、异常堆栈中的配置信息)。
2026-04-18 10:00:00 DEBUG - Initializing OpenAI client with key: sk-proj-abc123...
// [VULNERABLE] 在源代码中硬编码 OpenAI API Key
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.web.bind.annotation.*;
@RestController
public class HardcodedKeyVulnerableController {
// [VULNERABLE] 此代码存在硬编码密钥漏洞,原因:API Key 直接写在源代码中
private static final String OPENAI_API_KEY = "sk-proj-abc123def456ghi789jkl012mno345";
private final ChatClient chatClient;
public HardcodedKeyVulnerableController() {
// 漏洞:API Key 硬编码在源代码中,代码泄露即密钥泄露
OpenAiApi api = OpenAiApi.builder()
.apiKey(OPENAI_API_KEY)
.build();
OpenAiChatModel model = OpenAiChatModel.builder()
.openAiApi(api)
.build();
this.chatClient = ChatClient.builder(model).build();
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return chatClient.prompt().user(message).call().content();
}
}
# [VULNERABLE] application.yml - 明文存储 API Key
spring:
ai:
openai:
api-key: sk-proj-abc123def456ghi789jkl012mno345
base-url: https://api.openai.com
anthropic:
api-key: sk-ant-api03-xyz789abc456def012ghi345
// [VULNERABLE] 直接使用配置属性(明文存储在配置文件中)
@Configuration
public class HardcodedKeyVulnerableConfig {
@Value("${spring.ai.openai.api-key}")
private String openaiApiKey; // 漏洞:从明文配置文件读取
@Bean
public ChatClient chatClient() {
OpenAiApi api = OpenAiApi.builder()
.apiKey(openaiApiKey)
.build();
return ChatClient.builder(
OpenAiChatModel.builder().openAiApi(api).build()
).build();
}
}
// [SECURE] 使用环境变量注入 API Key
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.context.annotation.*;
@Configuration
public class HardcodedKeySecureConfig {
// [SECURE] 修复了硬编码密钥漏洞,修复方式:从环境变量读取密钥
@Bean
public ChatClient chatClient() {
// 安全 1:从环境变量读取 API Key,不在代码或配置文件中明文存储
String apiKey = System.getenv("OPENAI_API_KEY");
if (apiKey == null || apiKey.isBlank()) {
throw new IllegalStateException(
"OPENAI_API_KEY environment variable is not set");
}
OpenAiApi api = OpenAiApi.builder()
.apiKey(apiKey)
.build();
OpenAiChatModel model = OpenAiChatModel.builder()
.openAiApi(api)
.build();
return ChatClient.builder(model).build();
}
}
# [SECURE] application.yml - 使用环境变量占位符
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY} # 安全:从环境变量注入
base-url: ${OPENAI_BASE_URL:https://api.openai.com}
// [SECURE] 使用 AWS Secrets Manager 等密钥管理服务
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest;
@Configuration
public class HardcodedKeySecureKmsConfig {
private final SecretsManagerClient secretsClient;
// [SECURE] 修复了硬编码密钥漏洞,修复方式:使用 KMS 动态获取密钥
@Bean
public ChatClient chatClient() {
// 安全 2:从密钥管理服务动态获取 API Key
String apiKey = getSecret("prod/openai-api-key");
if (apiKey == null || apiKey.isBlank()) {
throw new IllegalStateException("Failed to retrieve API key from KMS");
}
OpenAiApi api = OpenAiApi.builder()
.apiKey(apiKey)
.build();
OpenAiChatModel model = OpenAiChatModel.builder()
.openAiApi(api)
.build();
return ChatClient.builder(model).build();
}
private String getSecret(String secretName) {
try {
GetSecretValueRequest request = GetSecretValueRequest.builder()
.secretId(secretName)
.build();
return secretsClient.getSecretValue(request).secretString();
} catch (Exception e) {
throw new RuntimeException("Failed to retrieve secret: " + secretName, e);
}
}
}
// [SECURE] 确保日志中不打印 API Key
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Configuration
public class SecureLoggingConfig {
private static final Logger log = LoggerFactory.getLogger(SecureLoggingConfig.class);
@Bean
public ChatClient chatClient() {
String apiKey = System.getenv("OPENAI_API_KEY");
// 安全 3:日志中只打印密钥前4位,其余用星号替代
if (apiKey != null) {
log.info("OpenAI API Key loaded: {}...{}",
apiKey.substring(0, 4),
"*".repeat(Math.max(0, apiKey.length() - 4)));
} else {
log.error("OPENAI_API_KEY environment variable is not set");
throw new IllegalStateException("API key not configured");
}
// ... 初始化 ChatClient
OpenAiApi api = OpenAiApi.builder().apiKey(apiKey).build();
OpenAiChatModel model = OpenAiChatModel.builder().openAiApi(api).build();
return ChatClient.builder(model).build();
}
}
sk-proj-、sk-ant-)application.properties/yml、Docker 环境文件中是否存在明文密钥