api接口安全驗證(sign簽名和token驗證)

背景

api的常用就不用多說了,在這個網絡時代,小到天氣信息,大到各種大數據平臺,我們的生活中充斥着各種各樣的api,面對各種複雜的用戶使用場景,你永遠不知道請求你的api的是一個普普通通的小白用戶,還是一個分分鐘偷的你連內褲品牌都藏不住的黑客,所以api的安全就非常重要了。
除了日常的漏洞修復、服務器維護,api的安全驗證防禦也是特別重要的一環。

api接口安全類型

api接口安全類型一般有以下幾種類型(不完全,能想到的就這些了,歡迎交流補充):

防禦類型 防禦方法
1.參數篡改 url簽名方式
2.防禦未授權用戶訪問 用戶token驗證
3.防禦未授權應用或者爬蟲 appid,appsecret保證授權訪問
4.防禦dos公積浪費資源 時間戳timestamp
5.防禦重要信息泄露 https加密傳輸
6.防禦重放攻擊 時間戳、隨機數等方法

獻醜代碼(TP5.2):

 protected $notCheckUserToken = array('V1.User/login', 'V1.User/register', 'V1.User/sendcode');// 不需要檢測用戶登錄的請求

/**
 * 公共初始化驗證信息
 */
protected function initialize()
{
    parent::initialize();
    $this->checkTime($this->request->only(['time'])); // 驗證時間是否超時
    $this->param = $this->request->param(true);// 獲取參數
    $this->checkParam($this->param); // 驗證參數規則,合法有效性
    $this->checkSign($this->param); // 驗證參數簽名,方式參數篡改
    // 排除不需要驗證登錄的請求後檢查用戶token
    if (!in_array($this->request->controller().'/'.$this->request->action(), $this->notCheckUserToken)) {
        $token = isset($this->param['token']) ? $this->param['token'] : '';
        $this->param['uid'] = $this->checkUserToken($token);
    }
}

1.參數篡改

防止參數篡改就是用md5或者sha1等加密函數或者自定義加密方法對請求的URL在客戶端進行加密生成sign/access_token,在請求時帶上這個生成的sign/access_token,以備服務器驗證。當請求到達服務器時,用同樣的加密方式對url進行加密,對比加密結果和客戶端請求帶來的sign/access_token,若兩者一致,則有效訪問,反之返回錯誤。以此來達到防參數篡改的目的。
爲了更嚴謹安全,通常也會增加“鹽”參數,“鹽”值可以預先保存在客戶端,加密時作爲參數參與加密,也可以通過動態的方式預先請求服務器獲取動態“鹽”值。
舉一個例子(PHP):

 /**
 * 服務器端根據參數生成請求籤名
 * 對參數進行字典排序,然後組合成字符串,先進行sha1編碼,然後對產生的結果進行MD5加密
 * @param $arr 參數集合
 * @return string
 */
public function generateSign($arr)
{
   // $app_secret 是一個靜態隨機字符串,在沒有和appid綁定的情況下,相當於一個“鹽”nonce
    unset($arr['sign']);
    ksort($arr, SORT_STRING); // 對除sign以外的請求的參數進行字典排序
    $str = $app_secret.implode('', $arr); // 將參數數組組合字符串
    $serverSign = md5(sha1($str));

    return $serverSign;
}

下面是驗證簽名的方法:

/**
 * 參數驗證簽名
 *
 * @param $arr
 *
 */
public function checkSign($arr)
{
    if (!isset($arr['sign'])) {
        $this->returnMsg(401, '請求籤名缺失!');
    }
    $clientSign = $arr['sign'];
    if ($clientSign != $this->generateSign($arr)) {
        $this->returnMsg(401, '請求籤名錯誤!');
    }
}

ruturnMsg方法內容:

/**
 * 返回數據統一處理json格式返回
 *
 * @param        $code 狀態碼
 * @param string $msg  消息字符串
 * @param array  $data 返回數據
 */
public function returnMsg($code, $msg = '', $data = [])
{
    echo json_encode(['code' => $code, 'msg' => $msg, 'data' => $data]);
    die;
}

2.未授權用戶訪問

對於一些需要用戶信息的請求,需要判斷用戶是否是授權用戶(註冊用戶/或者特殊權限),這時候需要使用user_token(token)
session保存),並返回給客戶端token保存。當用戶在下一次請求需要用戶驗證的url資源時,帶上token值,請求到達服務器後,與服務器端保存的token進行比對,一直則有權限訪問,反之則決絕客戶端請求並返回標識,客戶端跳轉到登錄頁。
生成token:

/**
 * 生成userToken用戶token信息
 * 生成方式:md5($uid+毫秒時間戳+4位隨機數)
 *
 * @param $uid
 * @return string
 */
protected function generateToken($uid)
{
    return md5($uid.uniqid().rand(1111, 9999));
}

驗證token:

/**
 * 檢查用戶token,判斷登錄狀態,還是要改成數據庫redis存儲比較好
 *
 * @param $token
 * @return mixed
 */
public function checkUserToken($token)
{
    if (!$token) $this->returnMsg(401, 'token參數缺失!');
    if (!Session::has($token)) {
        $this->returnMsg(401, '請先登錄!');
    }

    return session($token);
}

