为什么叫探秘
使用过阿里云 OSS 存储 API 的用户和客服都知道有一个通用的环节就是 Authorization 的计算,以及请求头的一些添加规范,给用户和客服烙下了不小的 “心理阴影” ,尤其 OSS Authorization 头的计算,光文章就密密麻麻的一篇(反正我的密集恐惧症犯了)。由于本人趟过坑今天就手把手,嘴对嘴的把过程说下,“活”儿不好,请各路老司机温柔以待!
官网的文章:https://help.aliyun.com/document_detail/31951.html?spm=a2c4g.11186623.6.1012.5bb84b4e6wXZcI
PutObject 规范:https://help.aliyun.com/document_detail/31978.html?spm=a2c4g.11186623.6.994.1632734cIiNNmj
先了解名词
AccessKey:访问云产品的凭证,类似宝藏的锁,可以是主账号或者 RAM 子账号的。
Access Key Secret:访问云产品的秘钥,类似宝藏的钥匙,可以是主账号或者 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 等操作。
主账号 / RAM 子账号的 AccessKey 、Access Key Secret 获取方式如下
丑话说在前面
- 我也是个客服,并非正规阿里云研发代码,仅个人分享。
- 本次代码仅仅演示最简单的 OSS 上传操作,没有做过多的校验,请使用者自行判断提交信息的正确性。
- 如果 “活” 儿不好,请客官们手指一二,下次改正缺点。
“被窝” 脚本如下
确实是在被窝里花了 30 分钟写出来的~,环境基础 2.7.5 来编写,如移植到各位客户机器上有异常,请看下对应的库文件,或者兼容性问题。
使用说明(都是必选):
PSA1#: python beiwo.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 本地文件的名称
趟坑:
- 客观想要玩的 安全,可以加个外套 Content-MD5,但是不注意的人就会忘记加了,补充如下:根据协议 RFC 1864 对消息内容(不包括头部)计算 MD5 值获得 128 比特位数字,对该数字进行 base64 编码为一个消息的 Content-MD5 值,并且 MD5 是 大写 哦。
- 如果客观想要单独加项目 CanonicalizedOSSHeaders 一定要记得不仅在 Header 中加,你的 hmac 计算时也要加,来个板 “例”
- Content-Type 一定要在 hmac 计算中
- 如果遇到 client 计算的 MD5 和 Server 不一致的情况请直接使用 HTTPS 传输,很可能中间的网络设置有故障或者劫持时导致内存被篡改,只要将 url 改为 https:// 就是启动 HTTPS 协议 上传/ 下载 了。
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)
脚本源码:
from optparse import OptionParser
import urllib,urllib2
import datetime
import base64
import hmac
import sha
import os
import sys
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.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):
return True
else:
return False
# GET local GMT time
def GetGMT(self):
GMT = datetime.datetime.utcnow().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:
sys.exit('\033[1;31;40m[error:]\033[0mFail to read localfile')
try:
request = urllib2.Request(self.url, files)
except Exception:
sys.exit('\033[1;31;40m[error:]\033[0mraise AttributeError, attr')
try:
request.add_header('Host','ali-beijing.oss-cn-beijing.aliyuncs.com')
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)
sys.exit("\n%s"%response.headers)
except Exception,e:
sys.exit('\n\033[1;31;40m[error:]\033[0m%s'%e)
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 uploadd to oss")
parser.add_option("-f",dest="fi",help="Must fill localfile path")
(options, args) = parser.parse_args()
handler = Main(options)
if handler.CheckParse():
handler.PutObject()
else:
sys.exit('\033[1;31;40m[error:]\033[0mCheck your parse not null')
运行结果: