mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-07 16:27:38 +08:00
安全修复: - 修复 dom.js 和 simyo/app.js 中的 innerHTML XSS 注入风险 - 使用 HTMLSanitizer.escapeHtml/escapeAttr 替代直接模板拼接 - 将 onclick 内联事件替换为 data-* 属性 + addEventListener - 移除 server.js 中硬编码的 Simyo X-Client-Token 架构优化: - 新增 _shared/rate-limiter.js 分布式限流模块 (Netlify Blobs) - verify-cookie.js 内存限流替换为 KV 跨实例共享方案 - giffgaff/utils.js debounce/throttle 改为委托共享实现 - simyo/app.js 会话存储迁移至 SecureStorage (自动 TTL 过期) - 合并 notifications-internal.js 至 notifications.js 消除双维护路径
66 lines
1.9 KiB
JavaScript
66 lines
1.9 KiB
JavaScript
'use strict';
|
||
|
||
/**
|
||
* 分布式限流模块
|
||
* 优先使用 Netlify Blobs 实现跨实例共享,降级为内存限流
|
||
*/
|
||
|
||
// 内存限流降级方案
|
||
const memoryStore = new Map();
|
||
|
||
function memoryRateLimit(key, windowMs, maxRequests) {
|
||
const now = Date.now();
|
||
const arr = memoryStore.get(key) || [];
|
||
const recent = arr.filter(ts => now - ts < windowMs);
|
||
recent.push(now);
|
||
memoryStore.set(key, recent);
|
||
return recent.length > maxRequests;
|
||
}
|
||
|
||
/**
|
||
* Netlify Blobs 限流(跨实例共享)
|
||
*/
|
||
async function blobsRateLimit(key, windowMs, maxRequests) {
|
||
try {
|
||
const { getStore } = await import('@netlify/blobs');
|
||
const store = getStore('rate-limits');
|
||
|
||
const data = await store.get(key, { type: 'json' });
|
||
const now = Date.now();
|
||
|
||
let timestamps = [];
|
||
if (data && Array.isArray(data.timestamps)) {
|
||
timestamps = data.timestamps.filter(ts => now - ts < windowMs);
|
||
}
|
||
|
||
timestamps.push(now);
|
||
|
||
const limited = timestamps.length > maxRequests;
|
||
|
||
// 更新时间戳记录,设置过期时间为窗口宽度 + 10 秒缓冲
|
||
await store.set(key, JSON.stringify({ timestamps }), {
|
||
expirationTtl: Math.ceil(windowMs / 1000) + 10
|
||
});
|
||
|
||
return limited;
|
||
} catch (error) {
|
||
// Blobs 不可用时降级到内存限流
|
||
console.warn('[RateLimiter] Blobs 不可用,降级到内存限流:', error.message);
|
||
return memoryRateLimit(key, windowMs, maxRequests);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查是否被限流
|
||
* @param {string} identifier - 限流标识(通常是 IP)
|
||
* @param {number} windowMs - 时间窗口(毫秒),默认 5 分钟
|
||
* @param {number} maxRequests - 窗口内最大请求数,默认 15 次
|
||
* @returns {Promise<boolean>} 是否被限流
|
||
*/
|
||
async function isRateLimited(identifier, windowMs = 5 * 60 * 1000, maxRequests = 15) {
|
||
const key = `ratelimit:${identifier}`;
|
||
return blobsRateLimit(key, windowMs, maxRequests);
|
||
}
|
||
|
||
module.exports = { isRateLimited };
|