[深刻08] 前端安全

導航

[深刻01] 執行上下文
[深刻02] 原型鏈
[深刻03] 繼承
[深刻04] 事件循環
[深刻05] 柯里化 偏函數 函數記憶
[深刻06] 隱式轉換 和 運算符
[深刻07] 瀏覽器緩存機制(http緩存機制)
[深刻08] 前端安全
[深刻09] 深淺拷貝
[深刻10] Debounce Throttle
[深刻11] 前端路由
[深刻12] 前端模塊化
[深刻13] 觀察者模式 發佈訂閱模式 雙向數據綁定
[深刻14] canvas
[深刻15] webSocket
[深刻16] webpack
[深刻17] http 和 https
[深刻18] CSS-interview
[react] Hookscss

[部署01] Nginx
[部署02] Docker 部署vue項目
[部署03] gitlab-CIhtml

[源碼-webpack01-前置知識] AST抽象語法樹
[源碼-webpack02-前置知識] Tapable
[源碼-webpack03] 手寫webpack - compiler簡單編譯流程前端

XSS

  • XSS ( Cross Site Script ) 跨站腳本攻擊
  • Cross Site Script 本來是縮寫爲CSS,爲了區分層疊樣式表css,改寫爲XSS
  • 原理:XSS攻擊是指攻擊者在網站上注入惡意的客戶端代碼,經過 惡意腳本 對客服端網頁進行篡改,從而在用戶瀏覽網頁時,對用戶瀏覽器就行控制,或者獲取用戶隱私數據一種攻擊方式
  • 惡意腳本:主要指 javascrip代碼,有時也指 htmlflash
  • 攻擊方式:有多種,共同的特徵是:竊取用戶的隱私數據
  • 攻擊類型:能夠分爲三類,反射型(非持久型)儲存型(持久型)基於DOM
  • 危害:
    • 利用虛假的輸入表單,騙取用戶的我的信息
    • 利用腳本獲取用戶的cookie
    • 顯示僞造的圖片和文章

反射型(非持久型)XSS攻擊

  • 反射型 XSS 只是簡單地把用戶輸入的數據 「反射」 給瀏覽器,這種攻擊方式每每須要攻擊者誘使用戶點擊一個惡意連接,或者提交一個表單,或者進入一個惡意網站時,注入腳本進入被攻擊者的網站。

儲存型(持久型)XSS攻擊

  • 存儲型 XSS 會把用戶輸入的數據 "存儲" 在服務器端,當瀏覽器請求數據時,腳本從服務器上傳回並執行。這種 XSS 攻擊具備很強的穩定性。

基於DOM的XSS攻擊

  • 基於 DOM 的 XSS 攻擊是指經過惡意腳本修改頁面的 DOM 結構,是純粹發生在客戶端的攻擊。

防護XSS攻擊

  • 設置 httpOnly 阻止經過script腳本獲取cookie
    • Document.cookie屬性XMLHttpRequest對象Request API
    • XMLHttpRequest獲取cookie
      • 經過xhr.getResponseHeader('Set-Cookie') // null,不能獲取cookie
      • 經過xhr.getAllResponseHeader()獲取全部的simple response header,並不包括Set-Cookie字段
      • 注意:這兩種方法都只能獲取simple response heade,而不能獲取Set-Cookie字段
      • 下面會詳解介紹XMLHttpRequest
  • 過濾檢查:
    • 對input,textares,form表單等作特殊符號的過濾檢查
    • HtmlEncode:某些狀況下,不能對用戶數據進行嚴格過濾,須要對標籤進行轉換
    • JavaScriptEncode
(1) HtmlEncode:對html標籤進行轉換
< -------------------------- &lt
> -------------------------- &gt
& -------------------------- &amp
'' ------------------------- &quot
空格 ----------------------- &nbsp



(2) JavascriptEncode:對js一些特殊符號進行轉碼
" ------------------------ \" \n ----------------------- \\n \r ----------------------- \\r 複製代碼

CSRF

  • CSRF( Cross Site Request Forgery ) 跨站請求僞造 forgeries:僞造品的意思
  • CSRF是一種劫持受信任用戶向服務器發送非預期請求的攻擊方式
  • 原理:
    • 主要是經過獲取用戶在目標網站的cookie,騙取目標網站的服務器的信任,在用戶已經登陸目標站的前提下,訪問到了攻擊者的釣魚網站,攻擊者直接經過 url 調用目標站的接口,僞造用戶的行爲進行攻擊,一般這個行爲用戶是不知情的。
    • 即獲取了cookie,就能夠作不少事情:好比以你的名義發送郵件、發信息、盜取帳號、購買商品、虛擬貨幣轉帳等等
案例:

