首頁  >  文章  >  web前端  >  看看這些關於瀏覽器的面試題,你能答對幾個?

看看這些關於瀏覽器的面試題,你能答對幾個?

青灯夜游
青灯夜游轉載
2022-03-23 10:39:593662瀏覽

這篇文章跟大家分享一些關於瀏覽器的面試題,看看你能回答幾個?對對答案解析,看看能答對幾個!

看看這些關於瀏覽器的面試題,你能答對幾個?

1.常見的瀏覽器核心有哪些?

瀏覽器的核心可以分成兩個部分:

#渲染引擎JS引擎注意:我們常說的瀏覽器核心就是指渲染引擎

由於JS引擎越來越獨立,核心就指的只是渲染引擎了,渲染引擎主要用來請求網路頁面資源解析排版後呈現給使用者

SpiderMonkeyJavaScriptCoreChakra(For JavaScript) Chakra(For JScript)Linear A(4.0-6.1)/ Linear B(7.0-9.2) / Futhark(9.5-10.2)/ Carakan(10.5-)##-V8
瀏覽器/RunTime 核心(渲染引擎) JavaScript 引擎
Chrome Blink(28~) Webkit(Chrome 27) V8
# #FireFox Gecko
#Safari Webkit
Edge EdgeHTML
IE Trident
Opera Presto->blink
#Node.js
###

2.瀏覽器的主要組成部分有哪些?

  • 使用者介面:包含網址列,前進/後退/刷新/書籤等按鈕

  • 瀏覽器引擎:在使用者介面和呈現引擎之間傳送指令

  • 渲染引擎:用來繪製請求的內容

  • 網路:用來完成網路調用,例如http請求,它具有平台無關的接口,可以在不同平台上工作

  • JavaScript解釋器:用來解析執行JavaScript程式碼

  • 使用者介面後端:用於繪製基本的窗口小部件,例如組合框和窗口,底層使用操作系統的用戶接口

  • 數據存儲:屬於持久層,瀏覽器在硬碟中保存類似cookie的各種數據,HTML5定義了web database技術,這是一種輕量級完整的客戶端儲存技術

##注意:與大多數瀏覽器不同的是,Google(Chrome)瀏覽器的每個標籤頁都分別對應一個呈現引擎實例。每個標籤頁都是一個獨立的進程

看看這些關於瀏覽器的面試題,你能答對幾個?

#3.說一說從輸入URL到頁面呈現發生了什麼?

這個題可以說是面試最常見也是一道可以無限難的題了,一般面試官出這題就是為了考察你的前端知識深度。

1、瀏覽器接受URL開啟網路請求執行緒(涉及:瀏覽器機制,執行緒與進程等)

2、開啟網路執行緒到發出一個完整的http請求(涉及:DNS查詢,TCP/IP請求,5層網路協定等)

3、從伺服器接收到請求到對應後台接受到請求(涉及:負載平衡,安全攔截,後台內部處理等等)

4、後台與前台的http互動(涉及:http頭,回應碼,封包結構,cookie等)

#5、快取問題(涉及:http強緩存與協商緩存,緩存頭,etag,expired,cache-control等)

6、瀏覽器接受到http封包後的解析流程(涉及html詞法分析,解析成DOM樹,解析CSS生成CSSOM樹,合併產生render渲染樹。然後layout佈局,painting渲染,複合圖層合成,GPU繪製,外鏈處理等)

#7、css視覺化模型(涉及:元素渲染規則,如:包含區塊,控制框,BFC,IFC等)

8、JS引擎解析過程(涉及:JS解析階段,預處理階段,執行階段產生執行上下文,VO(全域物件),作用域鏈,回收機制等)

你會發現一個簡單的輸入URL到頁面呈現,之間會發生這麼多過程,是不是瞬間覺得崩潰了?(別急,這一章我們不講這麼深,先教你如何回答這個問題,後面這一節單獨出文章講)

    #瀏覽器透過DNS伺服器得到網域的IP位址,向這個IP位址請求得到HTML文字
  • 瀏覽器渲染進程解析HTML文本,建構DOM樹
  • 解析HTML的同時,如果遇到內聯樣式或樣式文件,則下載並建立樣式規則,如果遇到JavaScript腳本,則會下載執行腳本
  • DOM樹和CSSOM建置完成之後,渲染程序將兩者合併成渲染樹(render tree)
  • 渲染程序開始對渲染樹進行佈局,產生佈局樹(layout tree)
  • 渲染樹對佈局樹進行繪製,產生繪製記錄

看看這些關於瀏覽器的面試題,你能答對幾個?

