React 的一些優(yōu)秀安全實(shí)踐
React.js、Vue.js 這些現(xiàn)代的前端框架默認(rèn)已經(jīng)對安全做了非常多的考慮,但是這仍然不能阻礙我們寫出一些安全漏洞 。。。因?yàn)榭蚣苡肋h(yuǎn)不能完全限制我們編程的靈活性,只要有一定的靈活性存在就意味著有安全風(fēng)險(xiǎn)。
下面我就帶大家一起來看一下,為了保證我們 React 應(yīng)用的安全性,有哪些值得遵循的優(yōu)秀實(shí)踐。
dangerouslySetInnerHTML
React? 會對默認(rèn)的數(shù)據(jù)綁定({}?)進(jìn)行自動轉(zhuǎn)義來防止 XSS? 攻擊,所有數(shù)據(jù)都會認(rèn)為是 textContent:
但是為了保障開發(fā)的靈活性,它也給我們提供了一些直接渲染 HTML? 的方法,比如 dangerouslySetInnerHTML:
在把數(shù)據(jù)傳入 dangerouslySetInnerHTML? 之前,一定要確保數(shù)據(jù)是經(jīng)過過濾或轉(zhuǎn)義的,比如可以通過 dompurify.sanitize 進(jìn)行過濾:
import dompurify from "dompurify";
import "./styles.css";
export default function App() {
const code = "<input onfocus=alert(1) autofocus />";
return (
<div className="App">
<div dangerouslySetInnerHTML={{ __html: dompurify.sanitize(code) }} />
</div>
);
}
避免直接操作 DOM 注入 HTML
除了 dangerouslySetInnerHTML? ,我們當(dāng)然還可以直接通過原生的 DOM API? 來插入 HTML:
另外也可以通過 ref? 來訪問 DOM? 來插入 HTML:
這兩個操作都是相當(dāng)危險(xiǎn)的操作,推薦大家既然用了 React? 就要盡量用 React? 的編寫方式來寫代碼,盡量不要直接操作 DOM?,如果你確實(shí)要渲染富文本,還是推薦用上面提到的 dangerouslySetInnerHTML,而且數(shù)據(jù)要經(jīng)過過濾或轉(zhuǎn)義。
服務(wù)端渲染
當(dāng)使用服務(wù)端渲染函數(shù)時,數(shù)據(jù)綁定也會提供自動內(nèi)容轉(zhuǎn)義,比如 ReactDOMServer.renderToString()? 和 ReactDOMServer.renderToStaticMarkup()。
在將字符串發(fā)送給客戶端進(jìn)行注水之前,避免將字符串直接拼接到 renderToStaticMarkup() 的輸出上。
為了避免 XSS?,不要將未過濾的數(shù)據(jù)與 renderToStaticMarkup() 的輸出連接在一起:
app.get("/", function (req, res) {
return res.send(
ReactDOMServer.renderToStaticMarkup(
React.createElement("h1", null, "Hello ConardLi!")
) + otherData
);
});
JSON 注入
將 JSON? 數(shù)據(jù)與服務(wù)器端渲染的 React? 頁面一起發(fā)送是很常見的。始終對 < 字符進(jìn)行轉(zhuǎn)義來避免注入攻擊:
window.JSON_DATA = ${JSON.stringify(jsonData).replace( /</g, '\\u003c')}
URL 注入
前面幾個基本上都是直接渲染未經(jīng)過濾的富文本導(dǎo)致的 XSS?,實(shí)際上通過 URL? 偽協(xié)議也可以執(zhí)行 javascript 腳本:
因此所有需要注入到代碼里的 URL? 參數(shù),我們都要做好 URL 的合法性驗(yàn)證,千萬不要直接注入進(jìn)去:
import "./styles.css";
function isSafe(url) {
if (url && !url.startsWith("http")) {
return null;
}
// ... 其他判斷
}
export default function App() {
const code = "javascript:alert('xss')";
return (
<div className="App">
測試 URL 注入
<a href={isSafe(code)}>一個平平無奇的鏈接</a>
</div>
);
}
避免有漏洞的 React 版本
React? 以前也被測試出有比較高危的安全漏洞,建議經(jīng)常保持更新,來避免這些有漏洞的 React 版本:
避免有漏洞的其他依賴
一般我們的項(xiàng)目都會依賴大量的開源代碼,有時漏洞并不是我們寫出來的,而是這些依賴帶進(jìn)來的,因此我們無論使用任何框架,定期進(jìn)行依賴更新都是不錯的選擇。
Eslint React 安全配置
推薦大家通過 Eslint? 的 React 安全配置(https://github.com/snyk-labs/eslint-config-react-security/)來對代碼進(jìn)行約束,它會自動幫助我們發(fā)現(xiàn)一些代碼中的安全風(fēng)險(xiǎn)。