Skip to content

Commit 4b4e8d4

Browse files
committed
feat: enhance cookie management and implement pure JS SHA-256 hashing for improved compatibility and security
1 parent 793bdb0 commit 4b4e8d4

5 files changed

Lines changed: 150 additions & 20 deletions

File tree

doc/更新日志.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
V6.3.3
2+
更新时间 2025-10-11
3+
4+
* 修复
5+
6+
=================
17
V6.3.2
28
更新时间 2025-10-10
39

package-dist.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"name":"llonebot","version":"6.3.2","type":"module","description":"","main":"llonebot.js","author":"linyuchen"}
1+
{"name":"llonebot","version":"6.3.3","type":"module","description":"","main":"llonebot.js","author":"linyuchen"}

src/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from 'fs'
22
import packageJson from '../package-dist.json'
33

4-
export const version = '6.3.2'
4+
export const version = '6.3.3'
55

66
export const writeVersion = ()=>{
77
const pkgJsonPath = './package-dist.json'

src/webui/FE/utils/cookie.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,34 @@ export function setCookie(name: string, value: string, days: number = 30): void
1212
const date = new Date();
1313
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
1414
const expires = `expires=${date.toUTCString()}`;
15-
15+
1616
// 设置 Cookie 属性
17-
// SameSite=Strict: 防止 CSRF 攻击
17+
// SameSite=Lax: 允许同站和顶级导航携带Cookie,适合局域网IP访问
18+
// - Strict: 完全禁止跨站请求(会导致局域网IP访问时Cookie丢失)
19+
// - Lax: 允许顶级导航(如直接访问链接),但禁止第三方站点嵌入
20+
// - None: 允许所有跨站请求(需配合Secure标志,仅HTTPS可用)
1821
// Secure: 仅在 HTTPS 下传输(开发环境可能是 HTTP,所以条件判断)
1922
// HttpOnly: 无法通过 JS 设置(但我们需要 JS 访问,所以不设置)
2023
const isSecure = window.location.protocol === 'https:';
2124
const secureFlag = isSecure ? '; Secure' : '';
22-
23-
document.cookie = `${name}=${value}; ${expires}; path=/; SameSite=Strict${secureFlag}`;
25+
26+
// 对于非localhost的IP访问(如局域网IP),不设置SameSite属性以确保兼容性
27+
const hostname = window.location.hostname;
28+
const isIpAddress = /^\d+\.\d+\.\d+\.\d+$/.test(hostname);
29+
const sameSiteFlag = isIpAddress ? '' : '; SameSite=Lax';
30+
31+
const cookieString = `${name}=${value}; ${expires}; path=/${sameSiteFlag}${secureFlag}`;
32+
document.cookie = cookieString;
33+
34+
// 调试日志
35+
// console.log('[Cookie] 设置Cookie:', {
36+
// name,
37+
// hostname,
38+
// isIpAddress,
39+
// isSecure,
40+
// cookieString,
41+
// currentCookies: document.cookie
42+
// });
2443
}
2544

2645
/**
@@ -31,13 +50,18 @@ export function setCookie(name: string, value: string, days: number = 30): void
3150
export function getCookie(name: string): string | null {
3251
const nameEQ = name + '=';
3352
const ca = document.cookie.split(';');
34-
53+
3554
for (let i = 0; i < ca.length; i++) {
3655
let c = ca[i];
3756
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
38-
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
57+
if (c.indexOf(nameEQ) === 0) {
58+
const value = c.substring(nameEQ.length, c.length);
59+
// console.log('[Cookie] 获取Cookie:', { name, value, allCookies: document.cookie });
60+
return value;
61+
}
3962
}
40-
63+
64+
console.log('[Cookie] 未找到Cookie:', { name, allCookies: document.cookie });
4165
return null;
4266
}
4367

src/webui/FE/utils/passwordHash.ts

Lines changed: 111 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,123 @@
33
* 使用 SHA-256 对密码进行哈希处理
44
*/
55

