web安全是前端開發者們須要關注和掌握的必要內容。在寫該記錄以前,我也老是對安全策略這方面點到爲止;可是在真正瞭解以後才發現,頁面能安全活到如今也算是老天和後臺、運維同事的關照了。😂javascript
本次記錄主要有三點:XSS、CSRF、CSP、請求劫持和https。html
這一篇,會詳細記錄xss的攻擊和防護。另外兩點會分爲兩篇記錄文:詳解二,詳解三和詳解四。前端
XSS(cross-site scripting)跨站腳本攻擊是指頁面被注入惡意代碼。java
例1:根據請求接口URL參數決定頁面展現內容時:web
<input type="text" value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
您搜索的關鍵詞是:<%= getParameter("keyword") %>
</div>複製代碼
getParameter('keyword')是http請求的函數ajax
url:http://xxx/search?keyword="><script>alert('xss');</script>"數據庫
參數keyword會被從新拼接到HTML中:編程
<input type="text" value=""><script>alert('XSS');</script>"> <button>搜索</button><div> 您搜索的關鍵詞是:"><script>alert('XSS');</script> </div>複製代碼
惡意代碼將被執行。後端
面對這種狀況如何防範?瀏覽器
這裏的緣由是,瀏覽器把用戶的輸入當成腳本執行了,那麼將這段內容轉成文本就行。
<input type="text" value="<%= escapeHTML(getParameter("keyword")) %>">
<button>搜索</button>
<div>
您搜索的關鍵詞是:<%= escapeHTML(getParameter("keyword")) %>
</div>複製代碼
excapeHTML()按照如下規則進行轉譯:
|字符|轉義後的字符| |-|-| |&
|&
| |<
|<
| |>
|>
| |"
|"
| |'
|'
| |/
|/
|
經轉譯後,最終瀏覽器接收到的響應爲:
<input type="text" value=""><script>alert('XSS');</script>">
<button>搜索</button>
<div>
您搜索的關鍵詞是:"><script>alert('XSS');</script>
</div>複製代碼
例2:a標籤的href屬性
<a href="<%= escapeHTML(getParameter("redirect_to")) %>">跳轉...</a>複製代碼
getParameter('redirect_to')爲URL http://xxx/?redirect_to=javascript:alert('XSS')
即便作了轉譯,javascript:仍然是正確的href屬性值,包括在javascript:前面加空格%20,也依然會跳過HTML轉譯檢查。
解決辦法:白名單過濾,禁止'javascript:'連接、非法schema等。
例3:把數據經過JSON的方式內聯到HTML中
<script>
var initData = <%= data.toJSON() %>
</script>複製代碼
這時不能使用escapeHTML(),由於轉譯"後,JSON格式會被破壞。
可是內聯JSON也有不安全的地方:
U+2028
或 U+2029
這兩個字符時,不能做爲 JavaScript 的字面量使用,不然會拋出語法錯誤。</script>
時,當前的 script 標籤將會被閉合,後面的字符串內容瀏覽器會按照 HTML 進行解析;經過增長下一個 <script>
標籤等方法就能夠完成注入。所以,須要escapeEmbedJSON()函數,對內聯JSON進行轉義:
|字符|轉義後的字符| |-|-| |U+2028
|\u2028
| |U+2029
|\u2029
| |<
|\u003c
|
利用惡意腳本攻擊,攻擊者能夠獲取用戶的敏感信息如Cookie、SessionID等。
用戶輸入行爲,如下內容都不可信:
根據攻擊來源,能夠分爲存儲型、反射型和DOM型三類。
|類型|存儲區|插入點|
|存儲型 XSS|後端數據庫|HTML| |反射型 XSS|URL|HTML| |DOM 型 XSS|後端數據庫/前端存儲/URL|前端 JavaScript
存儲型XSS攻擊步驟:
1. 攻擊者將惡意代碼提交到目標網站數據庫
2. 用戶打開目標網站時,網站服務端將惡意代碼從數據庫提出,拼接到HTML中返回給瀏覽器
3. 用戶瀏覽器接收到響應後解析執行,惡意代碼也被執行
4. 惡意代碼竊取用戶數據併發送到攻擊者網站,或者冒充用戶行爲,調用目標網站接口執行惡意操做
這種攻擊常見於帶有用戶保存數據的網站功能,如論壇發帖、商品評論、用戶私信等。
反射型XSS攻擊步驟:
1. 攻擊者構造出包含惡意代碼的URL
2. 用戶打開含惡意代碼的URL,網站服務端取出惡意代碼並拼接到HTML返回瀏覽器
3. 瀏覽器解析執行,竊取用戶數據或冒充用戶行爲
與存儲型的區別,反射型的惡意代碼存儲在URL,存儲型的存儲在數據庫。反射型XSS漏洞常見於經過URL傳參的功能,如網站搜索、跳轉等。
因爲須要用戶主動打開,因此會有多種誘導用戶點擊的手段。POST的內容也能夠觸發反射型XSS,只不過須要構造表單提交頁面引導用戶點擊,因此少見。
DOM型攻擊步驟:
1. 攻擊者構造出包含惡意代碼的URL
2. 用戶打開該URL
3. 瀏覽器接收到響應後解析執行,前端JS取出URL中的惡意代碼並執行。
4. 惡意代碼竊取用戶數據併發送到攻擊者網站,或者冒充用戶行爲,調用目標網站接口執行惡意操做
DOM型XSS屬於前端Javascript自身的安全漏洞,而前兩種屬於服務端的安全漏洞。
據上所屬,XSS攻擊主要有兩大要素:
1. 攻擊者提交惡意代碼
2. 瀏覽器執行惡意代碼
從一開始的案列中得知,最簡單的是對用戶輸入文本的轉義。可是也知道轉義存在弊端。
除此轉義以外,也能夠對一些必要的輸入作檢查,如電話號碼、數字、URL、郵件地址等。
預防存儲型和反射型XSS
存儲型和反射型都是在服務端取出惡意代碼後,插入html的,被瀏覽器執行。因此常見的預防方式有兩種:改爲純前端渲染,把代碼和數據分隔開;對html作充分轉義。
純前端渲染:瀏覽器先加載靜態頁面(不包含任何業務相關的數據),再執行javascript,經過ajax加載業務數據,調用DOM API更新到頁面上。純前端渲染中,瀏覽器會明確文本(.innerText),屬性(.setAttribute),仍是樣式(.style)等等。但仍須注意避免DOM型XSS(請參考下文‘預防DOM型XSS攻擊’)。
不少內部、管理系統中,適合使用純前端渲染;但對於性能要求高的,或有SEO需求的頁面,拼接HTML的問題仍存在。
轉義HTML:除了上面說到的excapeHTML()等方法外,還能夠直接使用模板引擎,如ejs、doT.js、FreeMarker等,一般就是把& < > " ' /這些轉義掉,確實能起到必定的轉義做用,但並不完善。因此還能夠結合後臺編程語言,找到合適的轉義庫。如JAVA工程裏,經常使用的轉義庫org.owasp.encode,不一樣上下文要使用相應的轉移規則。
預防DOM型XSS攻擊
DOM型XSS攻擊就與前段javascript代碼自己是否嚴謹有關。
儘可能避免使用.innerHTML、outerHTML、document.write(),而換成.textContent、.setAttribute()等。
若是用Vue/React技術棧,而且不使用v-html/dangerouslySetInnerHTML功能,就在前段render階段避免innerHTL、outerHTML的XSS隱患。
DOM 中的內聯事件監聽器,如 location
、onclick
、onerror
、onload
、onmouseover
等,<a>
標籤的 href
屬性,JavaScript 的 eval()
、setTimeout()
、setInterval()
等,都能把字符串做爲代碼運行。若是不可信的數據拼接到字符串中傳遞給這些 API,很容易產生安全隱患,請務必避免。
其餘XSS預防措施
Content Security Policy,俗稱csp,在http請求時可在請求頭中顯示。做用:
輸入內容長度控制。
cookie的http-only限制:cookie只容許同域http請求攜帶,不容許讀取和修改。
驗證碼:圖片驗證碼、短信驗證碼等等。
通用XSS攻擊字符串手動檢測。
掃描工具自動檢測,如Arachni、Mozilla HTTP Observatory、w3af等。
雖然很難經過技術手段徹底避免XSS,但能夠儘可能減小漏洞的發生:
<%= data %>
而不是 <%- data %>
; 在 doT.js 中,儘可能使用 {{! data }
而不是 {{= data }
; 在 FreeMarker 中,確保引擎版本高於 2.3.24,而且選擇正確的 freemarker.core.OutputFormat
。onLoad="onload('{{data}}')"
、onClick="go('{{action}}')"
這種拼接內聯事件的寫法。在 JavaScript 中經過 .addEventlistener()
事件綁定會更安全。createElement
、setAttribute
之類的方法實現。或者採用比較成熟的渲染框架,如 Vue/React 等。