Skip to content

perf: AES 加密固定 IV 参数,支持标准 aes-128-cbc 加解密格式(#28) #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ CookieCloud是一个和自架服务器同步Cookie的小工具,可以将浏览

## 官方教程

![](images/20230121141854.png)
![](images/20230121141854.png)

1. 视频教程:[B站](https://www.bilibili.com/video/BV1fR4y1a7zb) | [Youtube](https://youtu.be/3oeSiGHXeQw) 求关注求订阅🥺
1. 图文教程:[掘金](https://juejin.cn/post/7190963442017108027)
Expand All @@ -29,7 +29,7 @@ CookieCloud是一个和自架服务器同步Cookie的小工具,可以将浏览
1. 目前只支持单向同步,即一个浏览器上传,一个浏览器下载
2. 浏览器扩展只官方支持 Chrome 和 Edge。其他 Chrome 内核浏览器可用,但未经测试。使用源码 `cd extension && pnpm build --target=firefox-mv2` 可自行编译 Firefox 版本,注意 Firefox 的 Cookie 格式和 Chrome 系有差异,不能混用

![](images/20230121092535.png)
![](images/20230121092535.png)

## 浏览器插件

Expand Down Expand Up @@ -84,7 +84,7 @@ cd api && yarn install && node app.js

进入浏览器插件列表,点击 service worker,会弹出一个面板,可查看运行日志

![](images/20230121095327.png)
![](images/20230121095327.png)

## API 接口

Expand Down Expand Up @@ -122,12 +122,18 @@ const data = JSON.stringify(cookies);

参考函数

```node
function cookie_decrypt( uuid, encrypted, password )
```js
function cookie_decrypt( uuid, encrypted, password, useIv = false)
{
const CryptoJS = require('crypto-js');
const the_key = CryptoJS.MD5(uuid+'-'+password).toString().substring(0,16);
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key).toString(CryptoJS.enc.Utf8);
const hash = CryptoJS.MD5(uuid+'-'+password).toString();
const the_key = hash.slice(0, 16);
const options = {
iv: CryptoJS.enc.Utf8.parse(hash.slice(8, 24)),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key, useIv ? options : void 0).toString(CryptoJS.enc.Utf8);
const parsed = JSON.parse(decrypted);
return parsed;
}
Expand All @@ -137,7 +143,7 @@ function cookie_decrypt( uuid, encrypted, password )

## 无头浏览器使用CookieCloud示例

请参考 `examples/playwright/tests/example.spec.js`
请参考 `examples/playwright/tests/example.spec.js`

```javascript
test('使用CookieCloud访问nexusphp', async ({ page, browser }) => {
Expand Down Expand Up @@ -167,7 +173,7 @@ async function cloud_cookie( host, uuid, password )
let cookies = [];
if( json && json.encrypted )
{
const {cookie_data, local_storage_data} = cookie_decrypt(uuid, json.encrypted, password);
const {cookie_data, local_storage_data} = cookie_decrypt(uuid, json.encrypted, password, json.iv);
for( const key in cookie_data )
{
// merge cookie_data[key] to cookies
Expand All @@ -180,11 +186,17 @@ async function cloud_cookie( host, uuid, password )
return cookies;
}

function cookie_decrypt( uuid, encrypted, password )
function cookie_decrypt( uuid, encrypted, password, useIv = false)
{
const CryptoJS = require('crypto-js');
const the_key = CryptoJS.MD5(uuid+'-'+password).toString().substring(0,16);
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key).toString(CryptoJS.enc.Utf8);
const hash = CryptoJS.MD5(uuid+'-'+password).toString();
const the_key = hash.slice(0, 16);
const options = {
iv: CryptoJS.enc.Utf8.parse(hash.slice(8, 24)),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key, useIv ? options : void 0).toString(CryptoJS.enc.Utf8);
const parsed = JSON.parse(decrypted);
return parsed;
}
Expand Down
25 changes: 15 additions & 10 deletions api/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ if (!fs.existsSync(data_dir)) fs.mkdirSync(data_dir);

var multer = require('multer');
var forms = multer({limits: { fieldSize: 100*1024*1024 }});
app.use(forms.array());
app.use(forms.array());

const bodyParser = require('body-parser')
app.use(bodyParser.json({limit : '50mb' }));
app.use(bodyParser.json({limit : '50mb' }));
app.use(bodyParser.urlencoded({ extended: true }));

const api_root = process.env.API_ROOT ? process.env.API_ROOT.trim().replace(/\/+$/, '') : '';
Expand All @@ -26,16 +26,16 @@ app.all(`${api_root}/`, (req, res) => {
});

app.post(`${api_root}/update`, (req, res) => {
const { encrypted, uuid } = req.body;
const { encrypted, uuid, iv = false } = req.body;
// none of the fields can be empty
if (!encrypted || !uuid) {
res.status(400).send('Bad Request');
return;
}

// save encrypted to uuid file
// save encrypted to uuid file
const file_path = path.join(data_dir, path.basename(uuid)+'.json');
const content = JSON.stringify({"encrypted":encrypted});
const content = JSON.stringify({ encrypted, iv });
fs.writeFileSync(file_path, content);
if( fs.readFileSync(file_path) == content )
res.json({"action":"done"});
Expand Down Expand Up @@ -67,7 +67,7 @@ app.all(`${api_root}/get/:uuid`, (req, res) => {
// 如果传递了password,则返回解密后的数据
if( req.body.password )
{
const parsed = cookie_decrypt( uuid, data.encrypted, req.body.password );
const parsed = cookie_decrypt( uuid, data.encrypted, req.body.password, data.iv);
res.json(parsed);
}else
{
Expand All @@ -88,12 +88,17 @@ app.listen(port, () => {
console.log(`Server start on http://localhost:${port}${api_root}`);
});

