Update20151202:
感謝大家的關注和回答,目前我從各種方式瞭解到的防禦方法,整理如下:
PHP直接輸出html的,可以採用以下的方法進行過濾
:
1.htmlspecialchars函數2.htmlentities函數3.HTMLPurifier.auto.php外掛程式4.RemoveXss函數(百度可以查到)
PHP輸出到JS代碼中,或者開發Json API的,則需要前端在JS中進行過濾
:
1.盡量使用innerText(IE)和textContent(Firefox),也就是jQuery的text()來輸出常值內容2.必須要用innerHTML等等函數,則需要做類似php的htmlspecialchars的過濾(參照@eechen的答案)
其它的通用的補充性防禦手段
1.在輸出html時,加上Content Security Policy的Http Header(作用:可以防止頁面被XSS攻擊時,嵌入第三方的指令檔等)(缺陷:IE或低版本的瀏覽器可能不支援)2.在設定Cookie時,加上HttpOnly參數(作用:可以防止頁面被XSS攻擊時,Cookie資訊被盜取,可相容至IE6)(缺陷:網站本身的JS代碼也無法操作Cookie,而且作用有限,只能保證Cookie的安全)3.在開發API時,檢驗請求的Referer參數(作用:可以在一定程度上防止CSRF攻擊)(缺陷:IE或低版本的瀏覽器中,Referer參數可以被偽造)
大概就是這些了,大家還有什麼別的思路,歡迎補充!
——————————————————————————————————————————————————
原問題如下:
1.PHP如何完美(或者儘可能完美地)防禦XSS攻擊(比htmlspecialchars更完善的)?
2.我在想是不是防禦XSS最好在前端做(畢竟JS在前端解析字串都有坑啊)?
3.有木有什麼解決方案或者思路啊,什麼都行?
最近都在研究XSS防禦的問題。
畢竟,比如使用者註冊的API,可能被Hacker利用,強行提交了""這樣的使用者名稱。
然後WEB前端怎麼都要有顯示使用者名稱的地方吧。。。
於是。。。Boom。。。
直入重點:
我看到很多應對XSS的防禦方案都是PHP的htmlentities函數或者htmlspecialchars。
隨意百度了下,貌似ThinkPHP3.x預設就是用的htmlspecialchars。
比如:$str = htmlspecialchars($str, ENT_QUOTES);//替換掉<>&'"這5個字元
但是,只替換掉那幾個字元真的夠嗎?
然後我發現了這個文章:
http://tieba.baidu.com/p/3003719171
使用\u003c\u003e在JS字串中會被解釋成<>的特性進行XSS攻擊。。。
臥槽。。。
然後我想到了JS裡的eval等等函數簡直是無底洞。。。
然後我發現了這個文章:
http://www.2cto.com/Article/201310/251830.html
使用各種編碼,各種手段執行JS,簡直喪心病狂。
比如:
啊!CAO。
我開始懷疑整個世界了。。。
所以,
我的問題是:
1.PHP如何完美(或者儘可能完美地)防禦XSS攻擊(比htmlspecialchars更完善的)?
2.我在想是不是防禦XSS最好在前端做(畢竟JS在前端解析字串都有坑啊)?
3.有木有什麼解決方案或者思路啊,什麼都行?
Update20151201:
能不要再複製粘貼答案,or迷信htmlspecialchars是無敵的了好嘛?
\u003cimg src=1 onerror=alert(/xss/)\u003e裡的任何一個字元都是不會被htmlspecialchars處理的。
自己看圖,對,就是你!
回複內容:
Update20151202:
感謝大家的關注和回答,目前我從各種方式瞭解到的防禦方法,整理如下:
PHP直接輸出html的,可以採用以下的方法進行過濾
:
1.htmlspecialchars函數2.htmlentities函數3.HTMLPurifier.auto.php外掛程式4.RemoveXss函數(百度可以查到)
PHP輸出到JS代碼中,或者開發Json API的,則需要前端在JS中進行過濾
:
1.盡量使用innerText(IE)和textContent(Firefox),也就是jQuery的text()來輸出常值內容2.必須要用innerHTML等等函數,則需要做類似php的htmlspecialchars的過濾(參照@eechen的答案)
其它的通用的補充性防禦手段
1.在輸出html時,加上Content Security Policy的Http Header(作用:可以防止頁面被XSS攻擊時,嵌入第三方的指令檔等)(缺陷:IE或低版本的瀏覽器可能不支援)2.在設定Cookie時,加上HttpOnly參數(作用:可以防止頁面被XSS攻擊時,Cookie資訊被盜取,可相容至IE6)(缺陷:網站本身的JS代碼也無法操作Cookie,而且作用有限,只能保證Cookie的安全)3.在開發API時,檢驗請求的Referer參數(作用:可以在一定程度上防止CSRF攻擊)(缺陷:IE或低版本的瀏覽器中,Referer參數可以被偽造)
大概就是這些了,大家還有什麼別的思路,歡迎補充!
——————————————————————————————————————————————————
原問題如下:
1.PHP如何完美(或者儘可能完美地)防禦XSS攻擊(比htmlspecialchars更完善的)?
2.我在想是不是防禦XSS最好在前端做(畢竟JS在前端解析字串都有坑啊)?
3.有木有什麼解決方案或者思路啊,什麼都行?
最近都在研究XSS防禦的問題。
畢竟,比如使用者註冊的API,可能被Hacker利用,強行提交了""這樣的使用者名稱。
然後WEB前端怎麼都要有顯示使用者名稱的地方吧。。。
於是。。。Boom。。。
直入重點:
我看到很多應對XSS的防禦方案都是PHP的htmlentities函數或者htmlspecialchars。
隨意百度了下,貌似ThinkPHP3.x預設就是用的htmlspecialchars。
比如:$str = htmlspecialchars($str, ENT_QUOTES);//替換掉<>&'"這5個字元
但是,只替換掉那幾個字元真的夠嗎?
然後我發現了這個文章:
http://tieba.baidu.com/p/3003719171
使用\u003c\u003e在JS字串中會被解釋成<>的特性進行XSS攻擊。。。
臥槽。。。
然後我想到了JS裡的eval等等函數簡直是無底洞。。。
然後我發現了這個文章:
http://www.2cto.com/Article/201310/251830.html
使用各種編碼,各種手段執行JS,簡直喪心病狂。
比如:
啊!CAO。
我開始懷疑整個世界了。。。
所以,
我的問題是:
1.PHP如何完美(或者儘可能完美地)防禦XSS攻擊(比htmlspecialchars更完善的)?
2.我在想是不是防禦XSS最好在前端做(畢竟JS在前端解析字串都有坑啊)?
3.有木有什麼解決方案或者思路啊,什麼都行?
Update20151201:
能不要再複製粘貼答案,or迷信htmlspecialchars是無敵的了好嘛?
\u003cimg src=1 onerror=alert(/xss/)\u003e裡的任何一個字元都是不會被htmlspecialchars處理的。
自己看圖,對,就是你!
這個問題我們還是先來請教一下磚家……
現在馬上為我們連線場外的磚家……
嘟嘟嘟……
磚家您好,請問這位同學的問題您怎麼看?
磚家:我趴在窗戶上看……
……@#%&*!~~(@$%……
好了,原來磚家是說最近霧霾嚴重,所以他只能趴在窗戶上看這個問題……
現在請聽專家解讀:
魔亦有道。
有專門的研究這些東西的,任何事只有專業領域的人做才會更有效率。
使用HTMLPurifier
才是終極理想。
http://www.xcoder.cn/index.php/archives/971
http://willko.iteye.com/blog/475493
http://www.piaoyi.org/php/HTML-Purifier-PHP-xss.html
http://www.edu.cn/ji_shu_ju_le_bu_1640/20080717/t20080717_310285.shtml
http://www.111cn.net/phper/phpanqn/78018.htm
http://security.ctocio.com.cn/securitycomment/54/8222554.shtml
其實我還想說,我不希望防XSS這種事情交給前端,範本語言來做,對於前端,給她用什麼就用什麼,用的不爽自己適當的做變數調節就可以了。給她們用,讓她們用的爽,用的簡單,這是我們好男人的責任和義務,大家說對不對啊,嘻嘻。
首先,我想說,不要用你的無知來挑戰大家
這是道高一尺魔高一丈的東西
html中的編碼:
< 進行編碼html十進位: < html十六進位:<url: %3C base64: PA==
javascript中的編碼:
< 進行編碼八進位:\74 十六進位:\x3c unicode:\u003c
當然 htmlspecialchars 肯定是不行的,只能進行簡單的處理,要不然還討論什麼xss了
The translations performed are:'&' (ampersand) becomes '&''"' (double quote) becomes '"' when ENT_NOQUOTES is not set."'" (single quote) becomes ''' (or ') only when ENT_QUOTES is set.'<' (less than) becomes '<''>' (greater than) becomes '>'
上面代碼還可以這樣寫
testtesttestclick test
但關鍵是,你確定你的那些代碼可以提交嗎?你要確定了再拿出來說
比如最簡單的href加入以下代碼基本上歇菜了
你到底有沒有測試過,就說你提到的那些情境能夠繞過htmlspecialchars呀,實踐出真知.
補充:
你說的對,畢竟很多時候要把AJAX載入的資料用innerHTML添加到頁面.
值得注意的是,innerHTML本質也是輸出HTML,
所以我們可以在輸出前用JS像PHP的htmlspecialchars那樣
把特殊字元(&,",',<,>)替換為HTML實體(&"'<>
).
或者乾脆直接用innerText(IE)和textContent(Firefox),也就是jQuery的text()來輸出常值內容.
StackOverflow上找的兩個實現:
function escapeHtml(text) { return text .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'");}function escapeHtml(text) { var map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, function(m) { return map[m]; });}
方法一,利用php htmlentities函數
php防止XSS跨站指令碼攻擊的方法:是針對非法的HTML程式碼封裝括單雙引號等,使用htmlspecialchars()函數 。
在使用htmlspecialchars()函數的時候注意第二個參數, 直接用htmlspecialchars($string) 的話,第二個參數預設是ENT_COMPAT,函數預設只是轉化雙引號(“), 不對單引號(‘)做轉義。
所以,htmlspecialchars函數更多的時候要加上第二個參數, 應該這樣用: htmlspecialchars($string,ENT_QUOTES).當然,如果需要不轉化如何的引號,用htmlspecialchars($string,ENT_NOQUOTES)。
另外, 盡量少用htmlentities, 在全部英文的時候htmlentities和htmlspecialchars沒有區別,都可以達到目的.但是,中文情況下, htmlentities卻會轉化所有的html代碼,連同裡面的它無法識別的中文字元也給轉化了。
htmlentities和htmlspecialchars這兩個函數對 '之類的字串支援不好,都不能轉化, 所以用htmlentities和htmlspecialchars轉化的字串只能防止XSS攻擊,不能防止SQL注入攻擊.
所有有列印的語句如echo,print等 在列印前都要使用htmlentities() 進行過濾,這樣可以防止Xss,注意中文要寫出htmlentities($name,ENT_NOQUOTES,GB2312) 。
方法二,給一個函數
function xss_clean($data){ // Fix &entity\n; $data=str_replace(array('&','<','>'),array('&','<','>'),$data); $data=preg_replace('/(&#*\w+)[\x00-\x20]+;/u','$1;',$data); $data=preg_replace('/(&#x*[0-9A-F]+);*/iu','$1;',$data); $data=html_entity_decode($data,ENT_COMPAT,'UTF-8'); // Remove any attribute starting with "on" or xmlns $data=preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu','$1>',$data); // Remove javascript: and vbscript: protocols $data=preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu','$1=$2nojavascript...',$data); $data=preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu','$1=$2novbscript...',$data); $data=preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u','$1=$2nomozbinding...',$data); // Only works in IE: $data=preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i','$1>',$data); $data=preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i','$1>',$data); $data=preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu','$1>',$data); // Remove namespaced elements (we do not need them) $data=preg_replace('#]*+>#i','',$data); // http://www.111cn.net/ do{// Remove really unwanted tags $old_data=$data; $data=preg_replace('#]*+>#i','',$data); }while($old_data!==$data); // we are done... return $data;}
想要“完美”防禦XSS,就要每個開發都完全瞭解XSS的知識,在合適的情境用合適的方案來編碼
推薦參考:
https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
任何用一個函數/一個庫來解決這個問題的努力都是白日做夢。
重新再貼一邊:
https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
永遠不可能完美防禦,但至少可以擋住99%(剩下的1%才是最兇猛的~~~),目前的統一做法是,做好輸入檢查,良好的編程意識,安全轉義,藉助第三方安全存放庫。不要信任輸入。也不要信任輸出。
為什麼不用 CSP 直接一了百了呢?