PHP 開發API接口簽名驗證


就安全來說,所有客戶端和服務器端的通信內容應該都要通過加密通道(HTTPS)傳輸,明文的HTTP通道將會是man-in-the- middle及其各種變種攻擊的溫床。所謂man-in-the-middle攻擊簡單講就是指惡意的黑客可以在客戶端和服務器端的明文通信通道上做手 腳,黑客可以監聽通信內容,偷取機密信息,甚至可以篡改通信內容,而通過加密后的通信內容理論上是無法被破譯的。

URL簽名生成規則

API的有效訪問URL包括以下三個部分:
1. 資源訪問路徑,如/v1/deal/find_deals;
2. 請求參數:即API對應所需的參數名和參數值param=value,多個請求參數間用&連接如deal_id=1-85462&appid=00000;
3. 簽名串,由簽名算法生成

簽名算法如下:

1. 對所有請求參數進行字典升序排列;
2. 將以上排序后的參數表進行字符串連接,如key1value1key2value2key3value3...keyNvalueN;
3. app secret作為后綴,對該字符串進行SHA-1計算,並轉換成16進制編碼;
4. 轉換為全大寫形式后即獲得簽名串

 

注意:請保證HTTP請求數據編碼務必為UTF-8格式,URL也務必為UTF-8編碼格式。

 

舉個實例:

PHP服務端先要給開發者(APP)分配一個appid與appsecret (正常情況下,開發者要到服務提供商的官網申請),作為客戶端,需要保留好官方頒發的appid & appsecret 

appid會在請求中作為一個應用標識參與接口請求的參數傳遞,appsecret 將作為唯一不需要參數傳遞,但是它將作為驗證當前請求的關鍵參數,只有應用開發者和頒發的服務端才知道。由於簽名是依靠同樣的算法加密實現,因此,應用端和服務端可以計算出相同的簽名值,簽名實際意義在於服務端對客戶端的訪問身份認證。在某種意義上簽名機制有點類似用公鑰方法簽名,用每個應用對應的私鑰值來解密,只是這種解密過程實質就是核對簽名參數值的過程。
假設分配:

$appid=5288971;
$appsecret= 'r5e2t85tyu142u665698fzu';

移動客戶端,需要請求服務列表(以下代碼可以為java或sf等移動端編寫)
請求地址: http://web.com/server/list

參數:

$array=[
'appid'=>5288971,
'menu'=>'客戶服務列表',
'lat'=>21.223,
'lng'=>131.334
    ];

 

對應簽名算法

// 1. 對加密數組進行字典排序 防止因為參數順序不一致而導致下面拼接加密不同
ksort($array);
 // 2. 將Key和Value拼接
 $str = "";
foreach ($array as $k => $v) {
 $str.= $k.$v;
}

//3. 通過sha1加密並轉化為大寫
//4. 大寫獲得簽名
$restr=$str.$appsecret;
$sign = strtoupper(sha1($restr)); 

將生產的sign簽名一起寫入array中,通過約定好的method方式發送參數到請求接口

$array['sign']=$sign;

 

打印$array

Array
(
    [appid] => 5288971
    [menu] => 客戶服務列表
    [lat] => 21.223
    [lng] => 131.334
    [sign] => C096D7811E944386CE880597BA334A5AB640B088
)

客戶端將數據封裝xml或Json發送到服務端,服務端先解析

{"appid":5288971,"menu":"\u5ba2\u6237\u670d\u52a1\u5217\u8868","lat":21.223,"lng":131.334,"sign":"C096D7811E944386CE880597BA334A5AB640B088"}
$serverArray= json_decode($json,TRUE);

服務端查詢appid對應的密鑰

$model=Model::find()->where("appid=:appid")->params([":appid"=>$serverArray['appid']])->one();
 if($model){
   $serverSecret=$model->appsecret;
}

 按照相同的字典排序與算法生成服務端的$sign ,判斷$sign 是否相同。

$clientSign=$serverArray['sign'];
unset($serverArray['sign']);
#生成服務端str
$serverstr = "";
foreach ($serverArray as $k => $v) {
 $serverstr.= $k.$v;
}
$reserverstr=$serverstr.$serverSecret;
$reserverSign = strtoupper(sha1($reserverstr));

if($clientSign!=$reserverSign){
    die('非法請求');
}else{
 //    your code continue;
}

 

在僅適用短信登錄做手機端app時,可以設置secret的過期時間,短信登錄后,保存appid(userid)與密鑰secret,每當用戶打開APP時,先聯網請求登錄是否過期,過期重新短信登錄獲取新的secret。

 

附加:

有時,我們使用hash_hmac進行加密(我們項目中使用……)

/*
 * 生成簽名,$args為請求參數,$key為私鑰
 */
function makeSignature($args, $key)
{
    if(isset($args['sign'])) {
        $oldSign = $args['sign'];
        unset($args['sign']);
    } else {
        $oldSign = '';
    }

    ksort($args);
    $requestString = '';
    foreach($args as $k => $v) {
        $requestString .= $k . '=' . urlencode($v);
    }
    $newSign = hash_hmac("md5",strtolower($requestString) , $key);
    return $newSign;
}

 

 

javascript 進行加密簽名

微信小程序開發的時候,為了數據的安全,也可以用js對數據簽名,發送服務器進行效驗認證.

先引入md5.js

<script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.js"></script>

 

 var postData =<?php echo json_encode($data); ?>;
    var token ='<?php echo token; ?>';
    /**
     * json 排序 
     * 先排序再toLower,所以Did 在appid 之前
     */
    function jsonSort(jsonObj) {
        let arr = [];
        for (var key in jsonObj) {
            arr.push(key)
        }
        arr.sort();
        let str = '';
        for (var i in arr) {
            str += arr[i].toLowerCase() + "=" + jsonObj[arr[i]].toLowerCase();
        }
        return str
    }

    strData = jsonSort(postData);
    var sign = md5(strData, token);
    postData['sign'] = sign;
    console.log(postData);

 JS-PHP 加密解密demo: https://files.cnblogs.com/files/dcb3688/php.javascript.encrypMD5.php.7z


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM