OSS 实践篇-OSS API 鉴权剖析

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: 出现 signature 一般出现客户端自签名调 API 的操作中, signature 的计算稍微复杂点,建议最好用 SDK 来替代计算的过程和多样性。如果业务强需求,先要读懂如果计算 signature。

背景

使用过阿里云 OSS 存储 API 的用户都知道,如果 OSS 是私有的权限,需要进行验签才能访问。验签过程要求客户端请求的 http request header 中有一个 Authorization(鉴权) 的 header,计算的复杂性和带来的很多问题让客户头痛不已,尤其 OSS Authorization 头的计算,今天带这大家剖析下鉴权的 API。

使用规范预热

官网鉴权文章:
header 签名
[URL 签名](URL 中携带签名。
https://help.aliyun.com/document_detail/31952.html)
PutObject 规范

签名区别

Header URL
不支持设置 expires ,但是要求请求时间不能超过 15min 支持设置 expires
常用 method GET、POST、PUT 常用 method GET、PUT
Date 时间是 GMT 格式 Date 替换成 expires 变成时间戳
signature 不需要 URL encode signature 需要 URL encode

鉴权名词

AccessKey

简称 AK ,访问云产品的凭证,类似宝藏的锁,可以是主账号或者 RAM 子账号的。

Access Key Secret

简称 SK,访问云产品的秘钥,类似宝藏的钥匙,可以是主账号或者 RAM 子账号的。

Authorization

Header 来包含签名(Signature)信息,表明这个消息已被授权。

Signature

经过各种计算得到的鉴权指纹信息。

CanonicalizedOSSHeaders

访问 OSS 时,用户想要加的自定义头,必须以 "x-oss-" 为头的前缀。如果使用,也必须要加到 Signature 中计算。

CanonicalizedResource

访问 OSS 的资源 object,结构是 /bucket/ object,如下:

  • /zhangyibo/Japan/video/tokhot.avi 将视频上传到 bucket 为 zhangyibo 的虚拟目录 Japan 下面,命名为 tokhot.avi ,可以是 PUT / GET 等操作。
  • /zhangybi/ 针对 bucket 是 zhangyibo 进行的操作,可以是 PUT / GET 等操作。

主账号 AK SK 获取方式

image.png

子账号 AK SK获取方式

进入到 RAM 访问控制台,找到对应的子账号

image.png

计算鉴权

当客户通过 header 或者 URL 中自签名计算 signature 时,经常会遇到计算签名失败 “The request signature we calculated does not match the signature you provided” ,可以参考以下 demo 演示了如何调用 API 自签名时上传 Object 到 OSS,注意签名和 header 加入的内容。

使用方法

$PSA1#: python Signature.py -h
Usage: beiwo.py [options]

Options:
  -h, --help  show this help message and exit
  -i AK       Must fill in Accesskey          访问云产品的 Accesskey
  -k SK       Must fill in AccessKeySecrety   访问云产品的 Accesskey Secret
  -e ED       Must fill in endpoint           OSS 的 endpoint 地理信息
  -b BK       Must fill in bucket             OSS bucket 
  -o OBJECTS  File name uploaded to oss       上传的 object 名称
  -f FI       Must fill localfile path        本地文件的名称
#! /us/bin/env python
#Author: hanli
#Update: 2018-09-29

from optparse import OptionParser
import urllib, urllib2
import datetime
import base64
import hmac
import sha
import os
import sys
import time


class Main():

# Initial input parse

def __init__(self,options):

  self.ak = options.ak
  self.sk = options.sk
  self.ed = options.ed
  self.bk = options.bk
  self.fi = options.fi
  self.oj = options.objects
  self.left = '\033[1;31;40m'
  self.right = '\033[0m'
  self.types = "application/x-www-form-urlencoded"    
  self.url = 'http://{0}.{1}/{2}'.format(self.bk,self.ed,self.oj)

# Check client input parse

def CheckParse(self):

  if (self.ak and self.sk and self.ed and self.bk and self.oj and self.fi) != None:
    if str(self.ak and self.sk and self.ed and self.bk and self.oj and self.fi):
      self.PutObject()
  else:
    self.ConsoleLog("error","Input parameters cannot be empty")

# GET local GMT time

def GetGMT(self):

  SRM = datetime.datetime.utcnow()
  GMT = SRM.strftime('%a, %d %b %Y %H:%M:%S GMT')

  return GMT

# GET Signature

def GetSignature(self):

  mac = hmac.new("{0}".format(self.sk),"PUT\n\n{0}\n{1}\n/{2}/{3}".format(self.types,self.GetGMT(),self.bk,self.oj), sha)
  Signature = base64.b64encode(mac.digest())

  return Signature

# PutObject

def PutObject(self):

  try: 
    with open(self.fi) as fd:
      files = fd.read()
  except Exception as e:
    self.ConsoleLog("error",e)

  try:
    request = urllib2.Request(self.url, files)
    request.add_header('Host','{0}.{1}'.format(self.bk,self.ed))
    request.add_header('Date','{0}'.format(self.GetGMT()))
    request.add_header('Authorization','OSS {0}:{1}'.format(self.ak,self.GetSignature()))
    request.get_method = lambda:'PUT'
    response = urllib2.urlopen(request,timeout=10)
    fd.close()
    self.ConsoleLog(response.code,response.headers)
  except Exception,e:
    self.ConsoleLog("error",e)

# output error log

def ConsoleLog(self,level=None,mess=None):

  if level == "error":
    sys.exit('{0}[ERROR:]{1}{2}'.format(self.left,self.right,mess))
  else:
    sys.exit('\nHTTP/1.1 {0} OK\n{1}'.format(level,mess))

if __name__ == "__main__":

parser = OptionParser()
parser.add_option("-i",dest="ak",help="Must fill in Accesskey")
parser.add_option("-k",dest="sk",help="Must fill in AccessKeySecrety")
parser.add_option("-e",dest="ed",help="Must fill in endpoint")
parser.add_option("-b",dest="bk",help="Must fill in bucket")
parser.add_option("-o",dest="objects",help="File name uploaded to oss")
parser.add_option("-f",dest="fi",help="Must fill localfile path")

(options, args) = parser.parse_args()
handler = Main(options)
handler.CheckParse()

### 请求头

PUT /yuntest HTTP/1.1
Accept-Encoding: identity
Content-Length: 147
Connection: close
User-Agent: Python-urllib/2.7
Date: Sat, 22 Sep 2018 04:36:52 GMT
Host: yourBucket.oss-cn-shanghai.aliyuncs.com
Content-Type: application/x-www-form-urlencoded
Authorization: OSS B0g3mdt:lNCA4L0P43Ax

响应头

HTTP/1.1 200 OK
Server: AliyunOSS
Date: Sat, 22 Sep 2018 04:36:52 GMT
Content-Length: 0
Connection: close
x-oss-request-id: 5BA5C6E4059A3C2F
ETag: "D0CAA153941AAA1CBDA38AF"
x-oss-hash-crc64ecma: 8478734191999037841
Content-MD5: 0MqhU5QbIp3Ujqqhy9o4rw==
x-oss-server-time: 15

注意事项

1、Signature 中所有加入计算的参数都要放在 header 中,保持 header 和 Signature 一致。

2、PUT 上传时,Signature 计算的 Content-Type 必须是 application/x-www-form-urlencoded 。

3、通过header 方式进行签名认证时无法设置过期时间。目前只有 SDK 、URL 签名支持设置过期时间。

4、用户想要保证文件一致性,可以在请求头增加 Content-MD5,但是不注意的人就会忘记加了,补充如下:根据协议 RFC 1864 对消息内容(不包括头部)计算 MD5 值获得 128 比特位数字,对该数字进行 base64 编码为一个消息的 Content-MD5 值,并且 MD5 是 大写。

5、如果用户想要单独加项目 CanonicalizedOSSHeaders 一定要记得不仅在 Header 中加,你的 hmac 计算时也要加

hmac.new("5Lic5Lqs5LiA54K56YO95LiN54Ot","PUT\n\napplication/x-www-form-urlencoded\nSun, 02 Sep 2018 03:20:05 GMT\nx-oss-video:tokhot.avi/zhangyibo/tokhot.avi", sha)

6、如果遇到 client 计算的 MD5 和 Server 不一致的情况请直接使用 HTTPS 传输,很可能中间的网络设置有故障或者劫持时导致内存被篡改,只要将 url 改为 https:// 就是启动 HTTPS 协议 上传/ 下载 了。

常见案例

通过微信小程序请求 OSS 返回签名失败,通过浏览器正常

1、只要通过浏览器访问,鉴权通过就证明 OSS 的签名校验是正常的没有问题,可以先排除掉 OSS 端。
2、客户端一定要在微信小程序上部署 HTTP 抓包,对后续分析很重要,抓包中可以看到所有的请求头和请求参数。
3、通过浏览器访问时的 HTTP 抓包。

image.png

结论:

1、通过 403 和 200 的抓包反复对比发现,通过小程序发出的 HTTP 请求和浏览器发起的 HTTP 请求的 URL 、signature、expires 都一样,唯一的区别就是微信小程序携带了 Content-type ,而通过 Chrom 的请求是没有携带 Content-type,怀疑矛头指向了这里。
2、经过代码确认,发现 signature 计算时是没有包含 Content-tpye 头的,而小程序发起的请求携带的 Content-tpye ,OSS 收到后会按照携带了 Content-tpye 去计算 signature ,所以每次计算都不一样。

结尾:

遇到类似问题,抓包是最能快速看到问题的。同时也必须要了解下 OSS 请求 header 中携带了 Content-tpye ,那么 signature 计算就要加上 Content-tpye ,保持一致。

多个 OSS SDK 测试,在 CDN 结合 OSS 场景时,客户端使用 CDN 域名计算 signature,发起 HEAD 请求,OSS 收到后返回 403

image.png

出现这个问题不区分什么 SDK 都会出现,问题原因是由于客户端发起的 HEAD 请求在通过 CDN 回原到 OSS 时,CDN 回原是用的 GET 请求,而 OSS 收到时就用 GET 请求方式去计算签名,得到的结果肯定和客户端计算不一致,可以升级到阿里云 CDN 处理。以上分析只适合上述场景。
问题可以通过 tcpdump 抓包或者 Wireshark 对比一下即可知道。

相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
目录
相关文章
|
1月前
|
JavaScript 前端开发 API
深入浅出:Vue 3 Composition API 的魅力与实践
【2月更文挑战第13天】 本文将探索 Vue 3 的核心特性之一——Composition API。通过对比 Options API,本文旨在揭示 Composition API 如何提高代码的组织性和可复用性,并通过实际案例展示其在现代前端开发中的应用。不同于传统的技术文章摘要,我们将通过一个具体的开发场景,引领读者步入 Composition API 的世界,展现它如何优雅地解决复杂组件逻辑的管理问题,从而激发读者探索和运用 Vue 3 新特性的热情。
19 1
|
2月前
|
数据采集 监控 算法
利用大数据和API优化电商决策:商品性能分析实践
在数据驱动的电子商务时代,大数据分析已成为企业提升运营效率、增强市场竞争力的关键工具。通过精确收集和分析商品性能数据,企业能够洞察市场趋势,实现库存优化,提升顾客满意度,并显著增加销售额。本文将探讨如何通过API收集商品数据,并将这些数据转化为对电商平台有价值的洞察。
|
17天前
|
XML JSON 安全
谈谈你对RESTful API设计的理解和实践。
RESTful API是基于HTTP协议的接口设计,通过URI标识资源,利用GET、POST、PUT、DELETE等方法操作资源。设计注重无状态、一致性、分层、错误处理、版本控制、文档、安全和测试,确保易用、可扩展和安全。例如,`/users/{id}`用于用户管理,使用JSON或XML交换数据,提升系统互操作性和可维护性。
14 4
|
23天前
|
消息中间件 缓存 API
微服务架构下的API网关性能优化实践
在现代的软件开发中,微服务架构因其灵活性和可扩展性被广泛采用。随着服务的细分与增多,API网关作为微服务架构中的关键组件,承担着请求路由、负载均衡、权限校验等重要职责。然而,随着流量的增长和业务复杂度的提升,API网关很容易成为性能瓶颈。本文将深入探讨API网关在微服务环境中的性能优化策略,包括缓存机制、连接池管理、异步处理等方面的具体实现,旨在为开发者提供实用的性能提升指导。
|
25天前
|
缓存 负载均衡 监控
构建高效微服务架构:API网关的作用与实践
【2月更文挑战第31天】 在当今的软件开发领域,微服务架构已成为实现系统高度模块化和易于扩展的首选方法。然而,随着微服务数量的增加,确保通信效率和管理一致性变得尤为重要。本文将探讨API网关在微服务架构中的核心角色,包括其在请求路由、安全性、负载均衡以及聚合功能方面的重要性。我们将通过具体案例分析,展示如何利用API网关优化后端服务,并讨论实施过程中的最佳实践和常见挑战。
|
28天前
|
搜索推荐 数据挖掘 API
1688商品详情API在电商平台中的应用与实践
随着电子商务的迅猛发展,越来越多的商家选择利用API(应用程序编程接口)来提升其在线业务的效率和用户体验。特别是在商品信息展示方面,1688商品详情API作为连接商家和消费者的重要桥梁,扮演着至关重要的角色。本文将深入探讨1688商品详情API的功能、应用场景以及如何通过该API提高电商平台的商品信息展示质量。
|
1月前
|
JSON API 数据格式
构建高效Python Web应用:Flask框架与RESTful API设计实践
【2月更文挑战第17天】在现代Web开发中,轻量级框架与RESTful API设计成为了提升应用性能和可维护性的关键。本文将深入探讨如何使用Python的Flask框架来构建高效的Web服务,并通过具体实例分析RESTful API的设计原则及其实现过程。我们将从基本的应用架构出发,逐步介绍如何利用Flask的灵活性进行模块化开发,并结合请求处理、数据验证以及安全性考虑,打造出一个既符合标准又易于扩展的Web应用。
615 4
|
1月前
|
JSON 前端开发 API
API商品接口对接使用:从理论到实践
随着电子商务的飞速发展,API商品接口已成为现代电子商务应用程序不可或缺的一部分。通过API商品接口,开发者可以轻松地从其他应用程序或服务中获取商品信息,实现快速、高效的电子商务功能。本文将探讨API商品接口的概念、对接使用的方法以及一个实际案例。
|
1月前
|
JSON JavaScript 前端开发
使用API接口获取商品数据:从入门到实践
随着电子商务的飞速发展,许多电商平台提供了API接口,允许开发者获取商品数据,以创建各种创新的应用。本文将详细介绍如何使用API接口获取商品数据,并通过代码示例进行演示。
|
2月前
|
缓存 Rust 安全
Rust中的RESTful API构建:实践与探索
本文详细阐述了在Rust编程语言中如何构建RESTful API的过程。我们将通过实际示例,介绍Rust的生态系统中用于构建API的流行库和框架,包括Actix-Web、Rocket和Gotham。此外,我们还将讨论RESTful设计原则、API安全性、性能优化等方面的内容,帮助读者在Rust中高效、安全地构建RESTful API。