那些对安全有用的 JavaScript 特性
优化方向:统一标题层级、强化代码语法高亮、突出安全关键信息、规整表格格式、精简冗余表述,提升整体可读性与专业性。
一、JavaScript LocalStorage 与安全
LocalStorage 是 HTML5 引入的本地存储方案,属于 Web Storage API 核心组成部分,用于在浏览器端持久化存储数据,减少服务器交互频次,是前端本地存储的常用选择。
1.1 核心特点
- 持久化存储:数据不会因浏览器关闭、页面刷新丢失,需手动或通过代码清除,否则永久保存在设备中。
- 域名隔离:数据与当前域名绑定,同一域名下页面可共享数据,不同域名(含子域名)无法跨域访问,这是其安全核心保障。
- 存储容量:默认约 5MB(浏览器间略有差异),远大于 Cookie 的 4KB 限制。
- 仅客户端存储:数据完全存于本地,不会随 HTTP 请求自动发送至服务器,与 Cookie 形成核心区别。
- 字符串存储限制:仅支持字符串类型,存储对象、数组需通过
JSON.stringify 转换为字符串,读取时用 JSON.parse 还原。
1.2 常用方法
LocalStorage 挂载于 window 对象,可通过 window.localStorage 直接访问,常用方法与属性如下表:
| 方法/属性 |
作用 |
示例代码 |
setItem(key, value) |
存储键值对(value 必须为字符串) |
localStorage.setItem('name', '小明') |
getItem(key) |
获取指定 key 对应的 value |
localStorage.getItem('name') // 返回 '小明' |
removeItem(key) |
删除指定 key 的键值对 |
localStorage.removeItem('name') |
clear() |
清空当前域名下所有 LocalStorage 数据 |
localStorage.clear() |
key(index) |
获取指定索引位置的 key 值 |
localStorage.key(0) // 返回第 1 个 key |
length |
获取当前存储的键值对总数 |
localStorage.length // 返回存储数量 |
1.3 安全须知
关键提示:LocalStorage 数据在前端可被篡改且明文显示,仅适合存储非敏感数据(如用户偏好设置),严禁直接存储密码、令牌等核心信息。
敏感场景使用示例(带安全校验)
若需存储认证信息(如用户令牌),需配合编码、校验机制保证完整性,且必须依赖后端二次验证,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const data = await response.json(); const rawId = data.data?.id; const rawAuth = data.data?.Authorization; const rawEmail = data.data?.email;
const encodedId = rawId ? btoa(rawId.toString()) : ''; const encodedAuth = rawAuth ? btoa(rawAuth) : ''; const encodedEmail = rawEmail ? btoa(rawEmail) : '';
const checkStr = `${encodedId}|${encodedAuth}|${encodedEmail}`; const storageChecksum = CryptoJS.MD5(checkStr).toString();
if (rawId) localStorage.setItem('userId', rawId); if (encodedId) localStorage.setItem('encodedUserId', encodedId); if (rawAuth) localStorage.setItem('userToken', rawAuth); if (rawEmail) localStorage.setItem('userEmail', rawEmail); localStorage.setItem('storageChecksum', storageChecksum);
|
请求受保护接口示例
使用 LocalStorage 中存储的令牌发起请求,需处理异常场景(如令牌过期、请求失败):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| try { const token = localStorage.getItem('userToken') || ''; if (!token) throw new Error('未获取到认证信息');
const response = await fetch('http://192.168.87.177:8000/DemoController/get_All_Articles', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Basic ${token}` }, body: JSON.stringify({ page: 1, per_page: 10 }) });
if (!response.ok) throw new Error(`请求失败:${response.status}`); const result = await response.json();
const articles = result.success && result.data?.data?.length ? result.data.data : []; } catch (error) { console.error('请求异常:', error.message); }
|
二、JavaScript 事件与安全交互
合理使用 JS 事件可提升交互安全性,如防重复提交、实时输入验证、资源加载异常监控等。以下按事件类别整理常用类型及安全用途:
| 事件类别 |
事件名称 |
说明 |
| 鼠标事件 |
click |
鼠标单击触发(左键为主),可用于按钮点击(需防重复点击,如添加禁用状态)。 |
|
dblclick |
鼠标双击触发,适用需确认的操作(如文件重命名)。 |
|
contextmenu |
鼠标右键触发,可禁用默认菜单(preventDefault()),防止敏感操作被误触。 |
| 键盘事件 |
keydown |
按下任意键触发,可用于快捷键控制(如 Ctrl+S 保存),需过滤非法键值。 |
|
keyup |
松开任意键触发,适合输入完成后的即时校验(如密码强度检测)。 |
| 表单事件 |
submit |
表单提交时触发(绑定在 <form> 上),需用 preventDefault() 阻止默认提交,先做前端校验。 |
|
input |
表单值实时变化时触发(如输入、粘贴),适合实时验证(如用户名是否已存在)。 |
|
blur |
元素失去焦点时触发,适合必填项确认(如输入框为空时提示)。 |
| 窗口/文档事件 |
DOMContentLoaded |
HTML 解析完成(DOM 树构建完毕)时触发,不等待资源加载,适合初始化安全校验(如检查令牌是否存在)。 |
|
beforeunload |
页面即将卸载时触发(如关闭标签页),可弹出确认框,防止用户误操作丢失数据。 |
|
error |
资源(图片、脚本)加载失败时触发,可用于监控安全异常(如脚本被篡改导致加载失败)。 |
| 触摸事件 |
touchstart/touchend |
手指触摸/离开屏幕时触发,移动端需防误触(如添加点击延迟判断)。 |
2.1 事件安全使用示例(文档初始化)
文档加载完成后执行初始化操作(如输出客户端标识、检查环境安全性),示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>安全事件初始化示例</title> </head> <body> <script> document.addEventListener('DOMContentLoaded', () => { const asciiArt = ` "ad888888b, ad88888ba 888888888888 88888888ba 88 d8" "88 d8" "8b ,8P' 88 "8b 88 a8P Y8a a8P d8" 88 ,8P 88 ,d8P" "Y8aaa8P" ,8P' 88aaaaaa8P' 88 ,adPPYba, ,adPPYb,d8 a8P" ,d8"""8b, d8" 88""""""8b, 88 a8" "8a a8" \`Y88 a8P' d8" "8b ,8P' 88 \`8b 88 8b d8 8b 88 d8" Y8a a8P 888 d8" 88 a8P 88 "8a, ,a8" "8a, ,d88 88888888888 "Y88888P" 888 8P' 88888888P" 88 \`"YbbdP"' \`"YbbdP"Y8 `; const style = ` color: #00f3ff; font-weight: bold; text-shadow: 0 0 2px #00f3ff; font-family: monospace; `; console.log(`%c${asciiArt}`, style);
const storedChecksum = localStorage.getItem('storageChecksum'); if (!storedChecksum) console.warn('未检测到 LocalStorage 校验信息,可能存在数据风险'); }); </script> </body> </html>
|

三、JavaScript 安全相关库
3.1 crypto-js.min.js
crypto-js.min.js 是基于 JavaScript 的加密算法库,支持前端/Node.js 环境,可快速实现数据加密、哈希、签名等安全操作,无需手动编写复杂算法逻辑。
核心信息解析
- 核心功能:覆盖主流安全算法,满足不同场景需求:
- 对称加密:AES、DES、Triple DES(适合本地数据加密)。
- 哈希算法:MD5、SHA-1、SHA-256、SHA-512(适合数据完整性校验)。
- 其他能力:HMAC 消息认证(防篡改)、PBKDF2 密钥派生(生成安全密钥)。
- “min.js”含义:表示代码已压缩,体积小、加载快,适合生产环境;未压缩版(
crypto-js.js)保留注释和清晰结构,用于开发调试。
- 安全用途:
- 敏感数据预处理:用户密码通过哈希(如 SHA-256)处理后再传后端,避免明文传输。
- 本地存储加密:对 LocalStorage 中的敏感信息(如部分用户数据)加密,降低泄露风险。
- 接口签名:生成请求参数的 HMAC 签名,后端验证签名合法性,防止请求被篡改。
安全使用示例
示例 1:结合盐值计算哈希(防彩虹表破解)
从后端获取动态盐值,避免前端硬编码盐值导致的安全风险:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
async function getHashSalt() { try { const response = await fetch('http://192.168.87.177:8000/Index/getsalt', { headers: { 'X-Client-IP': clientIp } }); const data = await response.json(); if (!data.success) throw new Error('盐值获取失败'); return data.data?.salt; } catch (error) { console.error('盐值请求异常:', error.message); throw error; } }
async function calculateHash(text) { const salt = await getHashSalt(); const textWithSalt = text + salt; const integrityHash = CryptoJS.SHA256(textWithSalt).toString(); return { integrityHash, salt }; }
|
示例 2:MD5 加密(适合非核心数据校验)
对非敏感数据(如请求参数)进行 MD5 哈希,用于简单完整性校验:
1 2 3 4 5 6 7 8 9 10 11
| const formData = { username: 'test', timestamp: Date.now() };
const dataStr = JSON.stringify(formData); const dataHash = CryptoJS.MD5(dataStr).toString();
fetch('/api/submit', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...formData, dataHash }) });
|
3.2 async/await 异步安全处理
async/await 是 ES2017 引入的异步语法,简化 fetch 等异步请求的代码逻辑,同时降低异步操作中的安全风险(如回调地狱导致的逻辑混乱、错误未捕获)。
核心概念
- 同步编程:代码按顺序执行,前一个操作未完成时,后续操作阻塞(如长时间请求会导致页面卡死)。
- 异步编程:耗时操作(如网络请求)放入“任务队列”,主线程继续执行其他代码,操作完成后触发回调(
async/await 是回调的语法糖,更易读)。
- 安全价值:通过清晰的代码结构,减少异步操作中的错误遗漏(如未捕获请求异常导致的安全隐患),同时便于添加安全校验逻辑(如请求前检查令牌、响应后验证数据)。
异步安全请求示例
无感处理密钥获取与接口请求,全程捕获异常并处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
async function getApiKey() { try { const response = await fetch('http://192.168.87.177:8000/Index/getApiKey', { headers: { 'Authorization': `Basic ${localStorage.getItem('userToken')}` } }); if (!response.ok) throw new Error(`密钥请求失败:${response.status}`); const data = await response.json(); if (!data.success) throw new Error('密钥返回异常'); return data.data?.apiKey; } catch (error) { console.error('密钥获取异常:', error.message); window.location.href = '/login'; return ''; } }
async function safeRequest(url, params) { const apiKey = await getApiKey(); if (!apiKey) return { success: false, message: '缺少接口密钥' };
try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': apiKey }, body: JSON.stringify(params) }); return await response.json(); } catch (error) { return { success: false, message: `请求异常:${error.message}` }; } }
safeRequest('http://192.168.87.177:8000/Api/getData', { page: 1 }) .then(result => { if (result.success) console.log('请求成功:', result.data); else console.error('请求失败:', result.message); });
|