6+
/**
7+
* 纯 JavaScript 实现的 SHA-256(兼容所有浏览器环境)
8+
* 来源:简化版本,符合 SHA-256 标准
9+
*/
10+
function sha256Pure(message: string): string {
11+
// 初始化哈希值
12+
const K = [
13+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
14+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
15+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
16+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
17+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
18+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
19+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
20+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
21+
];
22+
23+
const utf8Encode = new TextEncoder();
24+
const messageBytes = utf8Encode.encode(message);
25+
const messageBits = messageBytes.length * 8;
26+
27+
// 填充消息
28+
const paddedLength = Math.ceil((messageBits + 65) / 512) * 64;
29+
const padded = new Uint8Array(paddedLength);
30+
padded.set(messageBytes);
31+
padded[messageBytes.length] = 0x80;
32+
33+
// 添加消息长度
34+
const view = new DataView(padded.buffer);
35+
view.setUint32(paddedLength - 4, messageBits & 0xffffffff, false);
36+
37+
// 初始化哈希值
38+
let h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a;
39+
let h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
40+
41+
// 处理每个512位块
42+
for (let chunk = 0; chunk < paddedLength; chunk += 64) {
43+
const w = new Uint32Array(64);
44+
for (let i = 0; i < 16; i++) {
45+
w[i] = view.getUint32(chunk + i * 4, false);
46+
}
47+
for (let i = 16; i < 64; i++) {
48+
const s0 = ((w[i - 15] >>> 7) | (w[i - 15] << 25)) ^ ((w[i - 15] >>> 18) | (w[i - 15] << 14)) ^ (w[i - 15] >>> 3);
49+
const s1 = ((w[i - 2] >>> 17) | (w[i - 2] << 15)) ^ ((w[i - 2] >>> 19) | (w[i - 2] << 13)) ^ (w[i - 2] >>> 10);
50+
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
51+
}
52+
53+
let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
54+
55+
for (let i = 0; i < 64; i++) {
56+
const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
57+
const ch = (e & f) ^ (~e & g);
58+
const temp1 = (h + S1 + ch + K[i] + w[i]) >>> 0;
59+
const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
60+
const maj = (a & b) ^ (a & c) ^ (b & c);
61+
const temp2 = (S0 + maj) >>> 0;
62+
63+
h = g;
64+
g = f;
65+
f = e;
66+
e = (d + temp1) >>> 0;
67+
d = c;
68+
c = b;
69+
b = a;
70+
a = (temp1 + temp2) >>> 0;
71+
}
72+
73+
h0 = (h0 + a) >>> 0;
74+
h1 = (h1 + b) >>> 0;
75+
h2 = (h2 + c) >>> 0;
76+
h3 = (h3 + d) >>> 0;
77+
h4 = (h4 + e) >>> 0;
78+
h5 = (h5 + f) >>> 0;
79+
h6 = (h6 + g) >>> 0;
80+
h7 = (h7 + h) >>> 0;
81+
}
82+
83+
// 输出哈希值
84+
return [
85+
h0.toString(16).padStart(8, '0'),
86+
h1.toString(16).padStart(8, '0'),
87+
h2.toString(16).padStart(8, '0'),
88+
h3.toString(16).padStart(8, '0'),
89+
h4.toString(16).padStart(8, '0'),
90+
h5.toString(16).padStart(8, '0'),
91+
h6.toString(16).padStart(8, '0'),
92+
h7.toString(16).padStart(8, '0')
93+
].join('');
94+
}
95+
696
/**
797
* 将字符串转换为 SHA-256 哈希
898
* @param message 要哈希的字符串
999
* @returns 十六进制格式的哈希值
10100
*/
11101
export async function sha256(message: string): Promise<string> {
12-
// 将字符串编码为 UTF-8
13-
const msgBuffer = new TextEncoder().encode(message);
14-
15-
// 计算哈希
16-
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
17-
18-
// 转换为十六进制字符串
19-
const hashArray = Array.from(new Uint8Array(hashBuffer));
20-
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
21-
22-
return hashHex;
102+
// 优先使用纯JS实现,以确保在所有环境下都可用(包括局域网IP访问)
103+
try {
104+
return sha256Pure(message);
105+
} catch (error) {
106+
console.error('[PasswordHash] 纯JS SHA-256实现失败:', error);
107+
108+
// 如果纯JS实现失败,尝试使用 Web Crypto API
109+
if (typeof crypto !== 'undefined' && crypto.subtle) {
110+
try {
111+
const msgBuffer = new TextEncoder().encode(message);
112+
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
113+
const hashArray = Array.from(new Uint8Array(hashBuffer));
114+
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
115+
} catch (cryptoError) {
116+
console.error('[PasswordHash] Web Crypto API失败:', cryptoError);
117+
}
118+
}
119+
120+
// 如果所有方法都失败,抛出错误
121+
throw new Error('无法计算SHA-256哈希');
122+
}
23123
}
24124

25125
/**

0 commit comments

Comments
 (0)