1. 程式人生 > >API安全設計(1)

API安全設計(1)

技術 ref 項目 creat 升序 服務器 sig base test

1、API簡介

這段時間和外部公司合作,一直在寫對外API接口。提供的API接口是基於http協議的,也是無狀態的。每次請求都必須帶上身份認證信息。後臺服務對身份信息進行校驗。
基於HTTP協議的API身份認證有很多種方式,有HTTP Basic,HTTP Digest,API KEY,Oauth,JWK等方式,我這裏只講基於項目中的API KEY校驗。

2、API KEY

API接口均通過請求頭(HEADER)中傳遞的 TOKEN(授權令牌)來進行身份認證和鑒權, 系統會在校驗 TOKEN 的正確性和時效性。
API Key就是經過用戶身份認證之後服務端給客戶端分配一個API Key,類似:http://test/api/package/create,
將生成的token放在頭部進行傳輸。

一般的處理流程如下: 一個簡單的設計示例如下:
技術分享圖片

3、客戶端生成TOKEN

計算sign和token的PHP參考代碼:
設計思路:
user_id: 授權客戶的 id;
app_key:開通 API 授權後獲得的私鑰;
ts: 發起請求時的時間戳, 精確到秒,這個值跟接收到請求時的服務器時間戳,值偏差(正或負)超過 1200 秒( 20 分鐘)時,請求會被拒絕,要求調用方重新生成;
token(請使用 CCT +08:00 中國北京時間);
sign: 簽名字符串,對輸入參數的鍵值key進行升序排列,轉換成型如k1=v1&k2=v2的字符串,然後拼接時間戳和app_key,最後進行sha1混淆;

function getToken($inputArr)
{
    //當前unix時間戳
    $userId = ‘3322991‘;
    $appKey = ‘abc123‘;
    $ts = time();
    $sign = get_sign($inputArr, $ts, $appKey);
    $token = base64_encode($userId . ‘,‘ . $ts . ‘,‘ . $sign);

    return $token;
}

function getSign($inputArr, $ts, $appKey)
{
    ksort($inputArr);
    $inputStr = urlencode(http_build_query($inputArr));
    $sign = sha1($inputStr . $ts . $appKey);
    return $sign;
}

4、服務端解析TOKEN

後臺服務端解析TOKEN的主要思路是:
1、從頭部header獲取token參數;
2、根據token得到user_id、ts和sign;
3、然後根據user_id、ts和請求參數新生成簽名sign;
4、校驗app_key是否合法,校驗ts的時效性,校驗新生成的簽名sign和傳的簽名sign是否一致。

    /**
     * 解析TOKEN
     * @param $token
     * @param array $inputArr
     * @return array
     */
    function decToken($token, $inputArr = [])
    {
        $tokenInfo = base64_decode($token);
        $tokenInfo = explode(‘,‘, $tokenInfo);
        if (count($tokenInfo) != 3) {
            Error::trigger(‘TOKEN信息錯誤‘);
        }
        
        list($userId, $time, $sign) = $tokenInfo;
        Log::info("check token params,userId:{$userId},time :{$time},sign:{$sign},inputArr:" . json_encode($inputArr));
        
        // 時間有效性校驗
        if (abs(time() - $time) > \App::getConfig(‘api_token_expire_time‘)) {
            Error::trigger(Error::ERR_PARAM_TOKEN_TIME);
        }
        
        // 簽名校驗
        $tokenObj = new Token();
        // 獲取用戶appKey信息,根據實際項目生成app_key的規則而定
        $appKey = ‘*****************‘;
        
        $generateSign = $this->getSign($inputArr, $time, $appKey);
        
        // 校驗參數生成的token
        $newToken = $this->getToken($inputArr, $userId, $appKey, $time);
        Log::info("token:{$token},newToken:" . json_encode($newToken));
        Log::info("sign:{$sign},newSign:" . json_encode($generateSign));
        
        if ($sign !== $generateSign) {
            Error::trigger(Error::ERR_PARAM_TOKEN_SIGN);
        }
        
        Log::info(‘token decoded successfully‘);
        return $appKey;
    }

API安全設計(1)