您的位置:新葡亰496net > 新葡亰官网 > 新葡亰496net:并不安全

新葡亰496net:并不安全

发布时间:2019-11-16 08:37编辑:新葡亰官网浏览(175)

    别天真了,第三方 CSS 并不安全

    2018/03/07 · CSS · 安全

    原文出处: Jake   译文出处:[众成翻译

    • KING]()   

    最近一段时间,关于 通过 CSS 创建 “keylogger”(键盘记录器) 的讨论很多。

    有些人呼吁浏览器厂商去“修复”它。有些人则深入研究,表示它仅能影响通过类 React 框架建立的网站,并指责 React。而真正的问题却在于认为第三方内容是“安全”的。

    预加载图片是提高用户体验的一个很好方法。图片预先加载到浏览器中,访问者便可顺利地在你的网站上冲浪,并享受到极快的加载速度。这对图片画 廊及图片占据很大比例的网站来说十分有利,它保证了图片快速、无缝地发布,也可帮助用户在浏览你网站内容时获得更好的用户体验。本文将分享三个不同的预加 载技术,来增强网站的性能与可用性。 

    第三方图片

    <img src=";

    1
    <img src="https://example.com/kitten.jpg">

    如果我将上述代码引入我的文件中,即表示信任 example.com。对方可能会删除资源,给我一个 404,导致网站不完整,从而破坏这种信任关系。或者,他们可能会用其他非预期的数据来替代小猫图片的数据。

    但是,图片的影响仅限于元素本身的内容区域。我可以向用户解释并希望用户相信,“此处的内容来自 example.com,如果它有误,则是原站点的问题,并不是本站造成的”。但这个问题肯定不会影响到密码输入框等内容。

    方法一:用CSS和JavaScript实现预加载

    实现预加载图片有很多方法,包括使用CSS、JavaScript及两者的各种组合。这些技术可根据不同设计场景设计出相应的解决方案,十分高效。

    单纯使用CSS,可容易、高效地预加载图片,代码如下:

    Html代码  

    1. #preload-01 { background: url() no-repeat -9999px -9999px; }  
    2. #preload-02 { background: url() no-repeat -9999px -9999px; }  
    3. #preload-03 { background: url() no-repeat -9999px -9999px; }   

    将这三个ID选择器应用到(X)HTML元素中,我们便可通过CSS的background属性将图片预加载到屏幕外的背景上。只要这些图片的路径 保持不变,当它们在Web页面的其他地方被调用时,浏览器就会在渲染过程中使用预加载(缓存)的图片。简单、高效,不需要任何JavaScript。 

    该方法虽然高效,但仍有改进余地。使用该法加载的图片会同页面的其他内容一起加载,增加了页面的整体加载时间。为了解决这个问题,我们增加了一些JavaScript代码,来推迟预加载的时间,直到页面加载完毕。代码如下:

    Js代码  

    1. // better image preloading  
    2. function preloader() {  
    3.     if (document.getElementById) {  
    4.         document.getElementById("preload-01").style.background = "url() no-repeat -9999px -9999px";  
    5.         document.getElementById("preload-02").style.background = "url() no-repeat -9999px -9999px";  
    6.         document.getElementById("preload-03").style.background = "url() no-repeat -9999px -9999px";  
    7.     }  
    8. }  
    9. function addLoadEvent(func) {  
    10.     var oldonload = window.onload;  
    11.     if (typeof window.onload != 'function') {  
    12.         window.onload = func;  
    13.     } else {  
    14.         window.onload = function() {  
    15.             if (oldonload) {  
    16.                 oldonload();  
    17.             }  
    18.             func();  
    19. 新葡亰496net,        }  
    20.     }  
    21. }  
    22. addLoadEvent(preloader);  

    在该脚本的第一部分,我们获取使用类选择器的元素,并为其设置了background属性,以预加载不同的图片。

    该脚本的第二部分,我们使用addLoadEvent()函数来延迟preloader()函数的加载时间,直到页面加载完毕。

    如果JavaScript无法在用户的浏览器中正常运行,会发生什么?很简单,图片不会被预加载,当页面调用图片时,正常显示即可。 

    第三方脚本

    JavaScript

    <script src=";

    1
    <script src="https://example.com/script.js"></script>

    与图片相比,第三方脚本则有更多的控制权。如果我将上述代码引入我的文件中,则表示我赋予了 example.com 完全控制我的网站的权限。该脚本能:

    • 读取/修改页面内容。
    • 监听用户的所有交互。
    • 运行耗费大量计算资源的代码(如 cryptocoin 挖矿程序)。
    • 通过向本站发请求,这样能附带用户的 cookie,转发响应。(译注:盗取用户的 cookie 及其他数据)
    • 读取/修改本地存储。
    • ……可以做任何对方想做的事情。

    “本地存储”非常重要。如果脚本通过 IndexedDB 或缓存 API 发起攻击,则即使在删除脚本后,攻击仍可能在整个站点内继续存在。

    如果你引入了其他站点的脚本,则必须绝对相信对方及对方的防护能力。

    如果你遭到恶意脚本的攻击,则可设置 Clear-Site-Data header(清空站点数据响应头) 清除站点所有数据。

    方法二:仅使用JavaScript实现预加载

    上述方法有时确实很高效,但我们逐渐发现它在实际实现过程中会耗费太多时间。相反,我更喜欢使用纯JavaScript来实现图片的预加载。下面将提供两种这样的预加载方法,它们可以很漂亮地工作于所有现代浏览器之上。 

    JavaScript代码段1

    只需简单编辑、加载所需要图片的路径与名称即可,很容易实现:

    Html代码  

    1. <div>  
    2.     <script type="text/javascript">  
    3.         <!--//--><![CDATA[//><!-- 
    4.         var images = new Array() 
    5.             function preload() { 
    6.                 for (i = 0; i < preload.arguments.length; i ) { 
    7.                     images[i] = new Image() 
    8.                     images[i].src = preload.arguments[i] 
    9.                 } 
    10.             } 
    11.             preload( 
    12.                 "", 
    13.                 "", 
    14.                 "" 
    15.             ) 
    16.         //--><!]]>  
    17.      </script>  
    18. </div>  

    该方法尤其适用预加载大量的图片。我的画廊网站使用该技术,预加载图片数量达50多张。将该脚本应用到登录页面,只要用户输入登录帐号,大部分画廊图片将被预加载。 

    JavaScript代码段2

    该方法与上面的方法类似,也可以预加载任意数量的图片。将下面的脚本添加入任何web页中,根据程序指令进行编辑即可。

    Html代码  

    1. <div>  
    2.     <script type="text/javascript">  
    3.         <!--//--><![CDATA[//><!--  
    4.         if (document.images) { 
    5.                 img1 = new Image(); 
    6.                 img2 = new Image(); 
    7.                 img3 = new Image(); 
    8.                 img1.src = ""; 
    9.                 img2.src = ""; 
    10.                 img3.src = ""; 
    11.             } 
    12.         //--><!]]>  
    13.     </script>  
    14. </div>  

     

    正如所看见,每加载一个图片都需要创建一个变量,如“img1 = new Image();”,及图片源地址声明,如“img3.src =“../path/to/image-003.gif”;”。参考该模式,你可根据需要加载任意多的图片。

    我们又对该方法进行了改进。将该脚本封装入一个函数中,并使用 addLoadEvent(),延迟预加载时间,直到页面加载完毕。

    Js代码  

    1. function preloader() {  
    2.     if (document.images) {  
    3.         var img1 = new Image();  
    4.         var img2 = new Image();  
    5.         var img3 = new Image();  
    6.         img1.src = "";  
    7.         img2.src = "";  
    8.         img3.src = "";  
    9.     }  
    10. }  
    11. function addLoadEvent(func) {  
    12.     var oldonload = window.onload;  
    13.     if (typeof window.onload != 'function') {  
    14.         window.onload = func;  
    15.     } else {  
    16.         window.onload = function() {  
    17.             if (oldonload) {  
    18.                 oldonload();  
    19.             }  
    20.             func();  
    21.         }  
    22.     }  
    23. }  
    24. addLoadEvent(preloader);   

    第三方CSS

    JavaScript

    <link rel="stylesheet" href=";

    1
    <link rel="stylesheet" href="https://example.com/style.css">

    相比图片,CSS 在能力上更接近脚本。像脚本一样,它适用于整个页面。它可以:

    • 删除/添加/修改页面内容。
    • 根据页面内容发起请求。
    • 可响应多种用户交互。

    虽然 CSS 不能修改本地存储,也不能通过 CSS 运行 cryptocoin 挖矿程序(也许是可能的,只是我不知道而已),但恶意 CSS 代码仍然能造成很大的损失。

    方法三:使用Ajax实现预加载

    上面所给出的方法似乎不够酷,那现在来看一个使用Ajax实现图片预加载的方法。该方法利用DOM,不仅仅预加载图片,还会预加载CSS、 JavaScript等相关的东西。使用Ajax,比直接使用JavaScript,优越之处在于JavaScript和CSS的加载会影响到当前页面,而Ajax不会,使用Ajax该方法简洁、高效。

    Js代码  

    1. 新葡亰496net:并不安全。window.onload = function() {  
    2.     setTimeout(function() {  
    3.         // XHR to request a JS and a CSS  
    4.         var xhr = new XMLHttpRequest();  
    5.         xhr.open('GET', '');  
    6.         xhr.send('');  
    7.         xhr = new XMLHttpRequest();  
    8.         xhr.open('GET', '');  
    9. 新葡亰496net:并不安全。        xhr.send('');  
    10.         // preload image  
    11.         new Image().src = "";  
    12.     }, 1000);  
    13. };   

    上面代码预加载了“preload.js”、“preload.css”和“preload.png”。1000毫秒的超时是为了防止脚本挂起,而导致正常页面出现功能问题。 

    下面,我们看看如何用JavaScript来实现该加载过程:

    Js代码  

    1. window.onload = function() {  
    2.     setTimeout(function() {  
    3.         // reference to <head>  
    4.         var head = document.getElementsByTagName('head')[0];  
    5.         // a new CSS  
    6.         var css = document.createElement('link');  
    7.         css.type = "text/css";  
    8.         css.rel  = "stylesheet";  
    9.         css.href = "";  
    10.         // a new JS  
    11.         var js  = document.createElement("script");  
    12.         js.type = "text/javascript";  
    13.         js.src  = "";  
    14.         // preload JS and CSS  
    15.         head.appendChild(css);  
    16.         head.appendChild(js);  
    17.         // preload image  
    18.         new Image().src = "";  
    19.     }, 1000);  
    20. };   

    这里,我们通过DOM创建三个元素来实现三个文件的预加载。正如上面提到的那样,使用Ajax,加载文件不会应用到加载页面上。从这点上看,Ajax方法优越于JavaScript。

    键盘记录器

    从引起广泛关注的代码开始讲起:

    input[type="password"][value$="p"] { background: url('/password?p'); }

    1
    2
    3
    input[type="password"][value$="p"] {
      background: url('/password?p');
    }

    如果输入框的 value 属性值以 p 结尾,上述代码将会向 /password?p 发起请求。每个字符都可触发这个操作,通过它能获取到很多数据。

    默认情况下,浏览器不会将用户输入的值存储在 value 属性中,因此这种攻击需要依赖某些能同步这些值的东西,如 React。

    要应对这个问题,React 可用另一种同步密码字段的方式,或浏览器可限制那些能匹配密码字段属性的选择器。但是,这仅仅是一种虚假的安全。你只解决了在特殊情况下的该问题,而其他情况依旧。

    如果 React 改为使用 data-value 属性,则该应对方法无效。如果网站将输入框更改为 type="text",以便用户可以看到他们正在输入的内容,则该应对方法无效。如果网站创建了一个 <better-password-input> 组件并暴露 value 作为属性,则该应对方法无效。

    此外,还有很多其他的基于 CSS 的攻击方式:

    消失的内容

    body { display: none; } html::after { content: 'HTTP 500 Server Error'; }

    1
    2
    3
    4
    5
    6
    7
    body {
      display: none;
    }
     
    html::after {
      content: 'HTTP 500 Server Error';
    }

    以上是一个极端的例子,但想象一下,如果第三方仅对某一小部分用户这样做。不但你很难调试,还会失去用户的信任。

    更狡猾的方式如偶尔删除“购买”按钮,或重排内容段落。

    添加内容

    .price-value::before { content: '1'; }

    1
    2
    3
    .price-value::before {
      content: '1';
    }

    哦,价格被标高了。

    移动内容

    .delete-everything-button { opacity: 0; position: absolute; top: 500px; left: 300px; }

    1
    2
    3
    4
    5
    6
    .delete-everything-button {
      opacity: 0;
      position: absolute;
      top: 500px;
      left: 300px;
    }

    上面的按钮能做一些重要的操作,设置其为不可见,然后放在用户可能点击的地方。

    值得庆幸的是,如果按钮的操作确实非常重要,网站可能会先显示确认对话框。但也不是不可绕过,只需使用更多的 CSS 来欺骗用户点击 “确定” 按钮而不是“取消”按钮即可。

    假设浏览器确实采用上面的应对方法解决“键盘记录器”的问题。攻击者只需在页面上找到一个非密码文本输入框(可能是搜索输入框)并将其盖在密码输入框上即可。然后他们的攻击就又可用了。

    读取属性

    其实,你需要担心的不仅仅是密码输入框。你可能在属性中保存着其他的隐藏内容:

    JavaScript

    <input type="hidden" name="csrf" value="1687594325"> <img src="/avatars/samanthasmith83.jpg"> <iframe src="//cool-maps-service/show?st-pancras-london"></iframe> <img src="/gender-icons/female.png"> <div></div>

    1
    2
    3
    4
    5
    <input type="hidden" name="csrf" value="1687594325">
    <img src="/avatars/samanthasmith83.jpg">
    <iframe src="//cool-maps-service/show?st-pancras-london"></iframe>
    <img src="/gender-icons/female.png">
    <div></div>

    所有这些都可以通过 CSS 选择器获取,且能发出请求。

    监听交互

    JavaScript

    .login-button:hover { background: url('/login-button-hover'); } .login-button:active { background: url('/login-button-active'); }

    1
    2
    3
    4
    5
    6
    7
    .login-button:hover {
      background: url('/login-button-hover');
    }
     
    .login-button:active {
      background: url('/login-button-active');
    }

    可将 hover 和 active 状态发送到服务器。通过适当的 CSS,你就能获取到用户意图。

    读取文本

    JavaScript

    @font-face { font-family: blah; src: url('/page-contains-q') format('woff'); unicode-range: U 85; } html { font-family: blah, sans-serif; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @font-face {
      font-family: blah;
      src: url('/page-contains-q') format('woff');
      unicode-range: U 85;
    }
     
    html {
      font-family: blah, sans-serif;
    }

    在这种情况下,如果页面内有 q 字符,则会发送请求。你可以为不同的字符,并针对特定的元素,创建大量不同的字体。字体也可以包含 ligature(连字),所以你可以在开始检测字符序列。你甚至可以通过 将字体技巧与滚动条检测结合起来 来推断内容。

    译注:关于 ligature(连字), 可查看 Wikipedia Typographic Ligature

    第三方内容不安全

    这些只是我所知道的一些技巧,我相信还有更多。

    第三方内容在其沙箱区域内具有强大的能力。一张图片或沙盒化的 iframe 仅在一个小范围内的沙箱中,但脚本和样式的范围却是你的页面,甚至是整个站点。

    如果你担心恶意用户诱使你的网站加载第三方资源,可以通过 CSP 用作防护手段,其可以限制加载图片,脚本和样式的来源。

    你还可以使用 Subresource Integrity(子资源完整性 ) 来确保脚本/样式的内容匹配特定的 hash,否则将不加载。感谢 Hacker News上的Piskvorrr 提醒我!

    如果你想了解更多如上述的 hack 技巧,包括滚动条技巧,更多信息请参阅 Mathias Bynens’ talk from 2014,Mike West’s talk from 2013,或 Mario Heiderich et al.’s paper from 2012(PDF)。是的,这不是什么新东西。

    1 赞 1 收藏 评论

    新葡亰496net 1

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:新葡亰496net:并不安全

    关键词: