> 文档中心 > 使用阿里云sts鉴权模式实践(php),抛去复杂的官方sdk,自由裸奔

使用阿里云sts鉴权模式实践(php),抛去复杂的官方sdk,自由裸奔


背景

      因为最近需要遇到客户端直接使用hackCode方式引入子账号,存在账号泄露问题,遂寻找一种对客户端影响较少也能保证账号安全的解决方法,所以就有了本文。

关于STS

  1. 阿里云 STS(Security Token Service)是阿里云提供的一种临时访问权限管理服务。
  2. 通过 STS 服务,您所授权的身份主体(RAM 用户、RAM 用户组或 RAM 角色)可以获取一个自定义时效和访问权限的临时访问令牌。STS令牌持有者可以通过以下方式访问阿里云资源:
    • 通过编程方式访问被授权的阿里云服务 API。
    • 登录阿里云控制台操作被授权的云资源。
      *RAM (Resource Access Management) 是阿里云为客户提供的用户身份管理与资源访问控制服务。

为什么要使用RAM和STS

  1. RAM和STS需要解决的一个核心问题是如何在不暴露主账号的AccessKey的情况下安全的授权别人访问。因为一旦主账号的AccessKey暴露出去的话会带来极大的安全风险,别人可以随意操作该账号下所有的资源,盗取重要信息等。
  2. RAM提供一种长期有效的权限控制机制,通过分出不同权限的子账号,将不同的权限分给不同的用户,这样一旦子账号泄露也不会造成全局的信息泄露。但是,由于子账号在一般情况下是长期有效的,因此,子账号的AccessKey也是不能泄露的。
  3. 相对于RAM提供的长效控制机制,STS提供的是一种临时访问授权。通过STS可以返回临时的AccessKey和Token,这些信息可以直接发给临时用户用来访问OSS/VOD服务等。一般来说,从STS获取的权限会受到更加严格的限制,并且拥有时间限制,因此这些信息泄露之后对于系统的影响也很小。

STS 访问点

用于 API 访问的 STS 接入点:https://sts.aliyuncs.com。

STS 术语表

术语 说明
RAM Role 一种虚拟的RAM用户。
Role Arn Role ARN 是角色的全局资源描述符,用来指定具体角色。每个角色都有一个唯一的全局资源描述符。格式:acs:ram::$accountID:role/$roleName
Trusted Entity 角色的受信主体是指可以扮演角色的实体用户身份。创建角色时必须指定受信主体,角色只能被受信的主体扮演。受信主体可以是受信的阿里云云账号,或者受信阿里云服务。
Assume Role 扮演角色是实体用户获取角色身份的安全令牌的方法。一个实体用户通过调用 AssumeRole 的 API 可以获得角色的安全令牌,使用安全令牌可以访问云服务 API。

STS鉴权模式

OSS可以通过阿里云STS (Security Token Service) 进行临时授权访问。阿里云STS是为云计算用户提供临时访问令牌的Web服务。通过STS,您可以为第三方应用或子用户(即用户身份由您自己管理的用户)颁发一个自定义时效和权限的访问凭证。
STS鉴权模式具有以下优势:

  • 无需透露您的长期密钥(AccessKey)给第三方应用,只需生成一个访问令牌并将令牌交给第三方应用。您可以自定义这个令牌的访问权限及有效期限。
  • 无需关心权限撤销问题,访问令牌过期后访问权限会自动失效。

以APP应用为例,交互流程如图所示(来自阿里云官方文档):

以上(来自阿里云文档)只是为让大家先对STS有初步的了解,以及应用场景,下面进入正题

--------------------------------分割线-----------------------------

如何通过STS获取安全令牌呢?

1.先上代码

