天天看點

PHP網站常見的安全漏洞及防範措施PHP網站常見的安全漏洞及防禦措施

PHP網站常見的安全漏洞及防禦措施

注: 拿電商系統來舉例 (架構:Laravel5.*)

1.SQL注入漏洞

SQL注入是最古老、最流行同樣也是危害最大的漏洞之一,該漏洞從核心來說就是:将 【未經過濾】 的使用者輸入拼接到SQL語句中。

舉個例子:

這個時候使用者送出的 id 如果是 1 and 1 = 2,那麼最終被執行的 SQL 就是:

很明顯這個 SQL 查出來的結果必然為空,那麼頁面就會顯示該商品不存在。

但僅僅是這樣感覺好像沒有什麼危害,那這個時候攻擊者送出的 id 參數變成了:

執行的 SQL 就變成了:

假如攻擊者看到了正常的商品頁面,那就說明這個網站存在一個名為 admin 的管理者。接着請求:

這樣根據頁面是否展示商品資訊就能判斷出 admin 這個管理者在資料庫中的密碼是否為 32 位。這樣就可以一步一步來驗證出管理者的密碼。現在有很多自動化的 SQL 注入軟體,可以用很短的時間就把管理者的資料查詢出來。

在 PHP 的項目裡要避免 SQL 注入需要兩個條件:

  1. 使用 Prepared Statement + 參數綁定
  2. 絕對不手動拼接 SQL

Prepared Statement 簡單來說就是把要執行的 SQL 與 SQL 裡的參數分開,還是以上面的查詢商品為例,Prepared Statement 就是:

然後再傳入參數:1 and 1 = 1,這個時候資料庫會嚴格地去查找對應 ID = 1 and 1 = 1 的商品,而不是把 and 1 = 1 當做查詢條件,是以即使 1 = 1 成立,資料庫仍然傳回空。

在 Laravel 裡所有的 SQL 查詢都是 Prepared Statement 模式,是以隻要我們的代碼中沒有出現類似下方的代碼,就不會存在 SQL 注入的風險:

2.XSS漏洞

XSS 是與 SQL 注入齊名的漏洞,也可以用一句話來描述其核心:将未經過濾的使用者輸入原樣輸出到網頁中。

例如使用者把以下内容作為收貨位址送出:

而如果我們在背景原樣輸出這個收貨位址,就會觸發網頁 JS 代碼,彈出一個 123 的提示框。

例如在使用者輸入資訊儲存到資料庫中,并在之後輸出到了頁面中:

  • 使用者昵稱
  • 收貨位址的所有字段
  • 下單時的備注字段
  • 商品評價字段
  • 申請退款理由

在Laravel中要輸出文本有兩種方式:

{{ $str }}

{!! $str !!}

,前者等同于 echo htmlspecialchars($str) 而後者則是 echo $str,htmlspecialchars() 函數預設會把 <>&" 這個 4 個字元分别轉義成 <、&lgt;、& 和 ",是以我們隻要保證我們一直在用 {{ }} 來輸出就沒有問題;

3.CSRF跨站請求僞造漏洞

CSRF 漏洞的危害程度不低于 XSS,但是遠沒有 XSS 那樣出名。一句話描述就是:利用使用者的身份認證資訊在使用者目前已登入的 Web 應用程式上執行非使用者本意的操作。

舉個例子,假如 一個網站 登出的請求方式和路由是 Get /logout,那麼惡意使用者隻要在 該網站中 的文章裡插入一張圖檔,圖檔的 SRC 是 https://domain/logout,那麼任何通路這個文章的使用者都會被強制退出,因為浏覽器在請求這個 URL 之前并不知道這個 URL 是不是一張真的圖檔,那麼就會帶着目前使用者的 Cookie 用 Get 方式去請求這個退出頁面,也就導緻目前使用者被強制退出。

那麼是不是把請求方式改成 Post 就沒有問題了呢?确實無法通過圖檔的方式來觸發,但是惡意使用者可以在其他站點構造一個頁面,内容如下:

<form action="https://domain/logout" method="post" id="form"></form>
<script>document.gentElementById('form').submit();</script>
           

然後惡意使用者誘導正常的使用者去通路這個頁面(比如這個惡意使用者在 該網站 的頭條裡分享了這個頁面并取了一個很有誘惑的标題《教你如何實作升職加薪》,相信會有很多人通路),那麼通路了這個頁面的使用者就會被強制退出 。

如果一個銀行網站的轉賬功能有 CSRF 漏洞,那麼惡意使用者就可以通過 CSRF 漏洞來盜取其他使用者的資金。

根據上面所說的原理,不難發現要避免 CSRF 攻擊需要兩個條件:

  1. 敏感操作不能用 Get 請求方式;
  2. 對于非 Get 的請求方式,需要校驗請求中的 token 字段,這個字段值對每個使用者每次登入都是不一樣的。

對于 Laravel 項目來說已經内置了 CSRF 的防禦手段,我們在寫前端表單時都需要寫一個

<input type="hidden" name="_token" value="{{ csrf_token() }}">

來送出 CSRF Token,是以我們的項目不會有 CSRF 攻擊的風險。