4.瀏覽器是如何解析程式碼的?

解析HTML

HTML是逐行解析的,瀏覽器的渲染引擎會將HTML文件解析並轉換成DOM節點。

    將HTML解析成許多Tokens
  • 將Tokens解析成object
  • 將object組合成一個DOM樹

解析CSS

瀏覽器會從右往左解析CSS選擇器

我們知道DOM樹與CSSOM樹合併成render樹,實際上是將CSSOM附著到DOM樹上,因此需要根據選擇器提供的資訊對DOM樹進行遍歷。

我們來看一個例子:

<style>
.nav .title span {color:blue}
</style>

<div class=&#39;nav&#39;>
  <div class=&#39;title&#39;>
    <span>南玖</span>
  </div>
  <div class="sub_title">前端</header>
</div>

從右到左的匹配:

  • 先找到所有的最右節點span,對於每一個span ,向上尋找節點div.title

  • 由h3再向上尋找div.nav 的節點

  • 最後找到根元素html 則結束這個分支的遍歷。

解析JS

#在瀏覽器中有一個js解析器的工具,專門用來解析我們的js程式碼。

當瀏覽器遇到js程式碼時,立刻召喚「js解析器」出來工作。

解析器會找到js當中的所有變數、函數、參數等等,並且把變數賦值為未定義(undefined)。

把函數取出來成為一個函數塊,然後存放到倉庫當中。這件事情做完了才開始逐行解析程式碼(由上向下,由左向右),然後再去和倉庫進行配對。

5.DOMContentLoaded與load的差別?

  • DOMContentLoaded:僅當DOM解析完成後觸發,不包含樣式表,圖片等資源。
  • Load:當頁面上所有的DOM,樣式表,腳本,圖片等資源加載完畢事觸發。

6.瀏覽器重繪網域重排的差異?

  • 重排: 部分渲染樹或整個渲染樹需要重新分析且節點尺寸需要重新計算,表現為重新產生佈局,重新排列元素
  • 重繪: 由於節點的幾何屬性改變或樣式改變,例如元素背景元素,表現為某些元素的外觀被改變

重繪不一定導致重排,但重排一定繪導致重繪

如何觸發重繪和重排?

任何改變用來建立渲染樹的資訊都會導致一次重排或重繪:

    ##新增、刪除、更新DOM節點
  • 透過display: none隱藏一個DOM節點-觸發重排和重繪
  • 透過visibility: hidden隱藏一個DOM節點-只觸發重繪,因為沒有幾何變化
  • 移動或給頁面中的DOM節點新增動畫
  • 新增一個樣式表,調整樣式屬性
  • 使用者行為,例如調整視窗大小,改變字號,或捲動。

如何避免重繪或重排?

  • 集中改變樣式:例如使用class的方式來集中改變樣式

  • 使用

    document.createDocumentFragment ():我們可以透過createDocumentFragment建立一個遊離於DOM樹以外的節點,然後在此節點上批次操作,最後插入DOM樹中,因此只觸發一次重排

  • #提升為合成層

    將元素提升為合成層有以下優點:

    • #合成層的點陣圖,會交由

      GPU 合成,比CPU 處理要快

    • 當需要

      repaint 時,只需要repaint 本身,不會影響到其他的層

    • 對於

      transformopacity 效果,不會觸發layoutpaint

      提升合成層的最好方式是使用CSS 的

      will-change 屬性

  • ##7.為什麼JS是單線程的?

這主要與JS的用途有關,JS作為瀏覽器的腳本語言,最初主要是實現使用者與瀏覽器的交互,以及操作DOM。這決定了它只能是單線程,否則會帶來許多複雜的同步問題。

舉個例子:

如果JS是多執行緒的,其中一個執行緒要修改一個DOM元素,另外一個執行緒想要刪除這個DOM元素,這時候瀏覽器就不知道該聽誰的。所以為了避免複雜性,從一誕生,JavaScript就被設計成單一執行緒。

為了利用多核心CPU的運算能力,HTML5提出Web Worker標準,允許JavaScript腳本建立多個線程,但是子執行緒完全受主執行緒控制,且不得操作DOM。所以,這個新標準並沒有改變JavaScript單執行緒的本質

8.CSS載入會阻塞DOM嗎?

先上結論

    #CSS
  • 不會阻塞DOM的解析,但會阻塞DOM的渲染
  • CSS
  • 會阻塞JS執行,但不會阻塞JS檔案的下載

