PHP代碼安全有必要了解下

攻擊者經過構造惡意SQL命令發送到數據庫,若是程序未對用戶輸入的 SQL命令執行判斷過濾,那麼生成的SQL語句可能會繞過安全性檢查,插入其餘用於修改後端數據庫的語句,並可能執行系統命令,從而對系統形成危害php

例如刪除 id 爲 1 的帖子,sql 以下:html

1 $post_id = $_POST['post_id'];
2 
3 $sql = "DELETE FROM posts WHERE user_id = 1 AND id = $post_id";
4 
5 \DB::statement($sql);

 

若是有人在提交 post_id 時輸入 1 OR 1 ,你的語句會組合成這樣:laravel

$sql = "DELETE FROM posts WHERE user_id = 1 AND id = 1 OR 1";

通常比較常出如今原生的 SQL 操做,框架通常會解決這方面的問題。一般採用參數控制或過濾特殊字符避免上述的問題。web

越權漏洞

1. 水平越權sql

水平越權就是同等角色下的用戶,不但可以訪問和操做本身私有的數據,還能訪問其餘人私有的數據,其根本是基於數據的訪問權限。數據庫

刪除用戶收款方式的場景以下:json

graph LR
用戶登陸-->token
token-->獲取收款方式列表
獲取收款方式列表--token-->經過id刪除

經過收款方式 {id} 執行 delete 請求的路由爲: localhost/api/payments/{id}後端

假如用戶A的收款方式有{1,2,3}    用戶B的收款方式有{4,5}

若是沒有作數據控制,A 登陸後攜帶 A 的 token 執行刪除的接口 localhost/api/payments/4,則會刪除 B 的,因此須要對 destory 方法作數據控制api

 1 # 1 刪除前鑑權處理
 2 public function destory($id){
 3     $payment = Payment::find($id);
 4     if ($payment->user_id != $this->currentUser->id) {
 5             return ...
 6     }
 7     $payment->delete();
 8 }
 9 
10 # 2 參入id查詢刪除
11 public function destory($id){
12     Payment::whereUserId($this->currentUser->id)->whereId($id)->delete();
13 }
14 
15 # 3 模型關聯查詢
16 class User extends Model{
17     public function payments()
18     {
19             return $this->hasMany('App\Payment');
20     }
21 }
22 
23 class PaymentController extends Controller{
24     public function destory($id)
25     {
26             $this->currentUser->payments()->whereId($id)->delete();
27     }
28 }

 

推薦使用第三種方式作數據控制,否則面向對象白學了。獲取收款方式的列表一樣須要數據權限控制,用戶和收款方式存在一對多的關聯關係,模型關聯後,獲取用戶收款方式列表能夠寫成瀏覽器

 1 class PaymentController extends Controller{
 2     public function index($id)
 3     {
 4         #帶條件的查詢
 5         $payments = $this->currentUser->payments()->where(function($query){
 6                 ...
 7         })->get();
 8 
 9         #不帶條件的查詢
10         $payments = $this->currentUser->payments;
11     }
12 }

 

2. 垂直越權

低權限的角色經過一些途徑,得到高權限的能力,就發生了越權訪問。如普通用戶 guest 修改 admin 用戶的密碼;guest 可直接進入後臺取得域名管理、用戶管理等全部權限

解決這個問題,須要把權限職責以最小顆粒細分,基於 RBAC 設計權限管理系統。分爲如下關聯模型

graph LR 
用戶--多對多-->角色
用戶--多對多-->權限
角色--多對多-->權限

每次執行請求時,在前置中間件判斷這個用戶是否永遠該執行請求的權限,無權限則駁回。

3. 上下文越權

攻擊者可以利用應用程序狀態機中的漏洞得到關鍵資源的訪問權限,這就存在上下文相關的越權。上下文相關的越權漏洞通常屬於業務邏輯漏洞。 如在找回密碼過程當中,攻擊者使用本身的帳戶信息經過驗證,將他人的密碼進行了修改。

graph LR
1.郵箱驗證-->2.找回密碼