function cookie_decrypt( uuid, encrypted, password )
function cookie_decrypt( uuid, encrypted, password, iv = false)
{
const CryptoJS = require('crypto-js');
const the_key = CryptoJS.MD5(uuid+'-'+password).toString().substring(0,16);
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key).toString(CryptoJS.enc.Utf8);
const hash = CryptoJS.MD5(uuid+'-'+password).toString();
const the_key = hash.slice(0, 16);
const options = {
iv: CryptoJS.enc.Utf8.parse(hash.slice(8, 24)),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key, iv ? options : void 0).toString(CryptoJS.enc.Utf8);
const parsed = JSON.parse(decrypted);
return parsed;
}

21 changes: 13 additions & 8 deletions docker/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ if (!fs.existsSync(data_dir)) fs.mkdirSync(data_dir);

var multer = require('multer');
var forms = multer({limits: { fieldSize: 100*1024*1024 }});
app.use(forms.array());
app.use(forms.array());

const bodyParser = require('body-parser')
app.use(bodyParser.json({limit : '50mb' }));
app.use(bodyParser.json({limit : '50mb' }));
app.use(bodyParser.urlencoded({ extended: true }));

const api_root = process.env.API_ROOT ? process.env.API_ROOT.trim().replace(/\/+$/, '') : '';
Expand All @@ -33,7 +33,7 @@ app.post(`${api_root}/update`, (req, res) => {
return;
}

// save encrypted to uuid file
// save encrypted to uuid file
const file_path = path.join(data_dir, path.basename(uuid)+'.json');
const content = JSON.stringify({"encrypted":encrypted});
fs.writeFileSync(file_path, content);
Expand Down Expand Up @@ -67,7 +67,7 @@ app.all(`${api_root}/get/:uuid`, (req, res) => {
// 如果传递了password,则返回解密后的数据
if( req.body.password )
{
const parsed = cookie_decrypt( uuid, data.encrypted, req.body.password );
const parsed = cookie_decrypt( uuid, data.encrypted, req.body.password, data.iv);
res.json(parsed);
}else
{
Expand All @@ -88,12 +88,17 @@ app.listen(port, () => {
console.log(`Server start on http://localhost:${port}${api_root}`);
});

function cookie_decrypt( uuid, encrypted, password )
function cookie_decrypt( uuid, encrypted, password, useIv = false)
{
const CryptoJS = require('crypto-js');
const the_key = CryptoJS.MD5(uuid+'-'+password).toString().substring(0,16);
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key).toString(CryptoJS.enc.Utf8);
const hash = CryptoJS.MD5(uuid+'-'+password).toString();
const the_key = hash.slice(0, 16);
const options = {
iv: CryptoJS.enc.Utf8.parse(hash.slice(8, 24)),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key, useIv ? options : void 0).toString(CryptoJS.enc.Utf8);
const parsed = JSON.parse(decrypted);
return parsed;
}

16 changes: 11 additions & 5 deletions examples/playwright/tests/example.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async function cloud_cookie( host, uuid, password )
let cookies = [];
if( json && json.encrypted )
{
const {cookie_data, local_storage_data} = cookie_decrypt(uuid, json.encrypted, password);
const {cookie_data, local_storage_data} = cookie_decrypt(uuid, json.encrypted, password, json.iv);
for( const key in cookie_data )
{
// merge cookie_data[key] to cookies
Expand All @@ -37,11 +37,17 @@ async function cloud_cookie( host, uuid, password )
return cookies;
}

function cookie_decrypt( uuid, encrypted, password )
function cookie_decrypt( uuid, encrypted, password, iv = false)
{
const CryptoJS = require('crypto-js');
const the_key = CryptoJS.MD5(uuid+'-'+password).toString().substring(0,16);
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key).toString(CryptoJS.enc.Utf8);
const hash = CryptoJS.MD5(uuid+'-'+password).toString();
const the_key = hash.slice(0, 16);
const options = {
iv: CryptoJS.enc.Utf8.parse(hash.slice(8, 24)),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key, iv ? options : void 0).toString(CryptoJS.enc.Utf8);
const parsed = JSON.parse(decrypted);
return parsed;
}
}
51 changes: 32 additions & 19 deletions extension/function.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,17 @@ export async function upload_cookie( payload )
console.log("error", error);
showBadge("err");
return false;
}
}
// 用aes对cookie进行加密
const the_key = CryptoJS.MD5(payload['uuid']+'-'+payload['password']).toString().substring(0,16);
const hash = CryptoJS.MD5(payload['uuid']+'-'+payload['password']).toString();
const the_key = hash.slice(0, 16);
const data_to_encrypt = JSON.stringify({"cookie_data":cookies,"local_storage_data":local_storages,"update_time":new Date()});
const encrypted = CryptoJS.AES.encrypt(data_to_encrypt, the_key).toString();
const options = {
iv: CryptoJS.enc.Utf8.parse(hash.slice(8, 24)),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
const encrypted = CryptoJS.AES.encrypt(data_to_encrypt, the_key, options).toString();
const endpoint = payload['endpoint'].trim().replace(/\/+$/, '')+'/update';

// get sha256 of the encrypted data
Expand All @@ -189,10 +195,11 @@ export async function upload_cookie( payload )
console.log("same data in 24 hours, skip1");
return {action:'done',note:'本地Cookie数据无变动,不再上传'};
}

