????前端安全一直是一個蠻嚴苛的問題霍殴,特別如果設計到money更是如此单寂。了解前端安全,在平時的coding中主動考慮笑诅,防范于未然调缨,是一個有追求的程序猿應該做的疮鲫。
未登錄
????我們從弱弱的基本開始,第一步當然是登錄鑒權了弦叶,如果一個需要用戶身份鑒權的應用系統(tǒng)沒有登錄過濾那簡直是沒法想像的俊犯,方案基本都是用戶輸入用戶名密碼、或是三方 openID 授權后在 session 里保存用戶此次登錄的憑證來確保每次請求的合法性伤哺。
????由于 session有時效限制燕侠,所以若用戶一段時間未與服務器交互則會過期重登,當然我們也可以通過把登錄憑證存在 cookie 里來自由控制用戶登錄的有效時間立莉。這個是最基本的鑒權我們就不深入細節(jié)绢彤。
登錄了,但被CSRF
????雖然有了登錄驗證后桃序,我們可以擋掉其他非登錄用戶的騷擾了杖虾,但悲劇的是壞人們還是可以欺騙我們善良的用戶,借已登錄用戶的手來搞破壞媒熊。即 CSRF(Cross-site request forgery)跨站請求偽造奇适。
舉個例子:
????有個黑客的網(wǎng)站 h.com,我們的網(wǎng)站 a.com芦鳍。用戶登錄了a.com嚷往,但被誘點進入h.com(如收到 QQ 消息或郵件傳播的h.com 的鏈接),當用戶訪問這個鏈接時柠衅,h.com 上自動發(fā)送一個請求到 a.com皮仁,由于用戶已登錄a.com,瀏覽器根據(jù)同源策略菲宴,會在該請求上自動附帶了 cookie贷祈,而前面我們提到了鑒權是通過 cookie 里的某個 key 值憑證的,所以如若沒有判斷該請求的來源合法性喝峦,我們則通過了該偽造的請求势誊,執(zhí)行了相應的操作。比如這個請求是讓該用戶發(fā)一篇日志谣蠢,或是發(fā)微博粟耻,或是嚴重的發(fā)起一筆轉賬。
常見的諸如放一張看不見的圖片發(fā)起get請求
<img src=http://www.a.com/Transfer.phptoUserId=999&money=1000000>
????post 請求會稍微麻煩些眉踱,但同樣很好實現(xiàn)挤忙,可以構造一個表誘導用戶點擊,也可以直接利用ajax發(fā)送post請求谈喳。
????要防住此類偽造請求我們第一反應都是檢查這個請求的來源册烈,確實,在上述的情形下發(fā)來的請求報文里referer字段的網(wǎng)址不是我們的自己站點叁执,
而會是一個三方的茄厘,如上假設的 h.com矮冬。但是很多情況下,referer并不完全靠譜次哈,比如如果眾多二級域名之間需要通信胎署,那么referer可能會
設得比較泛,如*.a.com窑滞∏砟粒或是歷史原因一些 referer 為空的請求會漏過校驗等。所以這種方式只是提高了破解成本哀卫,并不能完全杜絕巨坊。
????現(xiàn)在業(yè)界比較通用的解決方案還是在每個請求上附帶一個anti-CSRF token。
????將sessionid加鹽再散列處理此改。然后一起發(fā)送給后端趾撵。服務器端拿到 token 后用相同的算法對 sid 運算后匹對,相同則為已登錄用戶發(fā)出請求共啃,沒有或不對等則說明該請求是偽造的占调。
token = MD5( sid * salt )
????其實這個算法的精髓在于使用了 cookie 中的 sid(用戶登錄后我們服務器種的 cookie 憑證),因為前端的代碼對用戶而言都是沒有秘密的移剪,只要花點時
間即可推算出我們的算法究珊,但由于攻擊者無法登錄,又拿不到 cookie 里的 sid(根據(jù)瀏覽器的同源策略纵苛,在 h.com 上無法獲取屬于 a.com 的 cookie)剿涮,所以無法構造出 token。
????至于加 MD5當然是因為我們不會傻的把登錄憑證 sid 放到 url 上給人直接拿了登錄- -(以前還真有人干過)攻人,為什么要加 鹽 salt 則是怕簡單的一層 MD5還是有可能被通過撞庫的方式解出 sid取试,當然加了 salt 也不意味著100%防住,只是大大提高了破解的成本而已怀吻。
有防 CSRF了想括,但被 XSS
從上面我們知道防住 CSRF 最關鍵的是要守住 cookie,如果用戶的 cookie 被人竊取了烙博,那上面的防護就形同虛設了。而 XSS 就可以很輕易的獲取用戶的 cookie烟逊,
所以有句話叫Buy one XSS, get a CSRF for free渣窜。
用戶輸入的內(nèi)容原封不動的通過服務器程序渲染在頁面上 。
反射型
舉個栗子
前端get一個請求:
后臺處理:
<?php echo 'Hello' . $_GET['name'];
代碼本意是根據(jù)queryString 的 name 來動態(tài)展示用戶名宪躯,但由于未對 name 做編碼校驗乔宿,當鏈接為:
www.a.com?xss.php?name=<script>alert(document.cookie);</script>
這時訪問這個鏈接則會彈出我們的 cookie 內(nèi)容,如果這時候再把 alert 改為一個發(fā)送函數(shù)访雪,則可把 cookie 偷走详瑞。
前端DOM-Based XSS
<script>
document.getElementById('intro-div').innerHTML = document.location.href.substring(document.location.href.indexOf("intro=")+6);
</script>
如上掂林,直接將用戶的輸出輸出到頁面標簽中。但是如果將鏈接中的內(nèi)容設置為
http://www.a.com/index.html?intro=<script>alert(document.cookie)</script>
那我們的 cookie 又沒了坝橡。
持久型XSS
也稱為存儲型 XSS泻帮,注入腳本跟 XSS 大同小異,只是腳本不是通過瀏覽器->服務器->瀏覽器這樣的反射方式计寇,而是多發(fā)生在富文本編輯器锣杂、日志、留言番宁、配置系統(tǒng)等數(shù)據(jù)庫保存用戶輸入內(nèi)容的業(yè)務場景元莫。即用戶的注入腳本保存到了數(shù)據(jù)庫里,其他用戶只要一訪問到都會中招蝶押。
前端get一個請求:
www.a.com/xss.php?name=<script>alert(document.cookie);</script>
后臺處理:
<?php $db.set("name", $_GET["name"]);
前端請求的頁面:
<?php echo 'Hello' . $db.get['name'];
這樣但凡請求了該頁面的都會被XSS攻擊到踱蠢。
解決XSS
????從上面我們可以看出各種攻擊手段很重要的一點就是要獲取 cookie,有了 cookie 就相當于獲取了我們用戶的身份信息棋电,所以自然的我們要保護我們的 cookie茎截。在 cookie 里有個 HttpOnly 屬性可以讓頁面無法通過 JS 來讀寫 cookie。
res.cookie('a', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
????開啟這個屬性后 document 將無法獲取 cookie离陶。當然這個方法也不是萬能的稼虎,我們的 cookie 還是會在 header 中,還是有其他手段去獲取 header 中的 cookie招刨,
不過使用后我們還是提高了攻擊的成本霎俩。關鍵還是我們要不相信用戶的一切輸入,對編碼輸出在頁面中會破壞原有代碼(HTML沉眶、JavaScript甚至WML等)規(guī)則的特殊字符以及對某些標簽的某些屬性進行白名單檢查打却。
XSS防護也做了,被用戶SQL注入
舉個例子:
功能是查詢userId為123的用戶出來,這個請求到我們服務端最后sql語句是這樣:
select * from users where userid=123
如果不做任何校驗谎倔,如果用戶輸入如下
123; DROP TABLE users;
嘎嘎柳击,整個表就沒有了。
所以同樣的片习,還是那個原則捌肴,我們不能相信用戶的任何輸入,如果一個sql語句里包含了用戶輸入的內(nèi)容藕咏,那我們要對內(nèi)容做sql安全相關的過濾檢查状知。
同時,使用一些ORM工具孽查,不使用拼湊字符串型的語句執(zhí)行方式饥悴。
總結
????總的來說,前端最重要的就是一個sessionId這個代表用戶身份的憑證,保護好這個憑證西设,同時利用同源策略以及自己加密token來識別用戶瓣铣,最后以最惡意的眼光對待用戶的輸入,不要相信用戶的輸入贷揽。這樣就能屏蔽絕大部分常見的安全問題了棠笑。