Django 主体编码 vs slack-api secret
Django body encoding vs slack-api secret
我正在按照 page 的说明进行操作。我正在构建一个 slack slash 命令处理服务器,但我无法重建签名来验证 slash 请求的真实性。
这是我的 Django 应用程序的代码片段(该视图使用 Django rest-framework APIView):
@property
def x_slack_req_ts(self):
if self.xsrts is not None:
return self.xsrts
self.xsrts = str(self.request.META['HTTP_X_SLACK_REQUEST_TIMESTAMP'])
return self.xsrts
@property
def x_slack_signature(self):
if self.xss is not None:
return self.xss
self.xss = self.request.META['HTTP_X_SLACK_SIGNATURE']
return self.xss
@property
def base_message(self):
if self.bs is not None:
return self.bs
self.bs = ':'.join(["v0", self.x_slack_req_ts, self.raw.decode('utf-8')])
return self.bs
@property
def encoded_secret(self):
return self.app.signing_secret.encode('utf-8')
@property
def signed(self):
if self.non_base is not None:
return self.non_base
hashed = hmac.new(self.encoded_secret, self.base_message.encode('utf-8'), hashlib.sha256)
self.non_base = "v0=" + hashed.hexdigest()
return self.non_base
这是在 class 中,其中 self.raw = request.body
django 请求和 self.app.signing_secret 是一个带有适当松弛秘密字符串的字符串。它不起作用,因为 self.non_base
产生了一个不准确的值。
现在,如果我打开交互式 python repl 并执行以下操作:
>>> import hmac
>>> import hashlib
>>> secret = "8f742231b10e8888abcd99yyyzzz85a5"
>>> ts = "1531420618"
>>> msg = "token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c"
>>> ref_signature = "v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503"
>>> base = ":".join(["v0", ts, msg])
>>> hashed = hmac.new(secret.encode(), base.encode(), hashlib.sha256)
>>> hashed.hexdigest()
>>> 'a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503'
您会认出引用的 link 示例。如果我将我的 django 应用程序中的值与我的示例之一一起使用,它在 repl 中工作但不在 django 应用程序中。
我的问题:我认为这是由于 self.raw.decode() 编码与我在 repl 中提取到 copy/paste 的打印输出不一致造成的。有没有人遇到过这个问题,解决方法是什么?我用 urllib.parse 库尝试了一些随机的东西......我怎样才能确保 request.body 编码与带有 get_data() 的烧瓶中的示例一致(如link)?
中的文档
更新:我定义了一个自定义解析器:
class SlashParser(BaseParser):
"""
Parser for form data.
"""
media_type = 'application/x-www-form-urlencoded'
def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as a URL encoded form,
and returns the resulting QueryDict.
"""
parser_context = parser_context or {}
request = parser_context.get('request')
raw_data = stream.read()
data = QueryDict(raw_data, encoding='utf-8')
setattr(data, 'raw_body', raw_data) # setting a 'body' alike custom attr with raw POST content
return data
要基于 进行测试,自定义解析器中的 raw_body 会生成与正常 "body" 完全相同的散列签名,但同样,在 repl 中复制粘贴以在外部进行测试DRF 有效。很确定这是一个编码问题,但完全不知所措...
我发现这个问题非常令人沮丧。
事实证明,签名密钥存储在一个太短的 str 数组中并且缺少尾随字符,这显然导致了消息的错误散列。
我正在按照 page 的说明进行操作。我正在构建一个 slack slash 命令处理服务器,但我无法重建签名来验证 slash 请求的真实性。
这是我的 Django 应用程序的代码片段(该视图使用 Django rest-framework APIView):
@property
def x_slack_req_ts(self):
if self.xsrts is not None:
return self.xsrts
self.xsrts = str(self.request.META['HTTP_X_SLACK_REQUEST_TIMESTAMP'])
return self.xsrts
@property
def x_slack_signature(self):
if self.xss is not None:
return self.xss
self.xss = self.request.META['HTTP_X_SLACK_SIGNATURE']
return self.xss
@property
def base_message(self):
if self.bs is not None:
return self.bs
self.bs = ':'.join(["v0", self.x_slack_req_ts, self.raw.decode('utf-8')])
return self.bs
@property
def encoded_secret(self):
return self.app.signing_secret.encode('utf-8')
@property
def signed(self):
if self.non_base is not None:
return self.non_base
hashed = hmac.new(self.encoded_secret, self.base_message.encode('utf-8'), hashlib.sha256)
self.non_base = "v0=" + hashed.hexdigest()
return self.non_base
这是在 class 中,其中 self.raw = request.body
django 请求和 self.app.signing_secret 是一个带有适当松弛秘密字符串的字符串。它不起作用,因为 self.non_base
产生了一个不准确的值。
现在,如果我打开交互式 python repl 并执行以下操作:
>>> import hmac
>>> import hashlib
>>> secret = "8f742231b10e8888abcd99yyyzzz85a5"
>>> ts = "1531420618"
>>> msg = "token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c"
>>> ref_signature = "v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503"
>>> base = ":".join(["v0", ts, msg])
>>> hashed = hmac.new(secret.encode(), base.encode(), hashlib.sha256)
>>> hashed.hexdigest()
>>> 'a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503'
您会认出引用的 link 示例。如果我将我的 django 应用程序中的值与我的示例之一一起使用,它在 repl 中工作但不在 django 应用程序中。
我的问题:我认为这是由于 self.raw.decode() 编码与我在 repl 中提取到 copy/paste 的打印输出不一致造成的。有没有人遇到过这个问题,解决方法是什么?我用 urllib.parse 库尝试了一些随机的东西......我怎样才能确保 request.body 编码与带有 get_data() 的烧瓶中的示例一致(如link)?
中的文档更新:我定义了一个自定义解析器:
class SlashParser(BaseParser):
"""
Parser for form data.
"""
media_type = 'application/x-www-form-urlencoded'
def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as a URL encoded form,
and returns the resulting QueryDict.
"""
parser_context = parser_context or {}
request = parser_context.get('request')
raw_data = stream.read()
data = QueryDict(raw_data, encoding='utf-8')
setattr(data, 'raw_body', raw_data) # setting a 'body' alike custom attr with raw POST content
return data
要基于
我发现这个问题非常令人沮丧。
事实证明,签名密钥存储在一个太短的 str 数组中并且缺少尾随字符,这显然导致了消息的错误散列。