如何给API签名

前言

有时候为了保护API,需要用到 API 签名,使用 API 签名的好处:

  • 让API只能被特定的人访问
  • 防止别人抓包拿到请求参数,通过篡改参数发起新的请求

客户端过程

  1. 给API调用者分配一个app_idapp_secretapp_secret调用者和服务端各保存一份,不对外泄露,app_id需要在调用 API 时作为参数传递。
  2. 除了app_id参数以外,另外增加noncetimestamp参数。timestamp是时间戳,nonce是一个随机字符串,每次调用API时都要重新生成,且需要保证唯一性。这两个参数主要用来防止重复请求。
  3. 将参数列表按字典升序排列,然后在末尾添加上app_secret参数,将参数列表拼接成一个字符串,对字符串做md5加密,得到signature签名,最后将签名也传递给API。

将参数列表拼接成字符串,可以有多种实现方法,在本文中我们使用这种方案:
1)将参数列表进行排序,按key字典升序排列
2)在末尾添加上app_secret参数
3)按key1=value1&key2=value2&key3=value3的格式,将参数列表拼接成一个字符串,其中value值要经过url编码处理
4)拼接完成后,做md5运算,生成散列值,此值便是签名

服务端过程

  1. 收到请求后,按照app_id参数查询到对应的app_secret
  2. 判断timestamp参数是否已过期
  3. 判断nonce参数是否已经被使用
  4. 剔除sign参数,将其它参数按照约定的签名计算方法,生成一个签名A,将签名Asign参数进行对比,如果一致则通过
  5. nonce参数标记为已使用(可以使用redis来存储,并设置过期时间)

注:
1)时间戳的超时时间不能设置得太小,需要考虑服务端和客户端时间可能会有时差的情况,推荐设置为5分钟
2)对于nonce参数,redis key的过期时间一定要大于时间戳的过期时间,比如时间戳过期时间是5分钟,那么redis key的过期时间就要 >= 5分钟

PHP代码示例

client.php


function genSignature(array $signArr, $appSecret): string
{
    ksort($signArr, SORT_STRING);
    $signArr['app_secret'] = $appSecret;

    $signStr = http_build_query($signArr);
    return md5($signStr);
}

function buildParams(array $params): array
{
    $appId = 'xxxx';
    $appSecret = 'yyyy';

    $params['app_id'] = $appId;
    $params['nonce'] = uniqid('', true);
    $params['timestamp'] = time();
    $params['sign'] = genSignature($params, $appSecret);

    return $params;
}

$params = [
    'username' => '小明',
    'gender' => '男',
];
var_dump(buildParams($params));

// TODO:使用生成的参数调用API接口

server.php


function signatureVerify(array $signArr, $appSecret): string
{
    unset($signArr['sign']);

    ksort($signArr, SORT_STRING);
    $signArr['app_secret'] = $appSecret;

    $signStr = http_build_query($signArr);
    return md5($signStr);
}

$params = $_GET;

$appId = $params['app_id'];
// TODO:根据app_id参数查找对应的app_secret
$appSecret = 'yyyy';

$mySign = signatureVerify($params, $appSecret);
if ($mySign === $params['sign']) {
    echo '签名验证成功';
} else {
    echo '签名验证失败';
}

// TODO:检查timestamp是否已过期
// TODO:检查nonce是否已被使用过
// TODO:将nonce存储到redis,并设置key的过期时间

关于app_secret的安全性

在上述整个过程中,最重要的是保证app_secret不外泄,如果app_secret外泄了,其他人就可以用它来生成签名。

但真实情况下,我们很难保证app_secret不外泄,如果客户端是server端还好说,但如果客户端是浏览器的话,由于网页代码都是可以被看到的,所以只要查看一遍网页的js代码,就可以找到app_secret

如果客户端是安卓APP,别人也可以通过拆解apk安装包,破解拿到app_secret

面对这种情况也没有完美的方法,毕竟源代码都被别人看到了,还能有啥安全可言。只能通过js代码混淆、apk包安全加固等方法来提高别人找到app_secret的难度。

加密

虽然我们给接口加了签名验证,但别人还是可以通过浏览器的开发者工具、或者抓包看到具体传递的参数名和参数值。

如果不想别人看到这些信息的话,可以将参数都加密,加密方法可以使用RSA加密(使用服务端公钥加密,服务端收到请求后使用私钥解密)、或者AES加密。

加密后,别人抓包看到的就都是密文了,可以提高别人破解接口的难度。

你可能感兴趣的:(其它,API,签名)