class STS{    protected $url = 'https://sts.aliyuncs.com';    protected $accessKeySecret = '1234567890qwertyuioasdfghj';    protected $accessKeyId = 'LT11234567898';    protected $roleArn = 'acs:ram::$accountID:role/$roleName';//指定角色的 ARN ,角色策略权限    protected $roleSessionName = 'client1';//用户自定义参数。此参数用来区分不同的 token,可用于用户级别的访问审计。格式:^[a-zA-Z0-9\.@\-_]+$    protected $durationSeconds = '1800';//指定的过期时间    protected $type = 'xxx';//方便调用时获取不同的权限    public function __construct($type)    { $this->type = $type; $this->setRoleArn();    }    public function sts()    { $action = 'AssumeRole';//通过扮演角色接口获取令牌 date_default_timezone_set('UTC'); $param = array(     'Format'    => 'JSON',     'Version'   => '2015-04-01',     'AccessKeyId'      => $this->accessKeyId,     'SignatureMethod'  => 'HMAC-SHA1',     'SignatureVersion' => '1.0',     'SignatureNonce'   => $this->getRandChar(8),     'Action'    => $action,     'RoleArn'   => $this->roleArn,     'RoleSessionName'  => $this->roleSessionName,     'DurationSeconds'  => $this->durationSeconds,     'Timestamp' => date('Y-m-d') . 'T' . date('H:i:s') . 'Z'     //'Policy'=>'' //此参数可以限制生成的 STS token 的权限,若不指定则返回的 token 拥有指定角色的所有权限。 ); $param['Signature'] = $this->computeSignature($param, 'POST'); $res = CurlHandle::httpPost($this->url, $param);//curl post请求 if ($res) {     return self::_render($res); } else {     return []; }    }    private static function _render($res)    { $res = json_decode($res, true); if (empty($res['Credentials'])) {     return []; } else {     return [  'accessKeySecret' => $res['Credentials']['AccessKeySecret'] ?? '',  'accessKeyId'     => $res['Credentials']['AccessKeyId'] ?? '',  'expiration'      => $res['Credentials']['Expiration'] ?? '',  'securityToken'   => $res['Credentials']['SecurityToken'] ?? '',     ]; }    }    protected function computeSignature($parameters, $setMethod)    { ksort($parameters); $canonicalizedQueryString = ''; foreach ($parameters as $key => $value) {     $canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value); } $stringToSign = $setMethod . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1)); $signature = $this->getSignature($stringToSign, $this->accessKeySecret . '&'); return $signature;    }    public function getSignature($source, $accessSecret)    { return base64_encode(hash_hmac('sha1', $source, $accessSecret, true));    }    protected function percentEncode($str)    { $res = urlencode($str); $res = preg_replace('/\+/', '%20', $res); $res = preg_replace('/\*/', '%2A', $res); $res = preg_replace('/%7E/', '~', $res); return $res;    }    public function getRandChar($length)    { $str = null; $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; $max = strlen($strPol) - 1; for ($i = 0; $i type == '123') {//根据入参使用不同的策略,当然这里还可以有其他写法兼容更多的策略的情况     $this->roleArn = 'acs:ram::123456789098:role/=$roleName'; }    }}

 

2.返回结果

{    "Credentials": { "AccessKeyId": "STS.xxxxxxxxxxxxx****",//访问密钥标识 "AccessKeySecret": "xxxxxxxxxx****",//访问密钥 "Expiration": "2019-04-09T11:52:19Z",//失效时间 "SecurityToken": "********"//安全令牌    },    "AssumedRoleUser": { "arn": "acs:sts::123456765456****:assumed-role/AdminRole/client", "AssumedRoleUserId":"1234567121****:alice" },    "RequestId": "xxxxxxxxxxxxxxxx"}

相信大家看完前面的解析、代码及返回结果后大概对怎么使用STS鉴权模式有一定了解;返回结果中对于客户端APP来讲需要用的有AccessKeyId、AccessKeySecret、Expiration、SecurityToken,客户端拿到这四个参数后将其传给阿里云ossSDK,vodSDK等便可以正常使用oss上传服务或者短视频vod服务了。

使用STS鉴权具体步骤

  1. 在阿里云后台开通一个RAM子账号
  2. 新建角色(表示某种操作权限),授权相应的策略
  3. 通过调用STS的AssumeRole接口,传递RAM子账号的AccessKeyId、AccessKeySecret以及指定角色的 ARN ,角色策略权限 roleArn 获取临时授权

    AccessKeyId": "STS.xxxxxxxxxxxxx****",//访问密钥标识"AccessKeySecret": "xxxxxxxxxx****",//访问密钥"Expiration": "2019-04-09T11:52:19Z",//失效时间"SecurityToken": "********"//安全令牌

     

  4. 然后…emmm…把想法写成代码逻辑。

  5. 附:阿里云对RAM和STS具体介绍文档地址:https://help.aliyun.com/document_detail/31931.html?spm=a2c4g.11186623.2.10.34c51388aBbFjR#concept-nsb-brz-5db