我在開發中常忽視的安全問題

JJian發表於2019-07-03

前言

  前不久,開發的任務告一段落。後來得知專案中bug比較多,需要去逐一檢查修復,苦於沒有專業的測試工程師,只好硬著頭皮上(覺得能學到很多東西),也學會了一些安全測試常用軟體基本操作,比如Burpsuite、sqlmap等。今天做一些總結吧,以此提醒日後的開發過程中不僅要考慮程式碼的質量規範等問題,還要注意儘量減少安全問題的出現,以往只是埋頭開發,並不太關注安全問題。所以這次對我來說收穫不小!

 


 

一、SQL隱碼攻擊

  Sql注入(SQL injection)是指攻擊者在伺服器端構造資料庫執行程式碼可以在伺服器中資料庫得到執行。由於攻擊程式碼在資料庫中執行,根據連線使用者的許可權,可以讀、修改資料庫資料甚至執行資料庫外部命令,典型的攻擊方法為竊取資料庫資料、控制作業系統等。

  使用sqlmap可對url進行SQL隱碼攻擊掃描,進而能攻破DB,獲取表資訊等,是十分需要我們開發者關注的問題,但是已經有很多手段能防止SQL隱碼攻擊,最簡單的就是使用Mybatis持久框架,同時摒棄宣告式註解SQL拼接:比如@Select等註解中的SQL語句可能需要拼接,推薦使用XML編寫SQL,儘量使用#{}引數傳入,而不是${}傳入

解決辦法:

  1、增加全域性防注入功能,從客戶端獲取到的引數都必須通過安全校驗,防範以下常見攻擊字元:

 

  '|"|>|..|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|script|frame|;|or|-|+|,|)|etc|style|expression

 

  注:對獲取的引數進行安全檢測之前應首先統一字元編碼和大小寫,避免攻擊者通過編碼和大小寫混用繞過安全檢查。

  2、摒棄SQL動態拼接的查詢方式,使用引數化查詢

  3、採用Mybatis等持久化框架官方推薦的SQL方式進行SQL編寫查詢

 

二、XSS漏洞

  與SQL隱碼攻擊類似,SQL隱碼攻擊是注入可執行的SQL程式碼到資料庫服務端,而XSS簡單來說就是注入可執行的具有危害的指令碼程式碼,具體來說:跨站指令碼(Cross-site Scripting簡稱XSS)是指攻擊者輸入攻擊程式碼到伺服器端,程式碼在其它使用者的瀏覽器中得到執行。由於攻擊程式碼在受害者的瀏覽器中執行,可以讀、修改和傳輸任何瀏覽器可以讀取的資料,典型的攻擊方法為竊取Cookie、網頁重定向等。

  往往在頁面的一些輸入框:

  

  或者Url中增加字尾或者改寫引數為xss指令碼程式碼,如:xxx/name=Lijian&id=123"()%26%25<acx><ScRiPt%20>alert('xss')</ScRiPt>

  

 

  xxx/serviceid=1%22%3e%3cscript%3ealert(1)%3c%2fscript%3e

  

  採取措施:

  1、增加全域性防護功能,從客戶端獲取到的引數都必須通過安全校驗,防範常見攻擊字元:注:對獲取的引數進行安全檢測之前應首先統一字元編碼和大小寫,避免攻擊者通過編碼和大小寫混用繞過安全檢查。

  2、將獲取到的資料進行HTML轉碼再存入資料庫或輸出。

  3、通過提高Seesion、Cookie等安全(設定HttpOnly,Secure等屬性)

三、傳輸安全

1、關於Cookie安全:

  1) 加密會話(SSL)缺少HttpOnly屬性

  有效防止客戶端指令碼,比如JS指令碼獲取Cookie資訊並且傳送到指定站點提供黑客使用。準確來說是防止非HTTP協議程式介面獲取Cookie(Java後端可以獲取Cookies,前端通過Ajax獲取)

  Java語言設定httpOnly

response.setHeader("Set-Cookie", "cookiename=value;Path=/;Domain=domainvalue;Max-Age=seconds;HTTPOnly");

  2) 加密會話(SSL)缺少Secure屬性

  如果設定了Secure=true,那麼這個cookie只能依靠https協議傳送,不能使用http協議。即此時的Cookie是在https時被建立的,如果改為http頁面則無法使用Cookie,通常結合httpOnly一起配置使用提高Cookie安全

  3) 防Cookie偽造

  傳統的Cookie(name, value)形式,網上流傳的是將value值加上儲存時間savaTime、自定義金鑰、簽名等方式

  4) 儘量不要存放敏感資訊

  不要在Cookie中放userInf等資訊,比如手機號碼,郵箱等,即使放也要加密!

