前端筆記(三) 前端如何防範XSS,淺談JS中各類寬高屬性

前端如何防範XSS

XSS ( Cross Site Scripting )

跨站腳本攻擊, 是一種攻擊者向用戶的瀏覽器注入惡意代碼腳本的攻擊。html

XSS攻擊的種類:前端

  • 持續型XSS攻擊

攻擊者輸入惡意代碼並經過評論或表單提交等方式將其提交到網站, 而網站的後端卻對用戶的數據不作任何處理進而直接存入數據庫, 當其餘用戶訪問該網站而且須要請求網站的評論數據時網站後端直接從數據庫中取出帶有惡意代碼的數據返回給用戶頁面此時用戶就會遭受到攻擊, 好比while { alert( 'Attack!' ) } 這樣的惡意腳本。node

  • 反射型XSS攻擊

用戶向網站發送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>
*/
  • 基於DOM的XSS攻擊

它是反射型攻擊的一種, 此時服務器返回的頁面是正常的, 只是頁面在執行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( Content Security Policy )

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的各類寬高屬性

網上看到有一篇博客講的挺好挺全面的, 有須要的能夠去看看:

戳這裏看JS中的各類寬高圖解

  • 本身寫的一個小demo

場景:實現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事件屬性:

  • event.stopPropagation()

終止事件在傳播過程的捕獲、目標處理或起泡階段進一步傳播。調用該方法後,該節點上處理該事件的處理程序將被調用,事件再也不被分派到其餘節點。

  • event.preventDefault()

該方法將通知 Web 瀏覽器不要執行與事件關聯的默認動做(若是存在這樣的動做)。

  • 不論鼠標指針穿過被選元素或其子元素,都會觸發 mouseover 事件。對應mouseout
  • 只有在鼠標指針穿過被選元素時,纔會觸發 mouseenter 事件。對應mouseleave

參考文章:

https://www.cnblogs.com/caizh...  XSS

https://www.cnblogs.com/zzgbl...  轉義方法

http://www.ruanyifeng.com/blo...   CSP

相關文章
相關標籤/搜索