CSRF攻擊的思想:(核心2和3)
一、用戶瀏覽並登陸信任網站(如:淘寶)
二、登陸成功後在瀏覽器產生信息存儲(如:cookie)
三、用戶在沒有登出淘寶的狀況下,訪問危險網站 
  // 注意:若是該cookie在沒有設置過時時間或者爲null,默認是會話時間session-cookie,關閉瀏覽器後cookie會被清除
  // Expires,Max-Age能夠設置cookie的過時時間
  // 因此這裏強調了是沒有登出的狀況,就有cookie被獲取的風險
  // 若是cookie設置了具體的過時時間,有效期內均可能被獲取
四、危險網站中存在惡意代碼,代碼爲發送一個惡意請求(如:購買商品/餘額轉帳)
  // 該請求,攜帶剛剛在瀏覽器產生的信息(cookie),進行惡意請求
五、淘寶驗證請求爲合法請求(區分不出是不是該用戶發送)
  // 用HTTP中的header頭中的 Refer 來預防
  // refer 能夠檢查請求源,只有合法的請求來源服務器才予以響應
六、達到了惡意目標
複製代碼

預防CSRF攻擊

  • 驗證碼:被認爲是對抗CSRF攻擊最簡潔有效的防護方法
    • CSRF每每是在用戶不知情的狀況下構建了網絡請求,而驗證碼會強制用戶必須與應用進行交互才能完成最終的請求
    • 注意:出於用戶考慮,不能給網站的全部操做都加上驗證碼,因此驗證碼只能做爲防護CSRF的輔助手段
  • refer檢查
    • HTTP中有Refer字段,表示請求來源地址,經過Refer能夠檢查請求是否來自合法的源,服務器只對合法的源予以響應
  • token
    • CSRF主要就是獲取cookie,因此要防護的話,就須要在請求中加入攻擊者不能僞造的信息,而且該信息不能保存在cookie中
    • 能夠在 HTTP 請求中以參數的形式加入一個隨機產生的 token,並在服務器端創建一個攔截器來驗證這個 token,若是請求中沒有 token 或者 token 內容不正確,則認爲多是 CSRF 攻擊而拒絕該請求。

複習

cookie

  • cookie是服務器保存在瀏覽器的一小段文本信息,大小不超過4KB,每次請求都會攜帶cookie
  • cookie主要用來分辨兩個請求知否來自同一個瀏覽器,和存儲一個狀態信息:如用戶偏好顏色,字體大小等
  • 瀏覽器能夠設置不接受cookie,也能夠設置不向瀏覽器發送cookie
  • window.navigator.cookieEnabled 返回一個布爾值,表示瀏覽器是否打開cookie功能
  • document.cookie 返回當前網頁的cookie
  • 單個域名設置的cookie不該該超過30個,大小不超過 4KB
  • 共享cookie的條件:域名和端口必須同樣,不要求協議相同
  • cookie的產生:cookie由HTTP協議生成,也主要是供HTTP協議使用

HTTP迴應:cookie的生成

  • 生成cookie:服務器經過設置HTTP迴應頭信息中,Set-Cookie 字段來保存cookie
  • HTTP迴應頭信息中,能夠包含多個 Set-Cookie 字段
  • 除了 cookie 的值,Set-Cookie 還能夠附加cookie的屬性,能夠包含多個屬性,沒有次序要求
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
// Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly  多個屬性,沒有次序要求

[page content]
複製代碼

服務器如何修改已經存在的cookie

  • 必須知足四個條件:cookie的 key,domain,path,secure 都匹配
  • 只要有一個屬性不一樣,都會生成全新的cookie
Set-Cookie: key1=value1; domain=example.com; path=/blog
Set-Cookie: key1=value2; domain=example.com; path=/

// 上面由於path不同,就設置了一個全新的cookie

// 下次發起請求時,會同時發送兩個cookie
// 發起請求時,發送的cookie => Cookie: key1=value1; key1=value2
// 注意:兩個cookie同名,可是越精確的cookie排在越前面
複製代碼

刪除一個現存cookie的惟一方法

  • 設置Expires屬性爲一個過時的時間

HTTP請求:cookie的發送

  • 瀏覽器在向服務器發送HTTP請求時,每一個請求都會攜帶上cookie
    • (即瀏覽器把以前服務器保存在瀏覽器上的cookie再發回服務器)
    • 發送時使用 Cookie 字段
    • 生成時使用 Set-Cookie 字段
  • Cookie字段能夠包含多個cookie,使用 ; 號分割
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
複製代碼

服務器收到瀏覽器發來的cookie時,有兩點沒法知道vue

  • Cookie的各類屬性:Expires,Max-Age,Domain,Path,Secure,HttpOnly
  • 哪一個域名設置的cookie,一級域名設置的,仍是二級域名設置的

