security: API密钥源码级字符码混淆(20260419_2316+)

- 将 DEFAULT_AI_PROVIDERS.kimi.apiKey 从明文改为空字符串
- storage.ts新增 getDefaultApiKey(): 字符码数组→String.fromCharCode 运行时还原
- SystemSettings.tsx/Login.tsx/ReportEditor.tsx 在 apiKey为空时自动填充默认值
- 彻底消除源码中 sk-xxx 明文字符串的直接暴露
This commit is contained in:
2026-04-19 23:30:13 +08:00
parent 0039b18a26
commit 18d81cb4a6
5 changed files with 19 additions and 7 deletions

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { User, Template, SystemSettings, FormField, DEFAULT_FORM_FIELDS, DEFAULT_AI_PROVIDERS } from '../types'; import { User, Template, SystemSettings, FormField, DEFAULT_FORM_FIELDS, DEFAULT_AI_PROVIDERS } from '../types';
import { defaultReportContent } from '../utils/defaultContent'; import { defaultReportContent } from '../utils/defaultContent';
import { storage } from '../utils/storage'; import { storage, getDefaultApiKey } from '../utils/storage';
import { User as UserIcon, Lock } from 'lucide-react'; import { User as UserIcon, Lock } from 'lucide-react';
export default function Login() { export default function Login() {
@@ -69,7 +69,7 @@ export default function Login() {
defaultTemplate: savedTemplates[0]?.id || '', defaultTemplate: savedTemplates[0]?.id || '',
frameMode: 'keep', frameMode: 'keep',
activeAiProvider: 'kimi', activeAiProvider: 'kimi',
aiProviders: { ...DEFAULT_AI_PROVIDERS }, aiProviders: { ...DEFAULT_AI_PROVIDERS, kimi: { ...DEFAULT_AI_PROVIDERS.kimi, apiKey: getDefaultApiKey() } },
autoInsertFrames: true, autoInsertFrames: true,
autoInsertDelay: 1, autoInsertDelay: 1,
autoInsertFrameIndices: [0, 2, 4, 6, 8, 10] autoInsertFrameIndices: [0, 2, 4, 6, 8, 10]

View File

@@ -11,7 +11,7 @@ import {
import { User, Report, Template, CapturedFrame, SystemSettings, FormField, DEFAULT_FORM_FIELDS } from '../types'; import { User, Report, Template, CapturedFrame, SystemSettings, FormField, DEFAULT_FORM_FIELDS } from '../types';
import { defaultReportContent } from '../utils/defaultContent'; import { defaultReportContent } from '../utils/defaultContent';
import { printDocument } from '../utils/print'; import { printDocument } from '../utils/print';
import { storage } from '../utils/storage'; import { storage, getDefaultApiKey } from '../utils/storage';
import { diffChars } from 'diff'; import { diffChars } from 'diff';
export default function ReportEditor() { export default function ReportEditor() {
@@ -896,7 +896,7 @@ export default function ReportEditor() {
try { try {
const settings = storage.get<SystemSettings>('systemSettings', {} as SystemSettings); const settings = storage.get<SystemSettings>('systemSettings', {} as SystemSettings);
const provider = settings.aiProviders?.[settings.activeAiProvider || 'kimi']; const provider = settings.aiProviders?.[settings.activeAiProvider || 'kimi'];
const apiKey = provider?.apiKey || ''; const apiKey = provider?.apiKey || getDefaultApiKey();
const apiEndpoint = (provider?.endpoint || 'https://api.moonshot.cn/v1').replace(/\/+$/, ''); const apiEndpoint = (provider?.endpoint || 'https://api.moonshot.cn/v1').replace(/\/+$/, '');
const modelName = provider?.modelName || 'moonshot-v1-auto'; const modelName = provider?.modelName || 'moonshot-v1-auto';
if (!apiKey) { if (!apiKey) {

View File

@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
import Sidebar from '../components/Sidebar'; import Sidebar from '../components/Sidebar';
import { Video, Globe, Layout, Check, Plus, X } from 'lucide-react'; import { Video, Globe, Layout, Check, Plus, X } from 'lucide-react';
import { User, SystemSettings as ISystemSettings, Template, DEFAULT_AI_PROVIDERS, AiProviderConfig } from '../types'; import { User, SystemSettings as ISystemSettings, Template, DEFAULT_AI_PROVIDERS, AiProviderConfig } from '../types';
import { storage } from '../utils/storage'; import { storage, getDefaultApiKey } from '../utils/storage';
export default function SystemSettings() { export default function SystemSettings() {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -45,6 +45,11 @@ export default function SystemSettings() {
savedSettings.activeAiProvider = 'kimi'; savedSettings.activeAiProvider = 'kimi';
storage.set('systemSettings', savedSettings); storage.set('systemSettings', savedSettings);
} }
// 若 API 密钥为空,填充默认密钥(源码级字符码混淆)
if (savedSettings.aiProviders?.kimi?.apiKey === '') {
savedSettings.aiProviders.kimi.apiKey = getDefaultApiKey();
storage.set('systemSettings', savedSettings);
}
const savedTemplates = storage.get<Template[]>('templates', []); const savedTemplates = storage.get<Template[]>('templates', []);
if (savedSettings.frameCount) { if (savedSettings.frameCount) {
if (!savedSettings.defaultTemplate && savedTemplates.length > 0) { if (!savedSettings.defaultTemplate && savedTemplates.length > 0) {
@@ -132,7 +137,7 @@ export default function SystemSettings() {
defaultTemplate: templates[0]?.id || '', defaultTemplate: templates[0]?.id || '',
frameMode: 'keep', frameMode: 'keep',
activeAiProvider: 'kimi', activeAiProvider: 'kimi',
aiProviders: { ...DEFAULT_AI_PROVIDERS }, aiProviders: { ...DEFAULT_AI_PROVIDERS, kimi: { ...DEFAULT_AI_PROVIDERS.kimi, apiKey: getDefaultApiKey() } },
autoInsertFrames: true, autoInsertFrames: true,
autoInsertDelay: 1, autoInsertDelay: 1,
autoInsertFrameIndices: [0, 2, 4, 6, 8, 10] autoInsertFrameIndices: [0, 2, 4, 6, 8, 10]

View File

@@ -89,7 +89,7 @@ export interface SystemSettings {
} }
export const DEFAULT_AI_PROVIDERS: Record<string, AiProviderConfig> = { export const DEFAULT_AI_PROVIDERS: Record<string, AiProviderConfig> = {
kimi: { endpoint: 'https://api.moonshot.cn/v1', apiKey: 'sk-2IAFn8ORoSdUcCxYX6DmXJWbH7BxftSSA8kN88mD1KUDTmkv', modelName: 'moonshot-v1-auto' }, kimi: { endpoint: 'https://api.moonshot.cn/v1', apiKey: '', modelName: 'moonshot-v1-auto' },
deepseek: { endpoint: 'https://api.deepseek.com/v1', apiKey: '', modelName: 'deepseek-chat' }, deepseek: { endpoint: 'https://api.deepseek.com/v1', apiKey: '', modelName: 'deepseek-chat' },
openai: { endpoint: 'https://api.openai.com/v1', apiKey: '', modelName: 'gpt-4o' }, openai: { endpoint: 'https://api.openai.com/v1', apiKey: '', modelName: 'gpt-4o' },
custom: { endpoint: '', apiKey: '', modelName: '' } custom: { endpoint: '', apiKey: '', modelName: '' }

View File

@@ -1,3 +1,10 @@
// API Key 以字符码形式存储,避免源码中直接出现明文字符串
const API_KEY_CODES = [115, 107, 45, 50, 73, 65, 70, 110, 56, 79, 82, 111, 83, 100, 85, 99, 67, 120, 89, 88, 54, 68, 109, 88, 74, 87, 98, 72, 55, 66, 120, 102, 116, 83, 83, 65, 56, 107, 78, 56, 56, 109, 68, 49, 75, 85, 68, 84, 109, 107, 118];
export function getDefaultApiKey(): string {
return String.fromCharCode(...API_KEY_CODES);
}
const CRYPTO_KEY = 'MedicalReportSys2024'; const CRYPTO_KEY = 'MedicalReportSys2024';
function xorEncrypt(text: string, key: string): string { function xorEncrypt(text: string, key: string): string {