前端如何防范XSS
跨站脚本攻击, 是一种攻击者向用户的浏览器注入恶意代码脚本的攻击。html
XSS攻击的种类:前端
攻击者输入恶意代码并经过评论或表单提交等方式将其提交到网站, 而网站的后端却对用户的数据不作任何处理进而直接存入数据库, 当其余用户访问该网站而且须要请求网站的评论数据时网站后端直接从数据库中取出带有恶意代码的数据返回给用户页面此时用户就会遭受到攻击, 好比while { alert( 'Attack!' ) } 这样的恶意脚本。node
用户向网站发送get请求某个网页的url, 但用户点开的是带攻击的url相似于:数据库
/* http://xxx?name=<script>alert('233')</script> <-- evil url ctx.render('index.html', { name:name }); --index.html-- <html> <div>${name}</div> </html> */
它是反射型攻击的一种, 此时服务器返回的页面是正常的, 只是页面在执行JS时会将恶意脚本一并执行。后端
/* http://xxx?name=<script>alert('233')</script> <-- evil url --index.js-- var name = getparm('name'); document.querySelector('div').innerHTML = name; */
防止XSS注入的一些方法:浏览器
function htmlEncode(html) { var sub = document.createElement('div'); sub.textContent != null ? sub.textContent = html : sub.innerText = html; var output = sub.innerHTML; sub = null; return output; }
//既然有转义固然也有反转义的方法 function htmlDecode(text) { var sub = document.createElement('div'); sub.innerHTML = text; var output = sub.textContent || sub.innerText; sub = null; return output; }
/* @ escape() @ encodeURI() @ encodeURIComponent() -- 对应的解码方法 -- @ unescape() @ unencodeURI() @ unencodeURIComponent() */
CSP的实质就是白名单制度, 开发者明确告诉客户端, 哪些外部资源能够加载和执行, 等同于提供白名单。它的实现和执行所有由浏览器完成, 开发者只需提供配置, CSP大大加强了网页的安全性。安全
有两种方法能够启用CSP, 一种是经过设置HTTP头信息的Content-Security-Policy 字段, 另一种是经过网页中的<meta> 标签。服务器
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">
CSP经过设置限制选项来提供咱们所需的白名单:框架
/* @ script-src: 外部脚本 @ style-src: 样式表 @ img-src: 图像 @ media-src: 媒体文件 ( 音频和视频 ) @ font-src: 字体文件 @ object-src: 插件 ( 好比Flash ) @ child-src: 框架 @ connect-src: HTTP 链接 ( 经过 XHR、WebSockets、EventSource等 ) ...... */
default-src 用来设置上面每一个选项的默认值:ide
Content-Security-Policy: default-src 'self' // 限制全部的外部资源, 只能从当前域名加载
其余的一些限制:
/* @ block-all-mixed-content:HTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启) @ upgrade-insecure-requests:自动将网页上全部加载外部资源的 HTTP 连接换成 HTTPS 协议 @ sandbox:浏览器行为的限制,好比不能有弹出窗口等 */
使用report-uri 还能够记录XSS注入的行为并报告给指定的url:
Content-Security-Policy: default-src 'self'; ...; report-uri https://retroastro.com;
设置多个值, 用空格分隔:
Content-Security-Policy: script-src 'self' https://host1.com https://host2.com
script-src 中的一些特殊值:
/* @ nonce: 每次HTTP回应给出一个受权token,页面内嵌脚本必须有这个token,才会执行。 @ hash: 列出容许执行的脚本代码的Hash值,页面内嵌脚本的哈希值只有吻合的状况下,才能执行。 */ /* nonce值的例子: Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa' <script nonce=EDNnf03nceIOfn39fn3e9h3sdfa> // some code </script> 设置的nonce值要与内嵌脚本中的nonce相等才会执行代码 */ /* hash值的例子: Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng=' <script> //somecode </script> 服务器给出一个容许执行代码片断的hash值, 对应的代码才会执行, 由于hash值相等。 */
JavaScript的各类宽高属性
网上看到有一篇博客讲的挺好挺全面的, 有须要的能够去看看:
场景:实现bilibili右侧导航栏的切换导航效果。
// HTML <main> <div class="container"> <div class="part">Live</div> <div class="part">Comics</div> <div class="part">Drama</div> <div class="part">Native</div> <div class="part">Music</div> <div class="part">Dance</div> <div class="part">Game</div> <div class="part">Science</div> <div class="part">Life</div> <div class="part">Kichiku</div> <div class="part">Advertise</div> <div class="part">Entertain</div> <div class="part">Fashion</div> <div class="part">TVshows</div> <div class="part">Film</div> <div class="part">Movies</div> </div> </main> <aside class="elevator-module"> <div class="nav-list"> <div class="item active" data-id=0>直播</div> <div class="item" data-id=1>动画</div> <div class="item" data-id=2>番剧</div> <div class="item" data-id="3">国创</div> <div class="item" data-id="4">音乐</div> <div class="item" data-id="5">舞蹈</div> <div class="item" data-id="6">游戏</div> <div class="item" data-id="7">科技</div> <div class="item" data-id="8">生活</div> <div class="item" data-id="9">鬼畜</div> <div class="item" data-id="10">广告</div> <div class="item" data-id="11">娱乐</div> <div class="item" data-id="12">时尚</div> <div class="item" data-id="13">TV剧</div> <div class="item" data-id="14">影视</div> <div class="item" data-id="15">电影</div> </div> <div class="s-line"></div> <div class="back-top"> <div class="circle"></div> </div> </aside>
// CSS .elevator-module{ position:fixed; top:20px; left:50%; margin-left:590px; transition:all .4s ease; z-index:10; } .elevator-module .nav-list{ position:relative; background-color:#f6f9fa; border:1px solid #e5e9ef; overflow:hidden; border-radius:4px; } .elevator-module .nav-list .item{ width:48px; height:32px; line-height:32px; text-align:center; transition:all .3s; cursor:pointer; } .elevator-module .nav-list .item.on, .elevator-module .nav-list .item:hover{ background-color:#00a1d6; color:#fff; } .nav-list .item.active{ color:#fff; background:#00a1d6; } .elevator-module .s-line{ position:relative; border-left:1px solid #ddd; border-right:1px solid #ddd; height:9px; width:30px; margin:0 auto; } .elevator-module .back-top:hover{ background-color:#00a1d6; border-color:#00a1d6; } .elevator-module .back-top{ position:relative; display:block; cursor:pointer; height:50px; background-color:#f6f9fa; overflow:hidden; border: 1px solid #e5e9ef; border-radius:4px; text-align: center; } .circle{ display:inline-block; width:30px; height:30px; margin-top:10px; border-radius:50%; background:#ffafc9; } .part{ width:800px; height:600px; margin:20px auto; background:#00a1d6; text-align:center; line-height:600px; font-size:13em; font-family:Comic Sans MS; color:#fff; font-weight:700; border-radius:4px; } .container{ width:1160px; margin:0 auto; position:relative; }
// JavaScript var ElevatorModule = { init() { this.scrollEvent(); }, scrollEvent() { var elevator = document.querySelector('.elevator-module'); var navList = document.querySelector('.nav-list'); var items = document.querySelectorAll('.nav-list .item'); var modules = document.querySelectorAll('.part'); var backtop = document.querySelector('.back-top'); window.addEventListener('scroll', slipper); navList.addEventListener('click', moveTo); backtop.addEventListener('click', fly); function slipper() { for ( var item of items ) { var rect = item.getBoundingClientRect(); var scrollTop = document.documentElement.scrollTop; var item_top = rect.top + scrollTop; //获取右侧导航每一个分区上方到页面最顶部的距离 var fetch = getLocation(); var id = item.getAttribute('data-id'); if ( item_top >= fetch[id] - 100 ) { //移到哪一个分区右侧的hover就停留在哪 items.forEach((el) => { el.classList.remove('active'); }) item.classList.add('active'); } } } var flag = true; //定位到指定分区 function moveTo() { var e = window.event; items.forEach((item) => { item.classList.remove('active'); }) if ( e.target && e.target.nodeName == 'DIV' ) { var id = e.target.getAttribute('data-id'); e.target.classList.add('active'); var fetch = getLocation(); if ( flag ) { AnimationFrame(fetch[id]); id ? flag = false : flag = true; //设置节流效果, 即在运动的过程当中不容许再次触发AnimationFrame函数 } } } //scrollTop过渡 function AnimationFrame(future_top) { let current_top = document.documentElement.scrollTop; var timer = setInterval( () => { if ( current_top > future_top ) { current_top -= 60; document.documentElement.scrollTop = current_top; } else if ( current_top < future_top ) { current_top += 60; document.documentElement.scrollTop = current_top; } if ( current_top >= future_top - 30 && current_top <= future_top + 30 ) { //当移动到指定的范围内时直接定位到对应的位置 document.documentElement.scrollTop = future_top; clearInterval(timer); flag = true; } },10) } //获取pageY function getLocation() { var arr = []; modules.forEach((module) => { var thing = pageY(module); arr.push(thing); }) return arr; } //获取页面中间每一个分块上方到页面最顶部的距离 function pageY(el) { if ( el.offsetParent ) { return el.offsetTop + pageY(el.offsetParent); } else { return el.offsetTop; } } //平滑返回页面顶部 function fly() { cancelAnimationFrame(timer); var timer = requestAnimationFrame( function fn() { var top = window.pageYOffset; if ( top > 0 ) { document.documentElement.scrollTop = top - 80; timer = requestAnimationFrame(fn); } else { cancelAnimationFrame(timer); } }) } } } ElevatorModule.init();
PS:
一些须要注意的常见js事件属性:
终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件再也不被分派到其余节点。
该方法将通知 Web 浏览器不要执行与事件关联的默认动做(若是存在这样的动做)。
参考文章:
https://www.cnblogs.com/caizh... XSS