Files
ocdp-go/docs/security/security-implementation.md
mangomqy c5e51ed069 ocdp v1
2025-11-13 02:54:06 +00:00

357 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🔒 安全方案文档
## 概述
本项目实现了一套完整的敏感信息保护方案确保密码、证书、Token 等敏感数据在存储和传输过程中的安全性。
## 🎯 解决的安全问题
### 1. 硬编码敏感信息
**问题**:代码中硬编码了 Harbor 密码、K8s 证书等敏感信息
**解决方案**:全部移至环境变量,通过 `.env` 文件配置
### 2. 明文存储密码和证书
**问题**:数据库中以明文存储敏感数据
**解决方案**:使用 AES-256-GCM 加密存储
### 3. API 响应泄露敏感信息
**问题**API 返回完整的密码和证书数据
**解决方案**:自动脱敏,仅返回掩码(`••••••••`
### 4. 前端显示敏感信息
**问题**:前端表单可能显示原始密码
**解决方案**:显示掩码,修改时仅支持覆盖
## 🏗️ 架构设计
```
┌─────────────────────────────────────────────────────────────┐
│ 前端 (Frontend) │
│ • 显示脱敏数据(••••••••) │
│ • 修改时仅支持覆盖,不能查看原值 │
└─────────────────────────────────────────────────────────────┘
│ HTTPS
┌─────────────────────────────────────────────────────────────┐
│ REST API (Handler 层) │
│ • 接收请求中的明文敏感数据 │
│ • 响应时自动脱敏(调用 ToRegistryResponse/ToClusterResponse
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Service 层(业务逻辑) │
│ • 处理业务逻辑 │
│ • 不关心加密/解密细节 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Repository 层(数据持久化) │
│ • Create/Update: 自动加密敏感数据后存储 │
│ • GetByID/List: 自动解密敏感数据后返回 │
│ • 使用 AES-256-GCM 加密算法 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Database加密存储
│ • 密码加密存储Base64 编码的密文) │
│ • 证书加密存储Base64 编码的密文) │
│ • Token加密存储Base64 编码的密文) │
└─────────────────────────────────────────────────────────────┘
```
## 🔐 加密实现
### 加密算法
- **算法**AES-256-GCM (Galois/Counter Mode)
- **密钥长度**256 bits (由用户提供的密钥通过 SHA256 派生)
- **认证加密**:提供数据机密性和完整性
- **随机 Nonce**:每次加密使用随机 nonce同样的明文产生不同的密文
### 加密流程
```go
// 1. 用户配置加密密钥
encryptor := crypto.NewAESEncryptor(config.EncryptionKey)
// 2. Repository 创建时注入加密器
clusterRepo := mock.NewClusterRepositoryMock(encryptor)
registryRepo := mock.NewRegistryRepositoryMock(encryptor)
// 3. 存储时自动加密
func (r *RegistryRepositoryMock) Create(ctx context.Context, registry *entity.Registry) error {
encrypted := r.encryptRegistry(registry) // 加密敏感字段
r.registries[registry.ID] = encrypted
return nil
}
// 4. 读取时自动解密
func (r *RegistryRepositoryMock) GetByID(ctx context.Context, id string) (*entity.Registry, error) {
registry := r.registries[id]
return r.decryptRegistry(registry), nil // 解密敏感字段
}
```
### 加密的字段
#### Registry镜像仓库
-`Password` - Harbor/镜像仓库密码
#### ClusterKubernetes 集群)
-`CAData` - CA 证书
-`CertData` - 客户端证书
-`KeyData` - 客户端密钥
-`Token` - Bearer Token
## 🎭 脱敏显示
### DTO 转换
```go
// Registry 响应 - 自动脱敏
func ToRegistryResponse(registry *entity.Registry) *RegistryResponse {
response := &RegistryResponse{
Username: registry.Username, // 用户名不脱敏
Password: crypto.MaskSensitiveData(registry.Password), // 密码脱敏
HasPassword: registry.Password != "",
}
return response
}
// Cluster 响应 - 自动脱敏
func ToClusterResponse(cluster *entity.Cluster) *ClusterResponse {
response := &ClusterResponse{
CAData: crypto.MaskSensitiveData(cluster.CAData), // ••••••••
CertData: crypto.MaskSensitiveData(cluster.CertData), // ••••••••
KeyData: crypto.MaskSensitiveData(cluster.KeyData), // ••••••••
Token: crypto.MaskSensitiveData(cluster.Token), // ••••••••
HasCAData: cluster.CAData != "",
HasCertData: cluster.CertData != "",
}
return response
}
```
### API 响应示例
```json
{
"id": "registry-123",
"name": "Harbor Production",
"url": "https://harbor.example.com",
"username": "admin",
"password": "••••••••",
"has_password": true
}
```
## 🔧 配置指南
### 1. 生成加密密钥
```bash
# 生成强加密密钥
openssl rand -base64 32
# 生成 JWT 密钥
openssl rand -base64 32
```
### 2. 创建 .env 文件
```bash
# 复制模板
cp backend/.env.example backend/.env
# 编辑配置
nano backend/.env
```
### 3. 必填配置项
```bash
# 生产环境必须修改这些配置!
ENCRYPTION_KEY=<your-encryption-key-from-openssl>
JWT_SECRET=<your-jwt-secret-from-openssl>
```
### 4. 可选:配置默认资源
```bash
# 默认用户
DEFAULT_USER_USERNAME=admin
DEFAULT_USER_PASSWORD=your-secure-password
DEFAULT_USER_EMAIL=admin@example.com
# 默认集群
DEFAULT_CLUSTER_NAME=Production K8s
DEFAULT_CLUSTER_HOST=https://k8s.example.com:6443
DEFAULT_CLUSTER_CA_DATA=<base64-encoded-ca-cert>
DEFAULT_CLUSTER_CERT_DATA=<base64-encoded-client-cert>
DEFAULT_CLUSTER_KEY_DATA=<base64-encoded-client-key>
# 默认镜像仓库
DEFAULT_REGISTRY_NAME=Harbor Production
DEFAULT_REGISTRY_URL=https://harbor.example.com
DEFAULT_REGISTRY_USERNAME=admin
DEFAULT_REGISTRY_PASSWORD=your-harbor-password
```
## 🚀 使用指南
### 启动应用
```bash
cd backend
# 开发模式(使用 Mock 存储)
make run-mock
# 生产模式(使用实际数据库)
ADAPTER_MODE=prod DATABASE_URL="postgresql://..." make run-prod
```
### 验证加密
```bash
# 1. 创建一个 Registry
curl -X POST http://localhost:8080/api/v1/registries \
-H "Content-Type: application/json" \
-d '{
"name": "Test Registry",
"url": "https://registry.example.com",
"username": "admin",
"password": "MySecretPassword123"
}'
# 2. 获取 Registry密码已脱敏
curl http://localhost:8080/api/v1/registries/<registry-id>
# 响应示例:
# {
# "password": "••••••••", // 已脱敏
# "has_password": true
# }
```
## 📝 前端集成
### 显示脱敏数据
```typescript
// Registry 表单
<Input
type="password"
value={registry.password} // 显示为 ••••••••
placeholder="修改密码(留空保持不变)"
onChange={(e) => setPassword(e.target.value)}
/>
// 说明文字
{registry.has_password && (
<p className="text-sm text-gray-500">
当前已设置密码(加密存储)。输入新密码以覆盖。
</p>
)}
```
### 修改策略
- **查看时**:显示掩码 `••••••••`,不显示实际值
- **修改时**
- 输入新值 → 覆盖原值
- 留空 → 保持原值不变
- 无法查看原值
## 🔒 安全最佳实践
### ✅ DO应该做
1. **生产环境必须使用强密钥**
```bash
ENCRYPTION_KEY=$(openssl rand -base64 32)
JWT_SECRET=$(openssl rand -base64 32)
```
2. **妥善保管 .env 文件**
- 不要提交到 Git已加入 `.gitignore`
- 使用密钥管理服务(如 AWS Secrets Manager, HashiCorp Vault
- 定期轮换密钥
3. **使用 HTTPS**
- 生产环境必须启用 TLS/SSL
- 使用有效的 SSL 证书
4. **定期审计**
- 定期检查访问日志
- 监控异常访问
### ❌ DON'T不应该做
1. ❌ 不要在代码中硬编码敏感信息
2. ❌ 不要将 `.env` 文件提交到版本控制
3. ❌ 不要在日志中打印敏感数据
4. ❌ 不要在前端缓存敏感信息
5. ❌ 不要使用默认密钥用于生产环境
## 🧪 测试
### 加密/解密测试
```bash
cd backend
go test ./internal/pkg/crypto -v
```
### 集成测试
```bash
# 运行所有测试
make test
# 测试加密存储
go test ./internal/adapter/output/persistence/mock -v
```
## 📊 性能考虑
- **加密开销**AES-GCM 加密非常快,对性能影响可忽略
- **内存使用**:每次读取时解密,不在内存中缓存明文
- **并发安全**Repository 使用 RWMutex 保护并发访问
## 🆘 故障排查
### 问题:解密失败
**原因**`ENCRYPTION_KEY` 发生变化
**解决方案**
1. 确保使用相同的加密密钥
2. 如果密钥丢失,需要重新创建所有敏感数据
### 问题API 返回空密码
**原因**:密码未设置或解密失败
**解决方案**
1. 检查 `has_password` 字段
2. 查看后端日志确认是否有解密错误
## 📚 相关文档
- [AES-GCM 加密算法](https://en.wikipedia.org/wiki/Galois/Counter_Mode)
- [Go crypto 包文档](https://pkg.go.dev/crypto)
- [OWASP 安全编码实践](https://owasp.org/www-project-secure-coding-practices-quick-reference-guide/)
## 🤝 贡献
如果发现安全问题,请:
1. 不要公开披露
2. 通过私密渠道联系维护者
3. 提供详细的复现步骤
## 📄 许可证
本项目的安全方案遵循项目主许可证。