在步驟1以後,執行找回密碼,路由爲 。若是此時沒有校驗當前找回密碼的帳戶是否爲進行郵箱校驗後的帳戶,由可能產生越權漏洞.

路由 : 【PUT 】localhost/api/users/find-password,接收參數 email,new_password.

錯誤:校驗和修改分紅 2 步 localhost/api/email/check -> localhost/api/users/password

 1 class UserController extends Controller{
 2     public function check($data)
 3     {
 4         if (checkEmail($data['email'], $data['code'])) {
 5                 return true;
 6         }
 7         ...
 8     }
 9 
10     public function findPassword()
11     {
12         $user = User::whereEmail($data['email'])->first();
13         $user->password = bcrypt($data['new_password']);
14         $user->save();
15     }
16 }

 

正確:在 findPassword 裏面再次驗證完成郵箱校驗的帳戶是否爲當前找回密碼的帳號

 1 class UserController extends Controller{
 2 
 3     public function check($data)
 4     {
 5         if (checkEmail($data['email'], $data['code'])) {
 6                 return true;
 7         }
 8         ...
 9     }
10 
11     public function findPassword($data)
12     {
13         if (checkEmail($data['email'], $data['code'])) {
14                 $user = User::whereEmail($data['email'])->first();
15                 $user->password = $data['new_password'];
16                 $user->save();
17         }
18         ...
19     }
20 }

 

限制分頁條目範圍,防止惡意請求

如獲文章列表的接口 localhost/api/articles

 1 public function index($params){
 2     $pageId = $params['pageid'] ?? PAGE_ID;    //頁碼
 3     $pageSize = $params['pagesize'] ?? 15;  //條碼
 4 
 5     $articles = Article::where(function ($query) use ($params) {
 6             ...
 7     })->take($pageSize)->skip($pageId * $pageSize)->orderby('id', 'desc')->get();
 8     ...
 9     ...
10 }

 

以上代碼若是沒有限制 pagesize 的範圍,惡意請求者請求把 pagesize 輸入 5000,10000 等甚至更大的數,會給數據庫帶來必定的壓力,localhost/api/articles?pageid=0&pagesize=10000

1 //用框架自帶的分頁方法
2 public function index(){
3     $builder = Article::with('category:id,name')->orderBy('id', 'desc')->paginate(8);
4     return response()->json(['status' => true, 'count' => $builder->total(), 'articles' => $builder->items()]);
5 }

 

JWT 的 Token 須要二次加密

許多拓展包加密出的token並不十分安全,用base64_decode能夠解密獲取 加密主鍵、載荷等重要信息,因此一般須要對JWT的token進行二次加密

限制上傳文件的類型

對於一個圖片上傳的接口,若是沒有對上傳文件的格式作限制,攻擊者頗有可能把 .php後綴的文件上傳到public/images目錄下,而後經過根目錄執行這個文件。

須要設計安全的文件上傳功能避免上述問題

  1. 文件上傳的目錄設置爲不可執行
  2. 判斷文件類型
  3. 使用隨機數改寫文件名和文件路徑
  4. 單獨設置文件服務器的域名

禁止或者避免寫自動解壓.zip 等壓縮文件的代碼

單純地限制文件或壓縮包大小並無用,一個ZIP炸彈的.zip文件僅有 42 KB,但在解壓後會佔用 4718592 GB

避免登陸密碼被暴力破解

  1. 設定嚴格的速率限制,如登陸次數限制,登陸錯誤次數達 x 次時暫停登陸 n 分鐘
  2. 密碼加上隨機鹽
1 public function reg(){
2     $user = new User;
3     $salt = radom(6);
4     $user->password = bcrypt($data['password'] . $salt);
5     ...
6 }

 

作好異常處理,避免在生產環境中不正確的錯誤報告暴露敏感數據

