<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/web/13009

            0x00 前言


            我不否認前端在處理XSS的時候沒有后端那樣方便快捷,但是很多人都在說過濾XSS的事就交給后端來做吧。前端做沒什么用。我個人是非常反感這句話的。雖然說前端防御XSS比較麻煩,但是,不是一定不行。他只是寫的代碼比后端多了而已。而且前端防御XSS比后端防御XSS功能多,雖說后端也可以完成這些功能,但是代碼量會比前端代碼多很多很多。其實說了那么多,交給nginx||apache||nodeJs||Python會更好處理。但是我不會C,也就沒辦法寫nginx模塊了。而且也不在本文章的范圍內,等我什么時候學會C再說把。

            0x01 后端數據反饋過濾


            現在大部分的網站都是在后端過濾一下后,就交給數據庫,然后前端輸出,整個流程只有后端做了防護,一般這個防護被繞過或者某個參數的防護沒有做,那么網站就會被淪陷了(請別以為XSS只能獲取cookie,熟練的程度取決于你的思想和編程) 現在我們來假設一下網站的一個URL參數沒有做好過濾,直接導入數據庫了,然后在前端反饋結果。代碼如下:

            把用戶輸入的內容導入到數據庫里defenderXssTest_GetData.php:

            #!php
            <?php
            if(empty($_GET['xss'])){        //判斷當前URL是否存在XSS參數
                exit(); 
            }
            $xssString = $_GET['xss'];
            /*數據庫基礎配置*/
            $mysql_name ='localhost';
            $mysql_username ='root';
            $mysql_password ='123456';
            $mysql_database ='xsstest'; 
            $conn = mysql_connect($mysql_name,$mysql_username,$mysql_password);
            mysql_query("set names 'utf8'");
            mysql_select_db($mysql_database);
            
            $sql = "insert into XSSTest (xss) values ('$xssString')";
            mysql_query($sql);
            mysql_close();
            

            返回數據庫中最后一條數據內容(即最新的內容)defenderXssTest_QueryData.php:

            #!php
            <?php
            /*數據庫基礎配置*/
            $mysql_name ='localhost';
            $mysql_username ='root';
            $mysql_password ='123456';
            $mysql_database ='xsstest'; 
            $conn = mysql_connect($mysql_name,$mysql_username,$mysql_password);
            mysql_query("set names 'utf8'");
            mysql_select_db($mysql_database);
            
            $sql ="select * from XSSTest where id = (select max(id) from XSSTest)"; //返回數據庫中最后一條數據
            $xssText = mysql_query($sql);
            while($row = mysql_fetch_array($xssText)){  //顯示從數據庫中返回的數據
                echo $row['xss'];
            }
            mysql_close();
            

            前端輸入及反饋defenderXssTest.html:

            #!xml
            <!DOCTYPE html>
            <html>
            <head>
                <meta charset="utf-8">
                <title>前端防御XSS#Demo1</title>
            </head>
            <body>
                <input type="text" name="xss">
                <input type="submit" value="提交" id="xssGet">
            </body>
            <!--測試請記得更換jQuery路徑!-->
            <script type="text/javascript" src="/Public/js/library/jquery.js"></script>
            <script>
                $("#xssGet").click(function(){
                    $.ajax({
                        url: '/defenderXssTest_GetData.php',
                        type: 'get',
                        dataType: 'text',
                        data: "xss="+$('input:first').val(),
                        cache:false, 
                        async:false,
                    })
                    .done(function() {
                        $.ajax({
                            url: '/defenderXssTest_QueryData.php',
                            type: 'post',
                            dataType: 'text',
                            cache:false, 
                            async:false,
                        })
                        .done(function(data) {
                            $("body").append(data);
                        })
                    })
                });
            </script>
            </html>
            

            一共三個文件,因為測試用,我就沒把數據庫基礎配置分離出來放在其他文件里了。

            現在我們在瀏覽器里打開defenderXssTest.html文件:

            p1

            p2

            現在我們再看下數據庫:

            p3

            已經導入到數據庫里了。

            OK,以上就是最普通的儲蓄型XSS案例。為什么會出現這個問題呢,是因為PHP沒有做好過濾。同時前端也沒有做好過濾,這里會有人說前端做沒用的,攻擊者可以使用burp抓到此數據包,然后改包就可以繞過了。對,確實是這樣。但是大伙從一開始就已經被誤導了。想知道哪里被誤導么,往下看。

            這里我畫個前端、Nginx、后端都做了過濾的圖:

            p4

            思維導圖URL:https://www.processon.com/view/link/56c486cde4b0e2317a8b6681

            這里我們可以看到防火墻的第一道門是前端過濾XSS機制。也是目前被大家所熟知的過濾結構。而本章要說的是:為什么不把前端過濾copy或者move到后端過濾機制下呢?

            這里是新型的過濾機制的圖:

            p5

            思維導圖URL:https://www.processon.com/view/link/56c4882ce4b0e5041c35ab53

            這里我們在后端過濾機制的后面加上了前端過濾。為什么要這樣做呢?

            大家都知道前端過濾XSS是可以被抓包軟件給修改的,所以是可以繞過,沒有什么用。而Nginx過濾我相信大家都知道,很少有人愿意去用它,因為如果是做安全文章一類的話,是會被Nginx給拋棄當前的數據包的,也就是你發布的文章不會被存到數據庫里,而且Nginx防御XSS模塊并沒有前端、后端那樣簡單方便,需要配置的東西很多。也導致了很多管理員不在Nginx安全上下功夫,即使管理員配置了Nginx過濾XSS模塊,也可以繞過。

            利用Nginx的一處邏輯缺陷(詳情請移步到:http://www.freebuf.com/articles/web/61268.html 文章里的0x03小節:利用Nginx&Apache環境bug來實現攻擊),至于后端過濾機制肯定會有不嚴謹的時候,不然也而不會導致那么多XSS漏洞了。所以當攻擊者輸入的XSS字符串繞過了前端、Nginx、后端的話,那么就會直接導入到數據庫中。那么這個時候后端傳來的數據就不可信了。而如果我們在前端顯示后端傳來的數據時加了過濾會怎么樣呢,答案是very good。當然了,這里有個前提,是前端顯示后端傳來數據的時候使用的是AJAX方法,而不是類似ThinkPHP這樣在模板里調用。確切的說:此方法只針對于API接口

            現在我們來做一個測試,之前的代碼就是使用了AJAX方法,而

            defenderXssTest_GetData.php和defenderXssTest_QueryData.php就類似于后端的API接口。我們現在在原有的基礎上添加一些代碼:

            下面是前端過濾XSS的代碼,取自于百度FEX前端團隊的Ueditor在線編輯器:

            #!php
            function xssCheck(str,reg){
                return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function (a, b) {
                    if(b){
                        return a;
                    }else{
                        return {
                            '<':'&lt;',
                            '&':'&amp;',
                            '"':'&quot;',
                            '>':'&gt;',
                            "'":'&#39;',
                        }[a]
                    }
                }) : '';
            }
            

            然后我們在原有代碼的基礎上添加xssCheck()函數就行了。如下:

            #!xml
            <!DOCTYPE html>
            <html>
            <head>
                <meta charset="utf-8">
                <title>前端防御XSS#Demo1</title>
            </head>
            <body>
                <input type="text" name="xss">
                <input type="submit" value="提交" id="xssGet">
            </body>
            <script type="text/javascript" src="/Public/js/library/jquery.js"></script>
            <script>
                $("#xssGet").click(function(){
                    $.ajax({
                        url: '/defenderXssTest_GetData.php',
                        type: 'get',
                        dataType: 'text',
                        data: "xss="+$('input:first').val(),
                        cache:false, 
                        async:false,
                    })
                    .done(function() {
                        $.ajax({
                            url: '/defenderXssTest_QueryData.php',
                            type: 'post',
                            dataType: 'text',
                            cache:false, 
                            async:false,
                        })
                        .done(function(data) {
                            $("body").append(xssCheck(data));
                        })
                    })
                });
                function xssCheck(str,reg){
                    return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function (a, b) {
                        if(b){
                            return a;
                        }else{
                            return {
                                '<':'&lt;',
                                '&':'&amp;',
                                '"':'&quot;',
                                '>':'&gt;',
                                "'":'&#39;',
                            }[a]
                        }
                    }) : '';
                }
            </script>
            </html>
            

            現在我們來輸入XSS字符串看看:

            p6

            變成了這個樣子。我們再去數據庫里看下:

            p7

            的確是完整的XSS字符串,但是前端過濾了,導致此XSS沒有用武之地。

            所以前端開發人員只需要在網站的base.js代碼里把過濾XSS的函數寫進去,再把每一個ajax傳過來的數據加上函數就可以了。

            0x02 前端報警機制


            這里的報警機制不能說特別的完整,是可以繞過的。那這個報警機制到底有何用處呢?就是在攻擊者測試的時候發現及報警。

            我們都知道測試XSS的時候和裝逼的時候,攻擊者會輸入alert()函數,而之前的過濾方式,都是使用正則匹配,從而導致正則過長,匹配不易,運行過慢等問題。而現在我們完全可以重寫alert函數來讓攻擊者在測試的時候,使用的是我們已經重寫后的函數,這樣做的好處是:當當前的參數不存在XSS的時候,這些函數是不會被觸發的。而當當前參數存在XSS的時候,攻擊者會依次輸入:woaini->查看是否在源碼里輸出->woaini<>->查看<>有沒有被過濾->輸入<script>alret(1)</script>或者<img src="test" onerror="alert(1)" />->使用了我們重寫的函數->觸發報警機制。這樣說可能有些人看不懂,下面是我畫的圖:

            p8

            思維導圖:https://www.processon.com/view/link/56c55805e4b0e5041c39261f

            讓我們來看下具體的代碼吧:

            #!php
            var backAlert = alert;  //把alert賦值給backAlert ,當后面重寫alert時,避免照成死循環,照成溢出錯誤。
            window.alert = function(str){       //重寫alert函數
                backAlert(str);
                console.log("已觸發報警,將數據發送到后臺");
            }
            

            再把console.log換成ajax把數據發送給后臺應用。后臺接受的時候記得做過濾。前端代碼記得加密,防止攻擊者看出意圖從而導致繞過,不觸發報警。因為可能有些公司、個人網站已經有了自己的攻擊報警系統、智能日志檢索系統,我也就不再寫了。把ajax發送的數據過濾后存到數據庫里,再顯示就行了。可以根據自己現有的框架進行開發,思路上面已經了,不難理解,代碼也不難寫。如果你不會或者說是不想寫,可以等到我下一篇的文章。到時候里面會有全部的源代碼。

            下一章也是有關XSS防御的,在“前端報警機制”的基礎上做的完善,有可能會用到后端,目前思路已經有了,但是沒時間寫。看三月底之前能不能寫出來吧。

            0x03 結語


            之前EtherDream已經說了前端防火墻了,只是他做的是防御,而我是不防御直接報警。然后人工修復代碼。因為雖然你防御住了,但是后端漏洞還在那,而觸發報警機制后就可以進行人工修復。不是說EtherDream寫的不好,反之非常好,在他的基礎上也可以修改成前端報警機制,不過我還是喜歡讓攻擊者高興幾十分鐘后,就懵逼的樣子。在EtherDream的代碼中有一個很棒的代碼片段,他使用了內聯事件監聽了onclick等on事件,可以近一步的監聽到黑客的操作。因為版權問題,我不方便把代碼貼到本文中,畢竟是別人的思想結晶。想了解的話可以去查看:

            http://fex.baidu.com/blog/2014/06/xss-frontend-firewall-1/

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线