2、關於密碼加密:

  除了規定高強度的複雜性外,還不能只是簡單的MD5加密,網上有很多的MD5解密網站可進行破解

四、敏感資訊傳輸

1、Html或者JS程式碼不要存在敏感重要的資訊

  眾所周知,前端程式碼不存在加密,大部分都是可見的,所以在前端開發過程中儘量不要留一些隱私敏感資訊,比如測試時候用來測試預留的銀行卡號等資訊應該刪除

  至少在前端展示的時候用“●●●●”或者“****”代替,而不是直接顯示在前端!

  如敏感資訊App Secret Key

  

  前端中:

  

  而不是:

  

2、除了密碼不要明文傳輸,使用者名稱視情況而進行加密

  當然像一些手機號碼也需要加解密,比如替換中間的幾位數,對中間幾位數進行加密隱藏等!

3、敏感資訊儘量不要放在session中,即使放也要加密

  對Session的key值儘量不要使用簡單的userId等,可以再加UUID,或者SessionId

五、暴力破解

1、使用者名稱的列舉

常見登入頁面,當輸入使用者名稱之後(還未輸入密碼進行登入)可能會返回“使用者名稱不存在”的提示,這樣的提示可能會讓黑客進行大量的使用者列舉破解使用者名稱。我們應該統一返回“使用者名稱或密碼錯誤”的提示,混淆防止黑客對使用者名稱進行暴力破解。

2、關鍵資訊輸入未設定錯誤次數可能會被暴力破解

如常見的密碼輸入,使用者輸入的次數超過一定數量後對使用者進行暫時鎖定或者強制修改密碼等;手機驗證碼輸入次數達到上限後,同樣進行鎖定(時間鎖定/操作鎖定)

3、篡改返回資料包,可繞過驗證碼

當然登入頁面還會密碼還有驗證碼等的校驗,比如註冊/登入頁面中往往最後有個輸入(手機)驗證碼的文字框,

   

 

  像我這樣的新手每次都會行雲流水,忘情地寫程式碼,自我感覺棒棒噠:嗯…..使用者輸入驗證碼之後我們後端要驗證驗證碼是否正確,我要開發一個checkVeriCode驗證驗證碼是否正確的介面,emmmm……..正確就返回true,錯誤就返回false。然後返回true之後,就可以繼續調我們的/login登入介面,或者註冊/register介面,So easy!超簡單(無比膨脹)。

  面對自己的ignorance,我只想說:千萬不要單獨開發驗證驗證碼是否正確的介面,進行呼叫!千萬不要單獨開發驗證驗證碼是否正確的介面,進行呼叫!千萬不要單獨開發驗證驗證碼是否正確的介面,進行呼叫!最好就使用一個介面(login介面或者register介面中)將驗證驗證碼環節放到其中,點選註冊/登入按鈕之後呼叫登入/註冊介面,先進行驗證碼的校驗,正確則直接進入登入/註冊儲存,錯誤則提示驗證碼失敗。簡單來說我們開啟F12開發者模式,只能看到一個介面,而不會出現checkVeriCode介面與/login(/register)兩個介面,前一個介面影響後一個介面。

  總結起來就是我們在開發埋頭苦幹忘我地編寫程式碼的時候,量減少“多介面強耦合”開發,即後一個介面會受前一個介面返回包影響(這種影響往往是比較重要的),而前一個介面返回包往往很容易被篡改(你說還有比true/fasle更容易被篡改的嘛?)

  Request:輸入錯誤的驗證碼----->呼叫checkVeriCode--------請求我不攔截你放你走

  Response:checkVeriCode介面返回false----->站住你別走,我要攔截你,把你打(改)成true---->好了,你可以走了--->驗證碼輸入正確---->成功破解登入/註冊

  我們使用Burpsuite中我們擷取校驗驗證碼介面,輸入錯誤的驗證碼123456

  

  返回false資料包,沒有任何問題。這是正確的

  

  下面我們篡改false為true之後返回到客戶端,即可繞過驗證碼環節

  