若是你不當心,可能會在生產環境中由於不正確的錯誤報告泄露了敏感信息,例如:文件夾結構、數據庫結構、鏈接信息與用戶信息。

  1. 在.env 文件中關閉調試模式
    APP_DEBUG=true
  2. php 錯誤控制 error_reporting、display_errors
     1 <?php
     2 // 關閉錯誤報告
     3 error_reporting(0);
     4 
     5 // 報告 runtime 錯誤
     6 error_reporting(E_ERROR | E_WARNING | E_PARSE);
     7 
     8 // 報告全部錯誤
     9 error_reporting(E_ALL);
    10 
    11 // 等同 error_reporting(E_ALL);
    12 ini_set("error_reporting", E_ALL);
    13 
    14 // 報告 E_NOTICE 以外的全部錯誤
    15 error_reporting(E_ALL & ~E_NOTICE);
    16 ?>
    17 
    18 display_errors = Off

     

php 弱語言的設計缺陷如:in_array

 1 $array=[0,1,2,'3'];
 2 
 3 var_dump(in_array('abc', $array)); //true
 4 
 5 var_dump(in_array('1bc', $array)); //true
 6 
 7 # 上面的狀況返回的都是 true, 由於’abc’會轉換爲 0,’1bc’轉換爲 1
 8 
 9 $a = null;
10 $b = false;
11 echo $a==$b;  //true
12 
13 $c = "";
14 $d = 0;
15 echo $c==$d   //true

 

在一些重要的地方須要使用 === 來做數據判斷。

LFI (本地文件包含)

LFI (本地文件包含) 是一個用戶未經驗證從磁盤讀取文件的漏洞。

不驗證過濾用戶的輸入 將它要渲染的模板文件用 GET 請求加載。

1 <body>
2     <?php
3       $page = $_GET['page'];
4       if(!$page) {
5         $page = 'main.php';
6       }
7       include($page);
8     ?></body>

 

因爲 Include 能夠加載任何文件,不只僅是 PHP,攻擊者能夠將系統上的任何文件做爲包含目標傳遞。

index.php?page=../../etc/passwd

 

這將致使 /etc/passwd 文件被讀取並展現在瀏覽器上。

要防護此類攻擊,你必須仔細考慮容許用戶輸入的類型,並刪除可能有害的字符,如輸入字符中的 「.」 「/」 「\」。

XSS

XSS 又叫 CSS (Cross Site Script) ,跨站腳本攻擊。它指的是惡意攻擊者往 Web 頁面裏插入惡意 html 代碼,當用戶瀏覽該頁之時,嵌入其中 Web 裏面的 html 代碼會被執行,從而達到惡意攻擊用戶的特殊目的。
1 <body>
2     <?php
3         $searchQuery = $_GET['q'];
4         /* some search magic here */
5     ?><h1>You searched for: <?php echo $searchQuery; ?></h1></body>

 

由於咱們把用戶的內容直接打印出來,不通過任何過濾,非法用戶能夠拼接 URL: search.php?q=%3Cscript%3Ealert(1)%3B%3C%2Fscript%3E

PHP 渲染出來的內容以下,能夠看到 Javascript 代碼會被直接執行:

1 <body>
2 <h1>You searched for: <script>alert(1);</script></h1>
3 <p>We found: Absolutely nothing because this is a demo</p>
4 </body>

 

Javascript 能夠:

  • 偷走你用戶瀏覽器裏的 Cookie;
  • 經過瀏覽器的記住密碼功能獲取到你的站點登陸帳號和密碼;
  • 盜取用戶的機密信息;
  • 你的用戶在站點上能作到的事情,有了 JS 權限執行權限就都能作,也就是說 A 用戶能夠模擬成爲任何用戶;
  • 在你的網頁中嵌入惡意代碼;

使用 htmlentities() 過濾特殊字符,防止大部分的 xss 攻擊

CSRF (跨站請求僞造)

例如網站上有用戶能夠用來註銷帳戶的連接。

<a href="http://your-website.com/delete-account">銷燬帳戶</a>

 

若是某個用戶評論:

<img src=」http://your-website.com/delete-account」> wow

 

用戶將在查看此評論的時候刪除他們的帳號。

laravel 的 web 路由默認開啓了 csrf 驗證,原理是在客戶端產生一個隨機的 token,在表單校驗時判斷這個 token 是不是這個頁面上的請求

相關文章
相關標籤/搜索