血淋淋的事实告诉你:你为什么不应该在JS文件中保存敏感信息
在JavaScript文件中存储敏感数据,不仅是一种错误的实践方式,而且还是一种非常危险的行为,长期以来大家都知道这一点。

而原因也非常简单,我们可以假设你为你的用户动态生成了一个包含API密钥的JavaScript文件:
apiCall= function(type, api_key, data) { ... }
var api_key = '1391f6bd2f6fe8dcafb847e0615e5b29'
var profileInfo = apiCall('getProfile', api_key, 'all')
跟上述例子一样,每当你在全局范围创建一个变量,意味着网站中任何一部分代码都可以访问到这个变量,包括你托管的其他脚本在内。
为什么这样做很明显是不安全的?
为什么开发人员不应该在JavaScript文件中嵌入敏感信息呢?原因有很多,对于经验不丰富的开发人员来说,通过JavaScript文件来传递数据是一种非常简单的方法,因为你可以将数据在服务器端生成和存储,然后将它们传递给客户端代码,而且这样还可以节省一部分发送给服务器端的请求。但是,这种时候我们通常会忽略的一个因素就是浏览器的扩展插件,有的时候为了使用相同的窗口对象,它们有的时候需要直接在DOM中注入script标签,因为仅仅依靠内容脚本可能无法实现预期的功能。
有没有办法保护变量的安全?
我们之前已经讨论了全局范围了,对于浏览器中的JavaScript来说,一个全局变量对于窗口对象来说是非常有用的。但是在ECMA Script5中,还有一种额外的范围,也就是函数范围。这也就意味着,如果我们使用var关键字在一个函数内部声明了一个变量,它就不是全局变量了。而在ECMA Script 6中又引入了另一种范围,即块范围,这个范围内的变量使用const和let关键字来声明。
这两种关键字可以用来声明块范围中的变量,但是我们无法修改const变量的值、如果我们没有用这些关键词来声明变量,或者说我们在函数外部使用了var变量,我们就相当于创建了一个全局变量,而这种情况并不是我们经常想要出现的。
“use strict”
为了防止你不小心创建了全局变量,其中一种有效方法就是激活限制模式,大家可以在一个文件或函数的起始位置添加字符串“use strict”来实现这个功能。接下来,如果你之前没有声明这个变量的话,你将无法使用这个变量。
"use strict";
var test1 = 'arka' // 有效
test2= 'kapı' // 引用错误
我们可以在IIFE(立即调用的函数表达式)中使用这种技术,IIFE可以用来创建一个函数范围,但是它们会立即执行函数主体,比如说:
(function(){
"use strict";
//在函数范围内声明变量
var privateVar = 'Secret value';
})()
console.log(privateVar) // 引用错误
可能乍看过去这会是一种创建变量的有效方式(其内容无法在范围外读取),但其实不然。虽然IIFE是一种防止全局命名空间被干扰的有效方式,但是它们并不能真正保护你的数据内容。
从私有变量中读取敏感数据
实际上,我们几乎无法保证私有变量中的内容真正的是“私有”的。原因有非常多,我们接下来会对其中的部分进行测试,虽然不够完整,但也足够证明给大家看,为什么我们不能在JavaScript文件中存储敏感数据。
重写原生函数
在下面的例子中,我们将使用一个api密钥来向服务器端发送请求。因此,我们需要通过网络并以明文数据的形式发送这个密钥,而且现在在JavaScript中也没有多少其他可选择的方法。假设我们的代码使用了fetch()函数:
window.fetch= (url, options) => {
console.log(`URL: ${url}, data:${options.body}`);
};
//EXTERNAL SCRIPT START
(function(){
"use strict";
var api_key ="1391f6bd2f6fe8dcafb847e0615e5b29"
fetch('/api/v1/getusers', {
method: "POST",
body: "api_key=" + api_key
});
})()
//EXTERNAL SCRIPT END
你可以看到,我们可以直接重写fetch()函数,然后窃取API密钥。唯一的前提就是我们需要在我们的脚本块后include一个外部脚本。在这个例子中,我们只是在控制台console.log出来了这个API密钥,但实际操作中我们还需要将其发送到我们的服务器中。
定义Setter和Getter
私有变量中可能不仅包含字符串,还有可能包含对象或数组。对象可以有不同的属性,在多数情况下,你可以直接设置和读取它们的值,但是JavaScript还支持很多其他有意思的功能。比如说,你可以在一个对象的属性被设置或被访问的时候执行另一个函数,这里可以使用__defineSetter__和__defineGetter__函数来实现。如果我们在对象构造函数的原型中使用__defineSetter__函数,我们就可以输出分配给目标对象属性的所有值。
Object.prototype.__defineSetter__('api_key',function(value){
console.log(value);
return this._api_key = value;
});
Object.prototype.__defineGetter__('api_key',function(){
return this._api_key;
});
//EXTERNAL SCRIPT START
(function(){
"use strict"
let options = {}
options.api_key ="1391f6bd2f6fe8dcafb847e0615e5b29"
options.name = "Alice"
options.endpoint ="get_user_data"
anotherAPICall(options);
})()
//EXTERNAL SCRIPT END
如果分配给对象属性的是一个API密钥,那我们就可以直接在setter中访问它了。另一方面,getter也可以确保我们的后续代码能够正确执行。
自定义枚举器
数组肯定是不能忽略的一个因素,如果代码中使用了for循环来遍历数组值,我们就可以在数组构造器原型中定义一个自定义的枚举器,这样不仅可以允许我们访问数组中的内容,而且也不会影响原生函数的功能。
Array.prototype[Symbol.iterator]= function() {
let arr = this;
let index = 0;
console.log(arr)
return {
next: function() {
return {
value: arr[index++],
done: index > arr.length
}
}
}
};
//EXTERNAL SCRIPT START
(function(){
let secretArray = ["this","contains", "an", "API", "key"];
for (let element of secretArray) {
doSomething(element);
}
})()
//EXTERNAL SCRIPT END
后话
除了本文所介绍的方法之外,攻击者还有很多从JavaScript文件中窃取敏感信息的方法。有的情况下,使用IIFE、限制模式和在函数/块范围声明变量都不一定能保证你的安全。因此,我建议大家可以从服务器端动态获取敏感数据,而不是直接在JavaScript文件中存储敏感数据。这样不仅更加安全,而且还易于维护,何乐而不为?
血淋淋的事实告诉你:你为什么不应该在JS文件中保存敏感信息的更多相关文章
- extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行!”
extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行!”
- schema文件中cube的事实表使用视图方法
在cube中可以使用查询结果或者视图来当做事实表,其中view的alias相当于表名,这个要和同一个cube中的level的表名对应,代码如下: <Cube name="YHZXZLF ...
- 信息收集&Fuzz
本文译自https://0xjoyghosh.medium.com/information-gathering-scanning-for-sensitive-information-reloaded- ...
- Asp.net操作cookie大全
实例代码: 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 3 ...
- PHP程序的常见漏洞攻击分析
综述:PHP程序也不是固若金汤,随着PHP的广泛运用,一些黑客们也在无时不想找PHP的麻烦,通过PHP程序漏洞进行攻击就是其中一种.在节,我们将从全局变量,远程文件,文件上载,库文件,Session文 ...
- 程序员级别鉴定书(.NET面试问答集锦)
作为一个.NET程序员,应该知道的不仅仅是拖拽一个控件到设计时窗口中.就像一个赛车手,一定要了解他的爱车 – 能做什么不能做什么. 本文参考Scott Hanselman给出的.NET问题列表,整理如 ...
- 关于allow_url_fopen的设置与服务器的安全
allow_url_fopen与安全以及PHP libcurl allow_url_fopen=ON常常会给服务器和管理员带来麻烦,但是经常性(至少我这样认为)的我们需要远程读取某个东西,如果设置al ...
- SQLite事务管理
事务管理对数据库一致性是至关重要的.数据库实现ACID属性以确保一致性.SQLite依赖于本地文件锁和页日志来实现ACID属性.SQLite只支持扁平事务,并不支持事务嵌套和保存点能力. 1.1 事务 ...
- 2.SDL规范文档
01.安全设计Checklist 输入验证 校验跨信任边界传递的不可信数据(策略检查数据合法性,含白名单机制等)格式化字符串时,依然要检验用户输入的合法性,避免可造成系统信息泄露或者拒绝服务 禁止向J ...
随机推荐
- Ionic Js十一:模态
ionicModal 可以遮住用户主界面的内容框. 你可以在你的 index 文件或者是其他文件内嵌入以下代码(里面的代码可以根据你自己的业务场景相应的改变). <script id=" ...
- Web安全漏洞深入分析及其安全编码
摘自:http://blog.nsfocus.net/web-vulnerability-analysis-coding-security/ 超全Web漏洞详解及其对应的安全编码规则,包括:SQL注入 ...
- 一文搞定 Mybatis 的应用
Mybatis 介绍 Mybatis 是一个开源的持久层框架,原来叫 ibatis ,它对 jdbc 操作数据库的过程进行了封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动. ...
- @NamedEntityGraphs --JPA按实体类对象参数中的字段排序问题得解决方法
JPA按实体类对象参数中的字段排序问题得解决方法@Entity @Table(name="complaints") @NamedEntityGraphs({ @NamedEntit ...
- springboot配置多数据源mongodb
参考大佬的文章 https://juejin.im/entry/5ab304dd51882555825241b3
- CF280C Game on Tree 期望
期望多少次操作,我们可以看做是染黑了多少节点 那么,我们可以用期望的线性性质,求出每个节点被染黑的概率之和(权值为$1$) 一个节点$u$被染黑仅跟祖先有关 我们把$u$到祖先的链抽出来 只要选取链上 ...
- 【BZOJ】3195: [Jxoi2012]奇怪的道路【状压/奇偶性】【思路】
3195: [Jxoi2012]奇怪的道路 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 802 Solved: 529[Submit][Statu ...
- 20172333 2017-2018-2 《Java程序设计》第6周学习总结
20172333 2017-2018-2 <Java程序设计>第6周学习总结 教材学习内容 1.数组的基本用法,如数组的定义:int[该数组类型] name = new int[X]X为数 ...
- python开发_csv(Comma Separated Values)_逗号分隔值_常用导入导出格式_完整版_博主推荐
## 最近出了一趟差,是从20号去的,今天回来...# 就把最近学习的python内容给大家分享一下...#''' 在python中,CSV(Comma Separated Values),从字面上面 ...
- MVC之Global.asax解析
大家看到上面的代码了,Application_Start大家都知道这是应用程序启动入口. AreaRegistration.RegisterAllAreas是什么呢? 我们先看看微软官方的注解: 我们 ...