CSSOM的作用

    #第一個是提供給JavaScript操作樣式表的能力
  • 第二個是為佈局樹的合成提供基礎的樣式資訊
  • 這個CSSOM體現在DOM中就是
  • document.styleSheets

  • 由先前講到的瀏覽器渲染流程我們可以看出:

    DOM和CSSOM通常是並行建構的,所以
  • CSS載入不會阻塞DOM的解析

  • render樹是依賴DOM樹和CSSOM樹的,所以它必須等到兩者都載入完畢才能開始建立渲染,所以
  • CSS載入會阻塞DOM的渲染

  • 由於JavaScript是可以操作DOM與CSS的,如果在修改這些元素屬性同時渲染介面(即JavaScript執行緒與UI執行緒同時進行),那麼渲染執行緒前後獲得的元素可能就不一致了。所以為了防止渲染出現不可預期的結果,瀏覽器設定
  • GUI渲染執行緒與JavaScript執行緒為互斥

    的關係

##JS需要等待CSS的下載,這是為什麼呢? (CSS阻塞DOM執行)

如果JS腳本的內容是取得元素的樣式,那麼它就必然依賴CSS。因為瀏覽器無法感知JS內部到底想幹什麼,為避免樣式獲取,就只好等前面所有的樣式下載完畢再執行JS。但JS檔案與CSS檔案下載是並行的,CSS檔案會在後面的JS檔案執行前先載入執行完畢,所以CSS會阻塞後面JS的執行

避免白屏,提高CSS的載入速度

  • 使用CDN(CDN會根據你的網路狀況,挑選最近的一個有快取內容的節點為你提供資源,因此可以減少載入時間)
  • 對CSS進行壓縮
  • #合理使用快取
  • #減少http請求數,合併CSS檔案

9 .JS會阻塞頁面嗎?

先上結論

JS會阻塞DOM的解析,因此也會阻塞頁面的載入

這也是為什麼我們常說要把JS檔案放在最下面的原因

#由於JavaScript 是可操縱DOM 的,如果在修改這些元素屬性同時渲染介面(即JavaScript 執行緒和UI 執行緒同時運行),那麼渲染執行緒前後獲得的元素資料就可能不一致了。

因此為了防止渲染出現不可預期的結果,瀏覽器設定 **「GUI 渲染執行緒與 JavaScript 引擎為互斥」**的關係。

當 JavaScript 引擎執行時 GUI 執行緒會被掛起,GUI 更新會被保存在一個佇列中等到引擎執行緒空閒時立即被執行。

當瀏覽器在執行 JavaScript 程式的時候,GUI 渲染線程會被保存在一個佇列中,直到 JS 程式執行完成,才會接著執行。

因此如果 JS 執行的時間過長,這樣就會造成頁面的渲染不連貫,導致頁面渲染載入阻塞的感覺。

10.defer和async的差別?

  • 兩者都是異步去載入外部JS文件,不會阻塞DOM解析
  • Async是在外部JS載入完成後,瀏覽器空閒時,Load事件觸發前執行,標記為async的腳本並不保證按照指定他們的先後順序執行,該屬性對於內聯腳本無作用(即沒有**“src”**屬性的腳本)。
  • defer是在JS載入完成後,整個文件解析完成後,觸發DOMContentLoaded 事件前執行,如果缺少src 屬性(即內嵌腳本),該屬性不應被使用,因為這種情況下它不起作用

11.瀏覽器的垃圾回收機制

##垃圾回收是一種自動的記憶體管理機制。當電腦上的動態記憶體不再需要時,就應該予以釋放。

要注意的是,自動的意思是瀏覽器可以自動幫助我們回收記憶體垃圾,但不代表我們不用關心記憶體管理,如果操作不當,JavaScript中仍會出現記憶體溢出的情況,造成系統崩潰。

由於字串,數組,物件等都沒有固定大小,因此需要當它們大小已知時,才能對他們進行動態的儲存分配。 JavaScript程式每次創建字串,數組或物件時,解釋器都必須分配記憶體來儲存那個實體。

JavaScript解釋器可以偵測到何時程式不在使用一個物件了,當它確定這個物件是無用的時候,他就知道不再需要這個物件了,就可以把它佔用的記憶體釋放掉了。

瀏覽器通常採用的垃圾回收有兩種方法:

標記清除引用計數

標記清除

這是JavaScript中最常用的垃圾回收方式

#從2012年起,所有現代瀏覽器都使用了標記清除的垃圾回收方法,除了低版本IE還是採用的引用計數法。

那麼什麼叫標記清除呢?

