diff --git a/manager/frontend/.env.development b/manager/frontend/.env.development index 5698c2b627a6e976cd98a310a332fcbf70897663..23182380581dd15025e906d513b91f19cb4fcfcd 100755 --- a/manager/frontend/.env.development +++ b/manager/frontend/.env.development @@ -1,2 +1,7 @@ # 开发环境 VITE_BASE_URL = / + +# IK49NV: 前端 AES 加密密钥,必须通过环境变量注入,不得硬编码在源码中。 +# 开发环境使用此占位密钥;生产环境务必在 .env.production 中替换为 +# 高强度随机字符串(建议 32+ 字符),且不得提交到版本控制。 +VITE_ENCRYPTION_KEY = dev-only-do-not-use-in-prod-32chars diff --git a/manager/frontend/.env.site b/manager/frontend/.env.site index 37c0b57b3684e618ae0111cd183113153e44f03b..f1f482d523f9ca9600f23ef57cbf877a5a69303e 100755 --- a/manager/frontend/.env.site +++ b/manager/frontend/.env.site @@ -1,2 +1,5 @@ # 仅用于将构建产物部署到自定义子路径(带子路径的反向代理场景) VITE_BASE_URL = / + +# IK49NV: 前端 AES 加密密钥,部署时务必通过环境变量覆盖为高强度随机值。 +VITE_ENCRYPTION_KEY = dev-only-do-not-use-in-prod-32chars diff --git a/manager/frontend/src/config/global.ts b/manager/frontend/src/config/global.ts index 671374d3283bb8e7082419c6a0e470b0e8ff10b0..1d9c467cb142fa359d7c6c54611735c225a0b0eb 100755 --- a/manager/frontend/src/config/global.ts +++ b/manager/frontend/src/config/global.ts @@ -498,4 +498,36 @@ export const GLOBAL_TERMS = { // Agent管理 AGENT_MANGE: 'Agent管理', }; -export const ENCRYPTION_KEY = 'ocmanager'; +/** + * IK49NV: 前端 AES 加密密钥原硬编码为 'ocmanager',暴露在浏览器源码中, + * 任何用户均可查看,加密形同虚设。 + * + * 修复:密钥改为通过 Vite 构建时环境变量 VITE_ENCRYPTION_KEY 注入, + * 不再硬编码在源码中。同时增加强度校验:密钥长度不少于 16 字符, + * 且不得为已知的弱值。校验失败时抛出异常(fail-fast),避免静默 + * 使用弱密钥。 + * + * 部署方应在 .env.production(或对应环境文件)中设置 + * VITE_ENCRYPTION_KEY 为一个高强度随机字符串(建议 32+ 字符)。 + */ +const WEAK_KNOWN_KEYS = new Set(['ocmanager', '', 'secret', 'key', 'password', 'aes-key']); + +let _cachedEncryptionKey: string | null = null; + +export function getEncryptionKey(): string { + if (_cachedEncryptionKey !== null) return _cachedEncryptionKey; + + const key = (import.meta.env.VITE_ENCRYPTION_KEY as string | undefined) ?? ''; + + if (key.length < 16) { + throw new Error( + 'ENCRYPTION_KEY 未配置或长度不足 16 字符,请在环境变量 VITE_ENCRYPTION_KEY 中设置高强度密钥', + ); + } + if (WEAK_KNOWN_KEYS.has(key.toLowerCase())) { + throw new Error('ENCRYPTION_KEY 配置为已知弱值,请更换为高强度随机密钥'); + } + + _cachedEncryptionKey = key; + return key; +} diff --git a/manager/frontend/src/utils/func.ts b/manager/frontend/src/utils/func.ts index 4fa0ae50de1d2ca06a4d0e73843054fffe6a5ba6..921ed3b3310c42654f73b92eca87729f0f144e94 100755 --- a/manager/frontend/src/utils/func.ts +++ b/manager/frontend/src/utils/func.ts @@ -1,5 +1,7 @@ import dayjs from 'dayjs'; +import CryptoJS from 'crypto-js'; import router from '@/router/index'; +import { getEncryptionKey } from '@/config/global'; // 前端添加id标识 export function formatList(list: any[], key: string, set_key: string) { @@ -158,9 +160,16 @@ export function toPasswordBase64(u8) { return btoa(binary); } -export async function encryptIfPlainWithKey(plain, keyStr) { +/** + * 使用受控 AES 密钥加密明文(若已加密则原样返回)。 + * + * IK49NV: 密钥不再由调用方传入(避免传入硬编码弱密钥 'ocmanager'), + * 改为统一从 getEncryptionKey() 获取经环境变量注入且校验通过的密钥。 + */ +export async function encryptIfPlainWithKey(plain: string): Promise { if (!plain || plain.startsWith('enc:')) return plain; try { + const keyStr = getEncryptionKey(); const encrypted = CryptoJS.AES.encrypt(plain, keyStr).toString(); return `enc:${encrypted}`; } catch (fallbackError) {