血淋淋的事实告诉你:你为什么不应该在JS文件中保存敏感信息
字数 771 2025-08-18 11:37:38
为什么不应该在JavaScript文件中保存敏感信息 - 安全开发指南
核心问题概述
在JavaScript文件中存储敏感数据是一种极其危险的开发实践,主要原因包括:
- 客户端JavaScript代码对所有用户完全可见
- 浏览器扩展可以注入脚本访问全局变量
- 攻击者可以重写原生函数窃取数据
- 无法真正保护变量不被外部访问
常见错误示例
// 错误示范:在全局作用域暴露API密钥
var api_key = '1391f6bd2f6fe8dcafb847e0615e5b29';
var profileInfo = apiCall('getProfile', api_key, 'all');
JavaScript作用域的限制
1. 作用域类型
- 全局作用域:使用
var在函数外部声明,对整个window对象可见 - 函数作用域:使用
var在函数内部声明(ES5) - 块级作用域:使用
let和const声明(ES6)
2. 严格模式("use strict")
"use strict";
var test1 = 'arka'; // 有效
test2 = 'kapı'; // 引用错误:变量未声明
严格模式可以防止意外创建全局变量,但不能真正保护敏感数据
看似安全的方案及其缺陷
立即调用的函数表达式(IIFE)
(function(){
"use strict";
var privateVar = 'Secret value';
})();
console.log(privateVar); // 引用错误
问题:虽然IIFE可以防止全局命名空间污染,但无法阻止以下攻击:
攻击者窃取数据的多种方式
1. 重写原生函数
// 攻击者可以重写fetch函数窃取API密钥
window.fetch = (url, options) => {
console.log(`URL: ${url}, data:${options.body}`);
// 实际攻击中会将数据发送到攻击者服务器
};
// 正常业务代码
(function(){
"use strict";
var api_key ="1391f6bd2f6fe8dcafb847e0615e5b29";
fetch('/api/v1/getusers', {
method: "POST",
body: "api_key=" + api_key
});
})();
2. 使用Setter和Getter
// 攻击者定义Setter捕获属性赋值
Object.prototype.__defineSetter__('api_key',function(value){
console.log(value); // 捕获API密钥
return this._api_key = value;
});
// 正常业务代码
(function(){
"use strict";
let options = {};
options.api_key = "1391f6bd2f6fe8dcafb847e0615e5b29";
// ...
})();
3. 自定义枚举器捕获数组数据
// 攻击者重写数组迭代器
Array.prototype[Symbol.iterator] = function() {
let arr = this;
console.log(arr); // 输出整个数组内容
// ...标准迭代器实现
};
// 正常业务代码
(function(){
let secretArray = ["this","contains", "an", "API", "key"];
for (let element of secretArray) {
// ...
}
})();
安全实践建议
-
永远不要在客户端代码中硬编码敏感信息:
- API密钥
- 数据库凭证
- 加密密钥
- 用户隐私数据
-
正确的敏感数据处理方式:
- 从服务器端动态获取敏感数据
- 使用安全的会话管理机制
- 实现适当的API访问控制
-
如果必须使用客户端密钥:
- 限制密钥的使用范围和权限
- 设置严格的CORS策略
- 实现密钥轮换机制
- 监控异常使用情况
总结
在JavaScript中保护敏感数据的根本原则是:任何发送到客户端的数据都不再是秘密。开发者必须:
- 认识到客户端环境是完全不可信的
- 将所有敏感数据处理保留在服务器端
- 使用安全的API设计模式
- 定期审计前端代码中的敏感信息泄露
通过服务器端动态提供必要凭证,不仅更安全,也更易于维护和更新。