3.dos攻擊

先介紹一下dos攻擊:

DoS攻擊是指故意的攻擊網絡協議實現的缺陷或直接通過野蠻手段殘忍地耗盡被攻擊對象的資源,目的是讓目標計算機或網絡無法提供正常的服務或資源訪問,使目標系統服務系統停止響應甚至崩潰,而在此攻擊中並不包括侵入目標服務器或目標網絡設備。這些服務資源包括網絡帶寬,文件系統空間容量,開放的進程或者允許的連接。這種攻擊會導致資源的匱乏,無論計算機的處理速度多快、內存容量多大、網絡帶寬的速度多快都無法避免這種攻擊帶來的後果。

爲了防禦dos攻擊,在請求中加入時間戳timestamp是一個非常有效的辦法,讓地址具有超時機制,在設定的時間範圍內(5-10分鐘)可以訪問,反之則拒絕請求,時間戳超時機制是防禦dos攻擊的有效手段。

/**
 * 驗證時間戳,客戶端和服務器請求時間差不能大於10分鐘
 *
 * @param $arr 參數數組
 */
public function checkTime($arr)
{
    if (!isset($arr['time']) || intval($arr['time']) <= 1) {
        $this->returnMsg(401, '時間戳不正確!');
    }
    if (time()-$arr['time'] > 600) {
        $this->returnMsg(401, '請求超時!');
    }
}

4.重要信息泄露

重要信息泄露,對於一些安全性較高的應用或者銀行卡密碼等機密信息來說,傳遞明文信息是非常不安全的,所以就誕生了https安全訪問,SSL加密由Netscape公司在1994年發明,使用SSL+http的方式,實現http的加密訪問,後來出現了TLS加密,是在SSL 3.0的基礎上演化而來,但是兩者加密算法不同,所以兩者之間不通用。
應用或者web要使用https,需要證書。
在這裏插入圖片描述

5.重放攻擊

先解釋一下重放攻擊:

重放攻擊(Replay Attacks)又稱重播攻擊、回放攻擊或新鮮性攻擊(Freshness Attacks),是指攻擊者發送一個目的主機已接收過的包,來達到欺騙系統的目的,主要用於身份認證過程,破壞認證的正確性。
它是一種攻擊類型,這種攻擊會不斷惡意或欺詐性地重複一個有效的數據傳輸,重放攻擊可以由發起者,也可以由攔截並重發該數據的敵方進行。攻擊者利用網絡監聽或者其他方式盜取認證憑據,之後再把它重新發給認證服務器。從這個解釋上理解,加密可以有效防止會話劫持,但是卻防止不了重放攻擊。重放攻擊任何網絡通訊過程中都可能發生。重放攻擊是計算機世界黑客常用的攻擊方式之一,它的書面定義對不瞭解密碼學的人來說比較抽象。
重放攻擊的基本原理就是把以前竊聽到的數據原封不動地重新發送給接收方。很多時候,網絡上傳輸的數據是加密過的,此時竊聽者無法得到數據的準確意義。但如果他知道這些數據的作用,就可以在不知道數據內容的情況下通過再次發送這些數據達到愚弄接收端的目的。例如,有的系統會將鑑別信息進行簡單加密後進行傳輸,這時攻擊者雖然無法竊聽密碼,但他們卻可以首先截取加密後的口令然後將其重放,從而利用這種方式進行有效的攻擊。再比如,假設網上存款系統中,一條消息表示用戶支取了一筆存款,攻擊者完全可以多次發送這條消息而偷竊存款。

對於重放攻擊,上面的解釋中有一個詞最重要,就是重複發送,解決方案就是打破重複,防禦方法一般有三種(來源百度):

  1. 加隨機數。該方法優點是認證雙方不需要時間同步,雙方記住使用過的隨機數,如發現報文中有以前使用過的隨機數,就認爲是重放攻擊。缺點是需要額外保存使用過的隨機數,若記錄的時間段較長,則保存和查詢的開銷較大。
  2. 加時間戳。該方法優點是不用額外保存其他信息。缺點是認證雙方需要準確的時間同步,同步越好,受攻擊的可能性就越小。但當系統很龐大,跨越的區域較廣時,要做到精確的時間同步並不是很容易。
  3. 加流水號。就是雙方在報文中添加一個逐步遞增的整數,只要接收到一個不連續的流水號報文(太大或太小),就認定有重放威脅。該方法優點是不需要時間同步,保存的信息量比隨機數方式小。缺點是一旦攻擊者對報文解密成功,就可以獲得流水號,從而每次將流水號遞增欺騙認證端。
    在實際中,常將方法(1)和方法(2)組合使用,這樣就只需保存某個很短時間段內的所有隨機數,而且時間戳的同步也不需要太精確。

對付重放攻擊除了使用以上方法外,還可以使用挑戰一應答機制和一次性口令機制,而且似乎後面兩種方法在實際中使用得更廣泛。

總結

以上零零總總的寫了幾種api安全驗證的方法,防禦各種各樣的攻擊和惡意請求。當然一般來講,普通的api會選擇以上方法中的幾種或者自己定義更復雜的驗證,還是需要根據自己的需要來選擇使用,畢竟最適合的纔是最好的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章