const payload2 = {
uuid: payload['uuid'],
encrypted: encrypted
encrypted: encrypted,
iv: true
};
// console.log( endpoint, payload2 );
try {
Expand All @@ -204,15 +211,15 @@ export async function upload_cookie( payload )
});
const result = await response.json();

if( result && result.action === 'done' )
await save_data( 'LAST_UPLOADED_COOKIE', {"timestamp": new Date().getTime(), "sha256":sha256 } );
if( result && result.action === 'done' )
await save_data( 'LAST_UPLOADED_COOKIE', {"timestamp": new Date().getTime(), "sha256":sha256 } );

return result;
} catch (error) {
console.log("error", error);
showBadge("err");
return false;
}
}
}

export async function download_cookie(payload)
Expand All @@ -230,7 +237,7 @@ export async function download_cookie(payload)
const result = await response.json();
if( result && result.encrypted )
{
const { cookie_data, local_storage_data } = cookie_decrypt( uuid, result.encrypted, password );
const { cookie_data, local_storage_data } = cookie_decrypt( uuid, result.encrypted, password, result.iv);
let action = 'done';
if(cookie_data)
{
Expand Down Expand Up @@ -262,8 +269,8 @@ export async function download_cookie(payload)
showBadge("err");
console.log("set cookie error", error);
}


}
}
}
Expand Down Expand Up @@ -292,11 +299,17 @@ export async function download_cookie(payload)
}
}

function cookie_decrypt( uuid, encrypted, password )
function cookie_decrypt( uuid, encrypted, password, iv = false)
{
const CryptoJS = require('crypto-js');
const the_key = CryptoJS.MD5(uuid+'-'+password).toString().substring(0,16);
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key).toString(CryptoJS.enc.Utf8);
const hash = CryptoJS.MD5(uuid+'-'+password).toString();
const the_key = hash.slice(0, 16);
const options = {
iv: CryptoJS.enc.Utf8.parse(hash.slice(8, 24)),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key, iv ? options : void 0).toString(CryptoJS.enc.Utf8);
const parsed = JSON.parse(decrypted);
return parsed;
}
Expand Down Expand Up @@ -342,7 +355,7 @@ async function get_cookie_by_domains( domains = [], blacklist = [] )
{
ret_cookies[domain].push( cookie );
}
}
}
}
}
else
Expand All @@ -353,7 +366,7 @@ async function get_cookie_by_domains( domains = [], blacklist = [] )
// console.log("the cookie", cookie);
if( cookie.domain )
{

let in_blacklist = false;
for( const black of blacklist )
{
Expand All @@ -373,16 +386,16 @@ async function get_cookie_by_domains( domains = [], blacklist = [] )
ret_cookies[cookie.domain].push( cookie );
}
}

}
}

}
// console.log( "ret_cookies", ret_cookies );
return ret_cookies;
}

function buildUrl(secure, domain, path)
function buildUrl(secure, domain, path)
{
if (domain.startsWith('.')) {
domain = domain.substr(1);
Expand Down