PageSourceSearch

http://vortaro.cn/js/security.js?v=20260509

js vortaro.cn collected 2026-10-06 13:15:11 UTC 7,414 bytes, 225 lines download raw bytes

1/**
2 * 安全防护模块
3 * 文件: js/security.js
4 * 功能:屏蔽开发者工具、禁用快捷键、防止代码注入
5 * 修复:针对 Safari 浏览器优化,减少误报
6 */
7
8(function() {
9    'use strict';
10    
11    if (window._securityLoaded) return;
12    window._securityLoaded = true;
13    
14    console.log('安全防护模块已加载');
15    
16    // ==================== 检测浏览器类型 ====================
17    
18    var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
19    var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
20    var isMacSafari = isSafari && !isMobile;
21    
22    if (isSafari) {
23        console.log('检测到 Safari 浏览器,使用兼容模式');
24    }
25    
26    // ==================== 快捷键屏蔽 ====================
27    
28    document.addEventListener('keydown', function(e) {
29        var key = e.key;
30        var ctrl = e.ctrlKey || e.metaKey;
31        var shift = e.shiftKey;
32        
33        // F12
34        if (key === 'F12') {
35            e.preventDefault();
36            return false;
37        }
38        
39        // Ctrl+Shift+I / J / C (开发者工具)
40        if (ctrl && shift && (key === 'I' || key === 'i' || key === 'J' || key === 'j' || key === 'C' || key === 'c')) {
41            e.preventDefault();
42            return false;
43        }
44        
45        // Ctrl+U (查看源代码)
46        if (ctrl && (key === 'U' || key === 'u')) {
47            e.preventDefault();
48            return false;
49        }
50        
51        // Ctrl+S (保存页面)
52        if (ctrl && (key === 'S' || key === 's')) {
53            e.preventDefault();
54            return false;
55        }
56        
57        // Safari 特定:Cmd+Option+I (开发者工具)
58        if (isSafari && ctrl && shift && (key === 'I' || key === 'i')) {
59            e.preventDefault();
60            return false;
61        }
62        
63        // Safari 特定:Cmd+Option+C (控制台)
64        if (isSafari && ctrl && shift && (key === 'C' || key === 'c')) {
65            e.preventDefault();
66            return false;
67        }
68    });
69    
70    // ==================== 右键菜单屏蔽 ====================
71    
72    document.addEventListener('contextmenu', function(e) {
73        var tag = e.target.tagName.toLowerCase();
74        var isEditable = tag === 'input' || tag === 'textarea' || tag === 'a';
75        if (!isEditable) {
76            e.preventDefault();
77            return false;
78        }
79    });
80    
81    // ==================== 开发者工具检测(Safari 优化版)====================
82    
83    var devToolsOpen = false;
84    var detectionEnabled = true;
85    var detectionCount = 0;
86    
87    // 方法1:通过窗口尺寸差异检测(针对 Safari 优化)
88    function detectBySize() {
89        // 手机端完全跳过尺寸检测
90        if (isMobile) {
91            return false;
92        }
93        
94        // Safari 桌面版特殊处理
95        if (isMacSafari) {
96            // Safari 的 outer/inner 差异本来就比较大,提高阈值
97            var widthDiff = Math.abs(window.outerWidth - window.innerWidth);
98            var heightDiff = Math.abs(window.outerHeight - window.innerHeight);
99            
100            // Safari 正常状态下差异可能达到 50-80px,阈值设更高
101            var threshold = 120;
102            return (widthDiff > threshold || heightDiff > threshold);
103        }
104        
105        // 其他桌面浏览器
106        if (window.outerWidth < 600 || window.outerHeight < 400) {
107            return false;
108        }
109        
110        var widthDiff = Math.abs(window.outerWidth - window.innerWidth);
111        var heightDiff = Math.abs(window.outerHeight - window.innerHeight);
112        
113        var threshold = 80;
114        return (widthDiff > threshold || heightDiff > threshold);
115    }
116    
117    // 方法2:通过 debugger 执行时间检测(Safari 兼容)
118    function detectByDebugger() {
119        try {
120            var start = Date.now();
121            // 使用更温和的检测方式,避免 Safari 卡顿
122            var test = function() { return; };
123            debugger;
124            var end = Date.now();
125            return (end - start > 150);
126        } catch(e) {
127            return false;
128        }
129    }
130    
131    // 方法3:通过 console 对象检测(Safari 兼容)
132    function detectByConsole() {
133        try {
134            // 检测 console 是否被重写
135            var hasConsole = typeof console !== 'undefined';
136            var hasLog = typeof console.log === 'function';
137            return !hasConsole || !hasLog;
138        } catch(e) {
139            return false;
140        }
141    }
142    
143    // 显示处理(不显示弹窗,避免打扰用户)
144    function handleDevToolsDetected() {
145        if (!devToolsOpen) {
146            devToolsOpen = true;
147            // 静默清空控制台,不显示警告弹窗
148            console.clear();
149        }
150    }
151    
152    // 重置状态
153    function resetDetection() {
154        devToolsOpen = false;
155        detectionCount = 0;
156    }
157    
158    // 综合检测(降低灵敏度)
159    function detectDevTools() {
160        if (!detectionEnabled) return;
161        
162        var isOpen = false;
163        
164        // Safari 使用更保守的检测策略
165        if (isSafari) {
166            // Safari 只使用 debugger 检测,不使用尺寸检测(误报太多)
167            isOpen = detectByDebugger();
168        } else {
169            // 其他浏览器综合检测
170            isOpen = detectBySize() || detectByDebugger();
171        }
172        
173        // 需要连续多次检测到才确认(减少误报)
174        if (isOpen) {
175            detectionCount++;
176            if (detectionCount >= 2) {
177                handleDevToolsDetected();
178            }
179        } else {
180            detectionCount = Math.max(0, detectionCount - 1);
181            if (detectionCount === 0 && devToolsOpen) {
182                resetDetection();
183            }
184        }
185    }
186    
187    // 降低检测频率,从1秒改为5秒
188    var checkInterval = setInterval(detectDevTools, 5000);
189    
190    // 页面失去焦点时暂停检测
191    window.addEventListener('blur', function() {
192        detectionEnabled = false;
193        detectionCount = 0;
194    });
195    
196    window.addEventListener('focus', function() {
197        detectionEnabled = true;
198        setTimeout(detectDevTools, 1000);
199    });
200    
201    // ==================== 可选:禁用某些调试技巧 ====================
202    
203    // 保护 console.clear 方法(Safari 兼容)
204    if (typeof console !== 'undefined' && console.clear) {
205        var originalClear = console.clear;
206        var clearDisabled = false;
207        
208        console.clear = function() {
209            if (!clearDisabled) {
210                // 允许清空,但不做额外处理
211                originalClear.apply(console, arguments);
212            }
213        };
214    }
215    
216    // ==================== 页面加载完成后的额外处理 ====================
217    
218    window.addEventListener('load', function() {
219        // 延迟启动检测,避免页面加载时误判
220        setTimeout(function() {
221            detectionEnabled = true;
222        }, 2000);
223    });
224    
225})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.