4、郵箱/簡訊轟炸

  這種郵件/簡訊轟炸是比較好理解的,黑客獲取到相關的介面之後進行返回撥用,進而對指定手機號碼/郵箱進行轟炸,嚴重的話郵箱伺服器等困難會崩潰。

  那麼此時我們簡單的就認為那還不簡單,對同一個手機號碼/郵件進行次數限定不就完了?真的是這樣就可以了嗎?當時我一開始的時候也是這麼想的!當時我想的最簡單的做法就是:在規定T時間內每次對同一個手機號碼傳送驗證碼後,存入Redis中的次數(有效期T)加1,達到一定數目後,進行“操作頻繁提示”。

  但其實我們還忽略了另外一個問題,如果攻擊者使用同一IP不同手機號碼進行攻擊,他的目標不再是轟炸手機,而是讓伺服器崩潰,那麼我們仍然要對同一個IP進行次數限制。需要注意的是對方可能使用的是代理IP,我們還需要檢測真實IP,網上有很多檢測是否是代理IP,獲取真實IP的方法,簡單的判斷:HTTP頭部X-Forward-For第一個IP就是我們的真實IP。

六、伺服器洩露

1、 伺服器版本洩露

  這個我們通常負載均衡使用Nginx伺服器,有時候錯誤輸入URL可能會跳轉到Nginx的錯誤頁面顯示404,此時會顯示一個Nginx的版本號,或者歡迎頁面

解決辦法:

  1)去除歡迎頁面

  找到index.html歡迎頁面,註釋html程式碼即可。

[open@dcspap01 ~]$ cd /usr/local/nginx/html

 

  2)去除版本號:

  第一步:修改nginx.conf,http中增加:server_tokens off

[open@dcspap01 ~]$ cd /home/open
[open@dcspap01 ~]$ vi nginx.conf

  第二步:編輯fastcgi.conf檔案,找到fastcgi_param SERVER SOFTWARE 去掉後面的引數$nginx_version,即隱藏nginx版本號

[open@dcspap01 ~]$ cd /usr/local/nginx
[open@dcspap01 ~]$ vi fastcgi.conf

 

  第三步:重啟nginx,需要root使用者登入

[open@dcspap01 ~]$ cd /usr/local/nginx/
[open@dcspap01 ~]$ ./nginx -s reload

2、點選劫持漏洞

  HTTP 響應頭資訊中的X-Frame-Options,可以指示瀏覽器是否應該載入一個 iframe 中的頁面如果伺服器響應頭資訊中沒有X-Frame-Options,則該網站存在點選劫持攻擊風險。

修改web伺服器配置,新增X-Frame-Options響應頭。賦值有如下三種:

  • DENY:不能被嵌入到任何iframe或者frame中。
  • SAMEORIGIN:頁面只能被本站頁面嵌入到iframe或者frame中。
  • ALLOW-FROM url:只能被嵌入到指定域名url的框架中。

解決辦法

  Java程式碼:

  response.addHeader("x-frame-options","SAMEORIGIN");

  Nginx配置:(新增到 'http', 'server' 或者 'location' 的配置中:)

  add_header X-Frame-Options SAMEORIGIN

  Apache配置:

  Header always append X-Frame-Options SAMEORIGIN

3、 謹慎啟用OPTIONS

  OPTIONS方法請求web伺服器告知其支援的各種功能。可以詢問伺服器通常支援哪些方法,或者對某些特殊資源支援哪些方法。如果單獨再詳細瞭解OPTIONS的話估計又得一大篇了,搜所以這麼簡單的總結下:

  OPTIONS通常存在於前後端分離的跨域請求中:瀏覽器自發起一次preflight request(預檢請求),以檢測實際請求是否安全,這也是黑客通常針對的點,那麼我們後端需要做什麼呢,就需要做一些簡單的跨域設定,允許部分的HTTP METHOD

response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
response.addHeader("Access-Control-Max-Age", "1800");

  啟用OPTIONS請求後,返回Allow頭,表明後端伺服器支援的HTTP方法,有些方法DELETE、HEAD等方法是沒必要或者不安全的,需要禁止,同時如果伺服器不需要支援WebDAV,請務必禁用它,或禁止不必要的HTTP方法!

  

 


 

總結

  1)我不是專業的安全測試,所以提到的問題可能不是很完善,解決辦法也可能不是很好,我相信肯定有很多的更好的解決辦法;

  2)在這裡我只是想提醒作為菜鳥開發的話不僅要關注程式碼質量,對程式碼負責。還要對安全問題負責,我認為這才是好的(禿頭)程式猿應該全方位考慮到的;

  3)“不光還要懂開發懂程式碼,還要全域性掌握一些知識”這是我對自己的要求,這些問題看似簡單但據我經歷來看,很多開發者都會忽視(我為什麼還要懂安全問題,我又不是測試)。

 

相關文章