Cookie的屬性

Expires,Max-Age

  • Expires屬性:指定一個具體的過時時間,UTC格式
    • 可使用 Date.prototype.toUTCString() 進行格式轉換
    • 若是不設置Expires屬性,或者設置null,該cookie就是session-cookie,只在會話時間內有效,即關閉瀏覽器窗口,當前session結束,cookie就會被刪除
  • Max-Age屬性:從如今開始cookie存在的秒數。60*60*24*365一年
  • 同時存在Expires和Max-Age時,Max-Age優先

Domain,Path

  • Domain:指定瀏覽器發出HTTP請求時,哪些域名要附帶這個Cookie
    • 若是沒有指定該屬性,將其設置爲URL的一級域名
  • Path:指定瀏覽器發出HTTP請求時,哪些路徑要附帶這個cookie
    • 只要瀏覽器發現,Path屬性是HTTP請求路徑的開頭一部分,就會在頭信息中攜帶這個cookie

Secure,HttpOnly

  • Secure:指定只有在加密協議HTTPS下,才能將這個cookie發送到服務器
  • HttpOnly:指定該cookie,沒法經過腳本拿到
    • Document.cookie屬性,XMLHttpRequest對象, Request API 都拿不到該cookie
    • Document.cookie讀寫cookie
      • Document.cookie讀取:讀取所有
      • Document.cookie寫入:一次只能寫入一個,寫入不是覆蓋,而是添加
      • Document.cookie的讀寫差別與HTTPT通訊格式有關:
      • 發送:HTTP請求發送cookie,Cookie字段,一次能夠設置多個cookie,用分號隔開
      • 生成:Http響應生成cookie,Set-Cookie字段,一次設置一個cookie,但能夠有多個Set-Cookie字段

XMLHttpRequest

  • const api = new XMLHttpRequest()
  • api.open(method, url, async)
  • api.send(body)
  • api.setRequestHeader()
  • api.getResponseHeader() // 獲取參數對應的 simple response header,參數必須在simple response header範圍內
  • api.getAllResponseHeader() // 獲取全部 simple response header
  • api.onreadystatechange()
  • api.onload()
  • api.onprogress() // 下載進度
  • api.upload.onprogress() // 上傳進度信息
  • api.setRequestHeader(name, value)
  • api.responseType // text, document, json, blob, arrayBuffer
  • api.timeout
  • api.abort() // 終止請求
  • api.withCredentidals // 一個布爾值,表示跨域請求時,是否能夠攜帶認證信息,如cookie
const api = new XMLHttpRequest()

(1) api.open()
- 初始化HTTP請求參數(url,http方法等),但並不發送請求,供 send() 方法使用

api.open(method, url, async, username, password)
method:HTTP請求的方法GET, POST, HEAD
url:請求的地址
async:是否異步,默認是true,即異步的發送請求
  // false:同步,對send方法的調用將阻塞,直到響應徹底接受
  // true或者省略:異步,且一般須要調用 onreadystatechange() 方法
  
(2) api.send()
- 發送一個http請求,請求參數寫在send方法中

api.send(body)
get請求: 參數能夠寫在open()方法中
post請求:參數卸載send()方法中

(3) api.setRequestHeader()
- 指定一個HTTP請求的頭部(請求頭),只有在readyState爲 1 時才能調用

api.setRequestHeader(name, value)
name: key
value:value
注意:setRequestHeader()方法能夠屢次調用,最終的值不是覆蓋override而是追加append
注意:setRequestHeader()方法只有在 readyState = 1 時才能調用,即open()以後send()以前

(4) api.getResponseHeader()
- 返回指定的HTTP響應頭部的值(響應頭)

(5) api.abort() //abort:停止的意思
- 取消當前響應,關閉鏈接而且結束任何未決的網絡活動
- api.abort()將readyState重置爲0
- 應用:若是請求用了太長的時間,並且響應不在必要時,能夠調用這個方法


(6) api.onreadystatechange()
- api.onreadystatechange()但 readyState = 3 時能夠調用屢次
- 注意:onreadystatechange都是小寫,而readyState是駝峯寫法

readyState狀態:
0 UNSENT ------------------ xhr對象成功構造,open()方法未被調用
1 OPEND ------------------- open()方法被調用,send()方法還未被調用,setRequestHeader()能夠被調用
2 HEADERS_RECEIVED -------- send()方法被調用,響應頭和響應狀態已經返回
3 LOADING ----------------- 響應體(response entity body)正在下載中,此狀態下api.response可能已經有了響應數據
4 DONE -------------------- 整個數據傳輸過程結束,無論本次請求是成功仍是失敗

