From 018971509a2fccffcd4dcae5dbe98cfa61570947 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Tue, 17 Oct 2023 16:01:54 +0800 Subject: [PATCH] :sparkles: Feature(custom): upload api now return encrypted full result --- public/i18n/en.yml | 1 + public/i18n/zh-CN.yml | 1 + public/i18n/zh-TW.yml | 1 + src/main/server/routerManager.ts | 29 ++++++++++++++++++--- src/main/utils/aesHelper.ts | 39 +++++++++++++++++++++++++++++ src/renderer/pages/PicGoSetting.vue | 20 ++++++++++++++- src/universal/types/i18n.d.ts | 1 + src/universal/types/view.d.ts | 3 ++- 8 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 src/main/utils/aesHelper.ts diff --git a/public/i18n/en.yml b/public/i18n/en.yml index de5e1352..269dfbe9 100644 --- a/public/i18n/en.yml +++ b/public/i18n/en.yml @@ -204,6 +204,7 @@ SETTINGS_SET_SERVER_KEY: Set Auth Key SETTINGS_TIP_PLACEHOLDER_HOST: Default:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_PORT: Default:36677 SETTINGS_TIP_PLACEHOLDER_KEY: This key is used to avoid malicious requests, through urlParams '?key=xxx' to pass +SETTINGS_SET_SERVER_AES_KEY: Set AES Key for server response SETTINGS_LOG_LEVEL_ALL: All SETTINGS_LOG_LEVEL_SUCCESS: Success SETTINGS_LOG_LEVEL_ERROR: Error diff --git a/public/i18n/zh-CN.yml b/public/i18n/zh-CN.yml index c6d459a0..784207de 100644 --- a/public/i18n/zh-CN.yml +++ b/public/i18n/zh-CN.yml @@ -206,6 +206,7 @@ SETTINGS_SET_SERVER_KEY: 设置鉴权密钥 SETTINGS_TIP_PLACEHOLDER_HOST: 推荐默认地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_PORT: 推荐默认端口:36677 SETTINGS_TIP_PLACEHOLDER_KEY: 用于接口鉴权, 通过url参数添加'?key=xxx' +SETTINGS_SET_SERVER_AES_KEY: 设置接口数据加密密钥 SETTINGS_LOG_LEVEL_ALL: 全部-All SETTINGS_LOG_LEVEL_SUCCESS: 成功-Success SETTINGS_LOG_LEVEL_ERROR: 错误-Error diff --git a/public/i18n/zh-TW.yml b/public/i18n/zh-TW.yml index e122d883..c056fd3e 100644 --- a/public/i18n/zh-TW.yml +++ b/public/i18n/zh-TW.yml @@ -204,6 +204,7 @@ SETTINGS_SET_SERVER_KEY: 設定鑒權密鑰 SETTINGS_TIP_PLACEHOLDER_HOST: 推薦預設地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_PORT: 推薦預設端口:36677 SETTINGS_TIP_PLACEHOLDER_KEY: 用於接口鑒權, 通過url參數添加'?key=xxx' +SETTINGS_SET_SERVER_AES_KEY: 設定AES加密密鑰 SETTINGS_LOG_LEVEL_ALL: 全部-All SETTINGS_LOG_LEVEL_SUCCESS: 成功-Success SETTINGS_LOG_LEVEL_ERROR: 錯誤-Error diff --git a/src/main/server/routerManager.ts b/src/main/server/routerManager.ts index e714dfd3..acf58c7c 100644 --- a/src/main/server/routerManager.ts +++ b/src/main/server/routerManager.ts @@ -11,6 +11,7 @@ import picgo from '@core/picgo' import { changeCurrentUploader } from '../utils/handleUploaderConfig' import { app } from 'electron' import fs from 'fs-extra' +import { AESHelper } from '../utils/aesHelper' const appPath = app.getPath('userData') const serverTempDir = path.join(appPath, 'serverTemp') @@ -76,12 +77,18 @@ router.post('/upload', async ({ const fullResult = result.fullResult logger.info('[PicList Server] upload result:', res) if (res) { + const treatedFullResult = { + isAESEncrypted: 1, + AESEncryptedData: new AESHelper().encrypt(JSON.stringify(fullResult)), + ...fullResult + } + delete treatedFullResult.config handleResponse({ response, body: { success: true, result: [res], - fullResult: [fullResult] + fullResult: [treatedFullResult] } }) } else { @@ -107,7 +114,13 @@ router.post('/upload', async ({ return item.url }) const fullResult = result.map((item: any) => { - return item.fullResult + const treatedItem = { + isAESEncrypted: 1, + AESEncryptedData: new AESHelper().encrypt(JSON.stringify(item.fullResult)), + ...item.fullResult + } + delete treatedItem.config + return treatedItem }) logger.info('[PicList Server] upload result', res.join(' ; ')) if (res.length) { @@ -163,7 +176,17 @@ router.post('/delete', async ({ return } try { - const result = await deleteChoosedFiles(list) + // 区分是否是aes加密的数据,如果不是直接传入list,如果是,解密后再传入 + const treatList = list.map(item => { + if (item.isAESEncrypted) { + const aesHelper = new AESHelper() + const data = aesHelper.decrypt(item.AESEncryptedData) + return JSON.parse(data) + } else { + return item + } + }) + const result = await deleteChoosedFiles(treatList) const successCount = result.filter(item => item).length const failCount = result.filter(item => !item).length if (successCount) { diff --git a/src/main/utils/aesHelper.ts b/src/main/utils/aesHelper.ts new file mode 100644 index 00000000..ea99be4a --- /dev/null +++ b/src/main/utils/aesHelper.ts @@ -0,0 +1,39 @@ +import crypto from 'crypto' +import picgo from '@core/picgo' + +function getDerivedKey (): Buffer { + const userPassword = picgo.getConfig('settings.aesPassword') || 'PicList-aesPassword' + const fixedSalt = Buffer.from('a8b3c4d2e4f5098712345678feedc0de', 'hex') + const fixedIterations = 100000 + const keyLength = 32 + return crypto.pbkdf2Sync(userPassword, fixedSalt, fixedIterations, keyLength, 'sha512') +} + +export class AESHelper { + key: Buffer + constructor () { + this.key = getDerivedKey() + } + + encrypt (plainText: string) { + const iv = crypto.randomBytes(16) + const cipher = crypto.createCipheriv('aes-256-cbc', this.key, iv) + let encrypted = cipher.update(plainText, 'utf8', 'hex') + encrypted += cipher.final('hex') + const encryptedData = `${iv.toString('hex')}:${encrypted}` + return encryptedData + } + + decrypt (encryptedData: string) { + const parts = encryptedData.split(':') + if (parts.length !== 2) { + return '{}' + } + const iv = Buffer.from(parts[0], 'hex') + const encryptedText = parts[1] + const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, iv) + let decrypted = decipher.update(encryptedText, 'hex', 'utf8') + decrypted += decipher.final('utf8') + return decrypted + } +} diff --git a/src/renderer/pages/PicGoSetting.vue b/src/renderer/pages/PicGoSetting.vue index cc6d0c66..8d63510e 100644 --- a/src/renderer/pages/PicGoSetting.vue +++ b/src/renderer/pages/PicGoSetting.vue @@ -598,6 +598,18 @@ {{ $T('SETTINGS_CLICK_TO_SET') }} + + + @@ -1702,7 +1714,8 @@ const form = reactive({ yourlsDomain: '', yourlsSignature: '', deleteLocalFile: false, - serverKey: '' + serverKey: '', + aesPassword: '' }) const languageList = i18nManager.languageList.map(item => ({ @@ -1860,6 +1873,7 @@ async function initData () { form.yourlsSignature = settings.yourlsSignature || '' form.deleteLocalFile = settings.deleteLocalFile || false form.serverKey = settings.serverKey || '' + form.aesPassword = settings.aesPassword || 'PicList-aesPassword' currentLanguage.value = settings.language ?? 'zh-CN' currentStartMode.value = settings.startMode || 'quiet' customLink.value = settings.customLink || '![$fileName]($url)' @@ -2210,6 +2224,10 @@ function handleYourlsSignatureChange (val: string) { saveConfig('settings.yourlsSignature', val) } +function handleAesPasswordChange (val: string) { + saveConfig('settings.aesPassword', val || 'PicList-aesPassword') +} + function confirmLogLevelSetting () { if (form.logLevel.length === 0) { return $message.error($T('TIPS_PLEASE_CHOOSE_LOG_LEVEL')) diff --git a/src/universal/types/i18n.d.ts b/src/universal/types/i18n.d.ts index f6054333..2af637b9 100644 --- a/src/universal/types/i18n.d.ts +++ b/src/universal/types/i18n.d.ts @@ -199,6 +199,7 @@ interface ILocales { SETTINGS_TIP_PLACEHOLDER_HOST: string SETTINGS_TIP_PLACEHOLDER_PORT: string SETTINGS_TIP_PLACEHOLDER_KEY: string + SETTINGS_SET_SERVER_AES_KEY: string SETTINGS_LOG_LEVEL_ALL: string SETTINGS_LOG_LEVEL_SUCCESS: string SETTINGS_LOG_LEVEL_ERROR: string diff --git a/src/universal/types/view.d.ts b/src/universal/types/view.d.ts index 82e8fc8d..c5acabcf 100644 --- a/src/universal/types/view.d.ts +++ b/src/universal/types/view.d.ts @@ -28,7 +28,8 @@ interface ISettingForm { yourlsDomain: string, yourlsSignature: string, deleteLocalFile: boolean, - serverKey: string + serverKey: string, + aesPassword: string } interface IShortKeyMap {