JavaScript中有一個全域對象,定期的,垃圾回收器將從這個全域對象開始,找出所有從這個全域對象開始引用的對象,再找這些對象所引用的物件...對這些活躍的物件標記,這是標記階段。清楚階段就是清楚那些沒有被標記的物件。

標記清除有一個問題,就是在清除之後,記憶體空間是不連續的,也就是出現了記憶體碎片。如果後面需要一個比較大的連續的記憶體空間,那就無法滿足要求。而

標記整理 方法可以有效德地解決這個問題。

在標記的過程中,引入了概念:三色標記法,三色為:

    白:未被標記的對象,即不可達對象(沒有掃描到的物件),可回收
  • 灰:已被標記的物件(可達物件),但是物件還沒有被掃描完,不可回收
  • 黑:已掃描完(可達物件),不可回收

標記整理:

標記階段與標記清除法沒什麼區別,只是標記結束後,標記整理法會將存活的物件向記憶體的一邊移動,最後清理掉邊界記憶體。

引用計數

#引用計數的意思是追蹤記錄每個值被引用的次數。當一個變數A被賦值時,這個值的引用次數就是1,當變數A重新賦值後,則之前那個值的引用次數就減1。當引用次數變成0時,表示沒有辦法再存取這個值了,所以就可以清除這個值佔用的記憶體了。

大多數瀏覽器已經放棄了這種回收方式

記憶體洩漏

為避免記憶體洩漏,一旦資料不再使用,最好透過將其值設為null來釋放其引用,這個方法叫做接觸引用

#哪些情況會造成記憶體洩漏?如何避免?

以Vue 為例,通常有這些狀況:

  • 監聽在window/body 等事件沒有解綁
  • 綁在EventBus 的事件沒有解綁
  • Vuex$storewatch 了之後沒有 unwatch
  • 使用第三方函式庫創建,沒有呼叫正確的銷毀函數

#解決:beforeDestroy 中及時銷毀

  • #綁定了DOM/BOM 物件中的事件addEventListener
  • removeEventListener

觀察者模式 $on

    $off
  • 處理。

    如果元件中使用了定時器,應銷毀處理。
    • 如果在
    • mounted/created
    鉤子中使用了第三方函式庫初始化,對應的銷毀。
  • 使用弱引用

    weakMap

      weakSet
    瀏覽器中不同類型變數的記憶體都是何時釋放的?

引用類型在沒有引用之後,透過 V8 自動回收。

基本型別如果處於閉包的情況下,要等閉包沒有引用才會被 V8 回收。

非閉包的情況下,等待 V8 的新生代切換的時候回收。

看看這些關於瀏覽器的面試題,你能答對幾個?

12.說一說瀏覽器的快取機制?

認識瀏覽器快取
  • 當瀏覽器要求一個網站時,會載入各種資源,對於一些不常變動的資源,瀏覽器會將他們保存在本地記憶體中,下次造訪時直接載入這些資源,提高存取速度。
  • 如何知道資源是請求的伺服器還是讀取的快取呢?
看上面這張圖,有些資源的size值是大小,有些是,有些是,顯示大小的是請求的伺服器資源,而顯示後面兩種的則是讀取的快取。 disk cache: 是將資源儲存在磁碟中,等待下次存取時不需重新下載,直接從磁碟中讀取,它的直接操作物件為CurlCacheManagermemory cache:##-memory cache disk cache相同點只能儲存一些衍生類別資源檔案只能儲存一些衍生類別資源檔
from disk cachefrom memory cache
。 (效率比記憶體快取慢,但儲存容量大,儲存時間長) 是將資源快取到記憶體中,等待下次造訪時不需重新下載,直接從記憶體中讀取。 (從效率上看它是最快的,從存活時間來看,它是最短的。)
#########不同點######退出進程時資料會被清除######退出進程時資料不會被清除### #########儲存資源######一般腳本、字體、圖片會存在記憶體當中######一般非腳本會存在記憶體當中,如css等###### ######

瀏覽器快取分類

  • 強快取
  • 協商快取

##瀏覽器向伺服器請求資源時,先判斷是否命中強緩存,沒命中再判斷是否命中協商快取

強快取

#瀏覽器在載入資源時,會先根據本地快取資源的header中判斷是否命中強緩存,如果命中則直接使用快取中的資源,不會再向伺服器發送請求。 (這裡的header中的信息指的是expirescache-control

  • Expires

該欄位是http1.0 時的規範,它的值為一個