(7) api.onload()
- api.onload()請求成功時觸發,此時 readyState = 4
- 請求成功的回調有兩個:
- 1. readyState===4時的api.onreadystatechange()
- 2. api.onload()方法
api.onload = function () {
  //若是請求成功
  if(api.status == 200){
    //do successCallback
  }
}

注意:status===200是有坑的,由於協商緩存返回的狀態碼是304,請求也是成功的請求,因此下面的判斷跟完善
api.onload = function() {
    if((api.status>=200 && api.status < 300) || api.status === 304) {
        // do successCallback
    }
}

(8) api.timeout
- api.timeout用來設置過時時間

問題1:請求的開始時間怎麼肯定?是api.onloadstart事件觸發的時候,也就是api.send()調用的時候
解析:由於api.open()只是建立了連接,當並無真正傳輸數據,只有調用api.send()時才真正開始傳輸
問題2:何時是請求結束?
解析:api.loadend事件觸發時結束

(9)  api.onprogress() 下載進度信息
(10) api.upload.onprogress = function(e) { 上傳進度信息
     if ( e.lengthComputable ) {
      const present = e.loaded / e.total * 100;
    }
}


-----------------------------------------------------------------------------------------------
1.
問題1:如何獲取response
提供三個屬性來獲取response:( api.response ) 和 ( api.responseText ) 和 ( responseXML )
api.responseText --- api.responseType='text'''、不設置時,xhr對象上纔有此屬性,此時才能調用
api.response --- responseType爲"""text"時,值爲"";responseType爲其餘值時,值爲 null


2.
問題2:api.responseType有哪些類型
api.responseType類型有:text, document, json, blob, arrayBuffer
複製代碼

api.getReponseHeader() 和 api.getAllResponseHeader()

  • api.getResponseHeader()獲取參數指定的 simple response header
  • api.getAllResponseHeader() 獲取全部 simple resopnse header
  • 注意:這兩個方法都不能獲取Set-Cookie,不管是同域仍是跨域
  • 注意:對於跨域請求,客戶端只能獲取simple response header 和 Access-Control-Expose-Headers
    • simple response header:
      • Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma
    • Access-Control-Expose-Headers:
      • 只在跨域請求時的響應頭中存在,表示服務端容許暴露給客服端的headers
      • 同域請求的響應中不包含該字段
      • expose:是暴露的意思
  • 總結:getAllResponseHeader()只能拿到限制之外的response header, getResponseHeader()的參數也必須是限制之外的參數,不然會Refused to get unsafe header的錯誤

api.withCredentials

  • api.withCredentials一個布爾值,表示跨域請求時,是否能夠攜帶認證信息,默認是false即不容許攜帶
    • 好比 cookie
  • 注意:在同域請求時,cookie會在請求頭中自動攜帶,可是在跨域請求時,並無自動攜帶,緣由是:在CORS標準中作了規定,默認狀況下,瀏覽器在發送跨域請求時,不能發送任何認證信息(credentials)如"cookies"和"HTTP authentication schemes"。除非xhr.withCredentials爲true
  • 跨域請求:因此跨域請求時
    • 客戶端: api.withCredentials = true 設置爲true
    • server端(響應頭):Access-Control-Allow-Credentials:true

案例

get請求

go() {
      console.log('1111111111');
      const api = new XMLHttpRequest();
      api.open('GET',  ----- 初始http請求參數,請求方式,url, 是否異步
      'https://bing.ioliu.cn/v1/rand?type=json', true);
      api.responseType = 'text';   ------ 文本格式的響應
      api.timeout = 5000; ---- 請求過時時間
      api.setRequestHeader('Content-type', 'application/json'); ----- 必須在open()後,send()前設置
      api.onreadystatechange = function() { ------ readyState改變時觸發
        if ( api.readyState === 4 && this.status === 200) { ---- this指的是api實例
          console.log(JSON.parse(this.responseText)) ------ this.response也能拿到一樣的數據
        }
      }
      // 除了在api.onreadystatechange指指定的會調中判斷readyState===4,也能夠直接在onload中觸發
      // 兩種方法均可以
      // 只判斷200狀態碼不完善,應該判斷 2xx 或者 304 則請求成功
      api.onload = function() { 
        if ( api.status >= 200 && api.status < 300 || api.status === 304 ) {
          console.log(JSON.parse(api.responseText), 'onload在請求成功時觸發');
        }
      }
      api.send();  ---- 發送數據
}
複製代碼

面試精簡 juejin.im/post/5cef3a…
juejin.im/post/59dc2b…
juejin.im/post/5cef3a…
美團 juejin.im/post/5bc009…
知乎 zhuanlan.zhihu.com/p/83865185
XMLHttpRequest segmentfault.com/a/119000000…
個人簡書 www.jianshu.com/p/7292d8b5d…java

相關文章
相關標籤/搜索