絕對時間
    的GMT 格式的時間字符串,如Expires:Mon,18 Oct 2066 23:59:59 GMT。這個時間代表著這個資源的失效時間,在此時間之前,也就是命中快取。這種方式有一個明顯的缺點,由於失效時間是一個絕對時間,所以當伺服器與客戶端時間偏差較大時,就會導致快取混亂。所以這種方式很快就在後來的HTTP1.1版本中被拋棄了。
  • Cache-Control

#Cache-Control 是http1.1 時出現的header訊息,主要是利用該欄位的max-age 值來進行判斷,它是一個相對時間,例如

Cache-Control:max-age=3600

,代表資源的有效期限是3600 秒。 cache-control 除了該欄位外,還有下面幾個比較常用的設定值:

no-cache

:需要進行協商緩存,發送請求到伺服器確認是否使用快取。

no-store

:禁止使用緩存,每次都要重新請求資料。

public

:可以被所有的使用者緩存,包括終端使用者和 CDN 等中間代理伺服器。

private

:只能被終端使用者的瀏覽器緩存,不允許 CDN 等中繼快取伺服器對其快取。

Cache-Control 與 Expires 可以在服務端設定同時啟用,同時啟用的時候 Cache-Control 優先權高。

協商快取

當強快取沒命中時,瀏覽器會傳送一個請求到伺服器,伺服器根據 header 中的資訊來判斷是否命中協商快取。如果命中,則傳回304 ,告訴瀏覽器資源未更新,可以使用本機快取。 (這裡的header訊息指的是Last-Modify/If-Modify-SinceETag/If-None-Match

  • #Last-Modify/If-Modify-Since

#瀏覽器第一次要求資源的時候,伺服器傳回的header 中會加上Last-Modify,Last-modify 是一個時間標識該資源的最後修改時間。

當瀏覽器再次要求該資源時,request 的請求頭中會包含 If-Modify-Since,該值為快取先前傳回的 Last-Modify。伺服器收到 If-Modify-Since 後,根據資源的最後修改時間判斷是否命中快取。

如果命中緩存,則傳回 304,並且不會傳回資源內容,並且不會傳回 Last-Modify。

缺點:

短時間內資源發生了改變,Last-Modified 並不會改變。

    週期性變化。如果這個資源在一個週期內修改回原來的樣子了,我們認為是可以使用快取的,但是 Last-Modified 可不這樣認為,因此便有了 ETag。
  • ETag/If-None-Match

#與Last-Modify/If-Modify-Since 不同的是,Etag/If-None-Match 傳回的是一個校驗碼。 ETag 可以保證每一個資源是唯一的,資源變化都會導致 ETag 改變。伺服器根據瀏覽器上送的 If-None-Match 值來判斷是否命中快取。

與 Last-Modified 不一樣的是,當伺服器傳回 304 Not Modified 的回應時,由於 ETag 重新產生過,response header 中還會把這個 ETag 傳回,即使這個 ETag 跟之前的沒有變化。

Last-Modified 與 ETag 是可以一起使用的,伺服器會優先驗證 ETag,一致的情況下,才會繼續比對 Last-Modified,最後才決定是否回傳 304。

總結

當瀏覽器存取一個已經造訪過的資源是,它的步驟是:

1.先看是否命中強緩存,命中?的話直接使用緩存

2.沒命中強緩存,則會發送請求到伺服器看是否命中?協商緩存

3.如果命中了協商緩存,伺服器會返回304告訴瀏覽器可以使用本地緩存

###4.沒命中協商緩存,則伺服器會返回新的資源給瀏覽器###

13.什么是浏览器的同源策略,以及跨域?

同源策略

同源策略是浏览器的一种自我保护行为。所谓的同源指的是:协议,域名,端口均要相同

浏览器中大部分内容都是受同源策略限制的,但是以下三个标签不受限制:

<img  src="..." / alt="看看這些關於瀏覽器的面試題,你能答對幾個?" >
<link href="..." />
<script src="..."></script>

跨域

跨域指的是浏览器不能执行其它域名下的脚本。它是由浏览器的同源策略限制的。

你可能会想跨域请求到底有没有发送到服务器?

事实上,跨域请求时能够发送到服务器的,并且服务器也能过接受的请求并正常返回结果,只是结果被浏览器拦截了。

跨域解决方案(列出几个常用的)

  • JSONP

它主要是利用script标签不受浏览器同源策略的限制,可以拿到从其他源传输过来的数据,需要服务端支持。

优缺点:

兼容性比较好,可用于解决主流浏览器的跨域数据访问的问题。缺点就是仅支持get请求,具有局限性,不安全,可能会受到XSS攻击。

思路:

  • 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。
  • 创建一个<script></script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。
  • 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show('南玖')
  • 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。
// front
function jsonp({ url, params, callback }) {
  return new Promise((resolve, reject) => {
    let script = document.createElement(&#39;script&#39;)
    window[callback] = function(data) {
      resolve(data)
      document.body.removeChild(script)
    }
    params = { ...params, callback } // wd=b&callback=show
    let arrs = []
    for (let key in params) {
      arrs.push(`${key}=${params[key]}`)
    }
    script.src = `${url}?${arrs.join(&#39;&&#39;)}`
    document.body.appendChild(script)
  })
}
jsonp({
  url: &#39;http://localhost:3000/say&#39;,
  params: { wd: &#39;wxgongzhonghao&#39; },
  callback: &#39;show&#39;
}).then(data => {
  console.log(data)
})
// server 借助express框架
let express = require(&#39;express&#39;)
let app = express()
app.get(&#39;/say&#39;, function(req, res) {
  let { wd, callback } = req.query
  console.log(wd) // Iloveyou
  console.log(callback) // show
  res.end(`${callback}(&#39;关注前端南玖&#39;)`)
})
app.listen(3000)

上面这段代码相当于向http://localhost:3000/say?wd=wxgongzhonghao&callback=show这个地址请求数据,然后后台返回show('关注前端南玖'),最后会运行show()这个函数,打印出'关注前端南玖'

  • 跨域资源共享(CORS)

CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。

CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现

浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求复杂请求

简单请求: (满足以下两个条件,就是简单请求)

1.请求方法为以下三个之一:

  • GET
  • POST
  • HEAD

2.Content-Type的为以下三个之一:

  • text-plain
  • multiparty/form-data
  • application/x-www-form-urlencoded

复杂请求:

不是简单请求那它肯定就是复杂请求了。复杂请求的CORS请求,会在正式发起请求前,增加一次HTTP查询请求,称为预检 请求,该请求是option方法的,通过该请求来知道服务端是否允许该跨域请求。

Nginx反向代理

Nginx 反向代理的原理很简单,即所有客户端的请求都必须经过nginx处理,nginx作为代理服务器再将请求转发给后端,这样就规避了浏览器的同源策略。

14.说说什么是XSS攻击

什么是XSS?

XSS 全称是 Cross Site Scripting,为了与css区分开来,所以简称XSS,中文叫作跨站脚本

XSS是指黑客往页面中注入恶意脚本,从而在用户浏览页面时利用恶意脚本对用户实施攻击的一种手段。

XSS能够做什么?

  • 窃取Cookie
  • 监听用户行为,比如输入账号密码后之间发给黑客服务器
  • 在网页中生成浮窗广告
  • 修改DOM伪造登入表单

XSS实现方式

  • 存储型XSS攻击
  • 反射型XSS攻击
  • 基于DOM的XSS攻击

如何阻止XSS攻击?

对输入脚本进行过滤或转码

对用户输入的信息过滤或者转码,保证用户输入的内容不能在HTML解析的时候执行。

利用CSP

该安全策略的实现基于一个称作 Content-Security-Policy的HTTP首部。(浏览器内容安全策略)它的核心思想就是服务器决定浏览器加载那些资源。

  • 限制加载其他域下的资源文件,这样即使黑客插入了一个 JavaScript 文件,这个 JavaScript 文件也是无法被加载的;
  • 禁止向第三方域提交数据,这样用户数据也不会外泄;
  • 提供上报机制,能帮助我们及时发现 XSS 攻击。
  • 禁止执行内联脚本和未授权的脚本;

利用 HttpOnly

由于很多 XSS 攻击都是来盗用 Cookie 的,因此还可以通过使用 HttpOnly 属性来保护我们 Cookie 的安全。这样子的话,JavaScript 便无法读取 Cookie 的值。这样也能很好的防范 XSS 攻击。

通常服务器可以将某些 Cookie 设置为 HttpOnly 标志,HttpOnly 是服务器通过 HTTP 响应头来设置的,下面是打开 Google 时,HTTP 响应头中的一段:

set-cookie: NID=189=M8l6-z41asXtm2uEwcOC5oh9djkffOMhWqQrlnCtOI; expires=Sat, 18-Apr-2020 06:52:22 GMT; path=/; domain=.google.com; HttpOnly

对于不受信任的输入,可以限制输入长度

15.说说什么是CSRF攻击?

什么是CSRF攻击?

CSRF 全称 Cross-site request forgery,中文为跨站请求伪造 ,攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。 CSRF攻击就是黑客利用用户的登录状态,并通过第三方站点来干一些嘿嘿嘿的坏事

几种常见的攻击类型

1.GET类型的CSRF

GET类型的CSRF非常简单,通常只需要一个HTTP请求:

 <img  src="http://bank.example/withdraw?amount=10000&for=hacker"  alt="看看這些關於瀏覽器的面試題,你能答對幾個?" >

在受害者访问含有这个img的页面后,浏览器会自动向http://bank.example/withdraw?account=xiaoming&amount=10000&for=hacker发出一次HTTP请求。bank.example就会收到包含受害者登录信息的一次跨域请求。

2.POST类型的CSRF

这种类型的CSRF利用起来通常使用的是一个自动提交的表单,如:

 <form action="http://bank.example/withdraw" method=POST>
    <input type="hidden" name="account" value="xiaoming" />
    <input type="hidden" name="amount" value="10000" />
    <input type="hidden" name="for" value="hacker" />
</form>
<script> document.forms[0].submit(); </script>

访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作。

3.链接类型的CSRF

链接类型的CSRF并不常见,比起其他两种用户打开页面就中招的情况,这种需要用户点击链接才会触发。这种类型通常是在论坛中发布的图片中嵌入恶意链接,或者以广告的形式诱导用户中招,攻击者通常会以比较夸张的词语诱骗用户点击,例如:

  <a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank">
  重磅消息!!
  <a/>

由于之前用户登录了信任的网站A,并且保存登录状态,只要用户主动访问上面的这个PHP页面,则表示攻击成功。

CSRF的特点

  • 攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。
  • 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据。
  • 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。
  • 跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。

CSRF通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行,而且这种攻击更加危险。

防护策略

駭客只能藉助受害者的cookie 騙取伺服器的信任,但是駭客並不能憑藉拿到**「cookie」**,也看不到**「cookie」的內容。另外,對於伺服器回傳的結果,由於瀏覽器「同源策略」**的限制,駭客也無法進行解析。

這就告訴我們,我們要保護的物件是那些可以直接產生資料改變的服務,而對於讀取資料的服務,則不需要進行CSRF的保護。而保護的關鍵,是「在請求中放入駭客所不能偽造的資訊」

同源偵測

#既然CSRF大多來自第三方網站,那麼我們就直接禁止外域(或不受信任的網域)對我們發起請求。

那麼問題來了,我們要如何判斷請求是否來自外域呢?

在HTTP協定中,每個非同步請求都會攜帶兩個Header,用於標記來源域名:

Origin Header

Referer Header

這兩個Header在瀏覽器發起請求時,大多數情況會自動帶上,並且不能由前端自訂內容。伺服器可以透過解析這兩個Header中的域名,確定請求的來源域。

使用Origin Header確定來源網域名稱在部分與CSRF相關的請求中,請求的Header中會攜帶Origin欄位。欄位內包含請求的網域名稱(不包含path及query)。 如果Origin存在,那麼直接使用Origin中的欄位確認來源網域就可以。 但是Origin在以下兩種情況下並不存在:IE11同源策略:MDN Same-origin_policy#IE_Exceptions302重定向: 在302重定向之後Origin不包含在重定向的請求中,因為Origin可能被認為是其他來源的敏感資訊。對於302重定向的情況來說都是定向到新的伺服器上的URL,因此瀏覽器不想將Origin洩漏到新的伺服器上。 根據HTTP協議,在HTTP頭中有一個欄位叫Referer,記錄了該HTTP請求的來源位址。對於Ajax請求,圖片和script等資源請求,Referer為發起請求的頁面位址。對於頁面跳轉,Referer為開啟頁面歷史記錄的前一個頁面位址。因此我們使用Referer中連結的Origin部分可以得知請求的來源網域。 這種方法並非萬無一失,Referer的值是由瀏覽器提供的,雖然HTTP協定上有明確的要求,但是每個瀏覽器對於Referer的具體實作可能有差別,並不能保證瀏覽器自身沒有安全漏洞。使用驗證 Referer 值的方法,就是把安全性都依賴第三方(即瀏覽器)來保障,從理論上來講,這樣並不是很安全。在部分情況下,攻擊者可以隱藏,甚至修改自己請求的Referer。 2014年,W3C的網路應用程式安全工作小組發布了Referrer Policy草案,對瀏覽器該如何發送Referer做了詳細的規定。截止現在新版瀏覽器大部分已經支持了這份草案,我們終於可以靈活地控制自己網站的Referer策略了。新版的Referrer Policy規定了五種Referer策略:No Referrer、No Referrer When Downgrade、Origin Only、Origin When Cross-origin、和 Unsafe URL。之前就存在的三種策略:never、default和always,在新標準裡換了個名稱。他們的對應關係如下:策略名稱
IE 11 不會在跨站CORS請求上新增Origin標頭,Referer頭將仍然是唯一的識別。最根本原因是因為IE 11對同源的定義和其他瀏覽器有不同,有兩個主要的區別,可以參考
使用Referer Header確定來源網域
屬性值(新) 屬性值(舊)
#No Referrer no-Referrer never
No Referrer When Downgrade### ###no-Referrer-when-downgrade######default############Origin Only######(same or strict) origin#######origin ############Origin When Cross Origin######(strict) origin-when-crossorigin######-#############Unsafe URL######unsafe-url######always#############

根据上面的表格因此需要把Referrer Policy的策略设置成same-origin,对于同源的链接和引用,会发送Referer,referer值为Host不带Path;跨域访问则不携带Referer。例如:aaa.com引用bbb.com的资源,不会发送Referer。

设置Referrer Policy的方法有三种:

  • 在CSP设置

  • 页面头部增加meta标签

  • a标签增加referrerpolicy属性

上面说的这些比较多,但我们可以知道一个问题:攻击者可以在自己的请求中隐藏Referer。如果攻击者将自己的请求这样填写:

 <img  src="http://bank.example/withdraw?amount=10000&for=hacker" referrerpolicy="no-referrer" alt="看看這些關於瀏覽器的面試題,你能答對幾個?" >

那么这个请求发起的攻击将不携带Referer。

另外在以下情况下Referer没有或者不可信:

1.IE6、7下使用window.location.href=url进行界面的跳转,会丢失Referer。

2.IE6、7下使用window.open,也会缺失Referer。

3.HTTPS页面跳转到HTTP页面,所有浏览器Referer都丢失。

4.点击Flash上到达另外一个网站的时候,Referer的情况就比较杂乱,不太可信。

无法确认来源域名情况

当Origin和Referer头文件不存在时该怎么办?如果Origin和Referer都不存在,建议直接进行阻止,特别是如果您没有使用随机CSRF Token(参考下方)作为第二次检查。

如何阻止外域请求

通过Header的验证,我们可以知道发起请求的来源域名,这些来源域名可能是网站本域,或者子域名,或者有授权的第三方域名,又或者来自不可信的未知域名。

我们已经知道了请求域名是否是来自不可信的域名,我们直接阻止掉这些的请求,就能防御CSRF攻击了吗?

且慢!当一个请求是页面请求(比如网站的主页),而来源是搜索引擎的链接(例如百度的搜索结果),也会被当成疑似CSRF攻击。所以在判断的时候需要过滤掉页面请求情况,通常Header符合以下情况:

Accept: text/html
Method: GET

但相应的,页面请求就暴露在了CSRF的攻击范围之中。如果你的网站中,在页面的GET请求中对当前用户做了什么操作的话,防范就失效了。

例如,下面的页面请求:

GET https://example.com/addComment?comment=XXX&dest=orderId

注:这种严格来说并不一定存在CSRF攻击的风险,但仍然有很多网站经常把主文档GET请求挂上参数来实现产品功能,但是这样做对于自身来说是存在安全风险的。

另外,前面说过,CSRF大多数情况下来自第三方域名,但并不能排除本域发起。如果攻击者有权限在本域发布评论(含链接、图片等,统称UGC),那么它可以直接在本域发起攻击,这种情况下同源策略无法达到防护的作用。

综上所述:同源验证是一个相对简单的防范方法,能够防范绝大多数的CSRF攻击。但这并不是万无一失的,对于安全性要求较高,或者有较多用户输入内容的网站,我们就要对关键的接口做额外的防护措施。

CSRF Token

前面讲到CSRF的另一个特征是,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用Cookie中的信息。

而CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。

利用Cookie的SameSite属性

可以看看MDN对此的解释:

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie/SameSite

SameSite可以设置为三个值,StrictLaxNone

  • Strict模式下,浏览器完全禁止第三方请求携带Cookie。比如请求sanyuan.com网站只能在sanyuan.com域名当中请求才能携带 Cookie,在其他网站请求都不能。

  • Lax模式,就宽松一点了,但是只能在 get 方法提交表单况或者a 标签发送 get 请求的情况下可以携带 Cookie,其他情况均不能。

  • 在None模式下,Cookie将在所有上下文中发送,即允许跨域发送。

更多编程相关知识,请访问:编程入门!!

以上是看看這些關於瀏覽器的面試題,你能答對幾個?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除