保护 Typeform Webhook Python
Securing Typeform Webhook Python
我正在尝试使用 Python/Django/DRF 接受来自 Typeform 的表单响应,但由于无法获得匹配的哈希值,我无法验证 webhook 请求。
以下是来自 Typeform 的说明:
1. Using the HMAC SHA-256 algorithm, create a hash (using created_token as a key) of the entire received payload as binary.
2. Encode the binary hash in base64 format.
3. Add prefix sha256= to the binary hash.
4. Compare the created value with the signature you received in the Typeform-Signature header from Typeform.
authentication.py
class TypeformAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
typeform_signature = request.META.get('HTTP_TYPEFORM_SIGNATURE')
data = request.body
secret_key = os.environ.get('TYPEFORM_SECRET_KEY')
if not typeform_signature:
return None
if typeform_signature:
hash = hmac.new(bytes(secret_key, encoding='utf-8'), data, hashlib.sha256)
actual_signature = 'sha256={}'.format(base64.b64encode(hash.digest()).decode())
user = User.objects.get(username='typeform-user')
if actual_signature == typeform_signature:
return(user, None)
else:
raise exceptions.AuthenticationFailed('Typeform signature does not match.')
else:
return None
示例负载
{
"event_id": "01DTXE27VQSA3JP8ZMP0GF9HCP",
"event_type": "form_response",
"form_response": {
"form_id": "OOMZur",
"token": "01DTXE27VQSA3JP8ZMP0GF9HCP",
"landed_at": "2019-11-30T05:55:46Z",
"submitted_at": "2019-11-30T05:55:46Z",
"definition": {
"id": "OOMZur",
"title": "Auto Liability (New Company)",
"fields": [
{
"id": "GnpcIrevGZQP",
"title": "What is your business name?",
"type": "short_text",
"ref": "3e60e064-f14c-4787-9968-0358e8f34468",
"properties": {}
}
]
},
"answers": [
{
"type": "text",
"text": "Lorem ipsum dolor",
"field": {
"id": "GnpcIrevGZQP",
"type": "short_text",
"ref": "3e60e064-f14c-4787-9968-0358e8f34468"
}
}
]
}
}
Typeform 生成的哈希
sha256=jdzKuFkijyBIMvmGyveHfcfzcNXUeQCuveNGP6CEdXk=
authentication.py 生成的哈希值
sha256=at4SsBIi2IXJ8vr1Ix3tHW7iK9q5KQfx20EBa+l9wKU=
我能够让它工作。以防万一有人在验证网络书时遇到问题。我找到了本指南并进行了改编。
而不是在身份验证内部处理它。我改为用视图处理它。
import hashlib
import hmac
import json
import base64
import os
@csrf_exempt
def inbound_application_create_view(request):
header_signature = request.META.get('HTTP_TYPEFORM_SIGNATURE')
if header_signature is None:
return HttpResponseForbidden('Permission denied.')
sha_name, signature = header_signature.split('=', 1)
if sha_name != 'sha256':
return HttpResponseServerError('Operation not supported.', status=501)
mac = hmac.new(force_bytes(os.environ.get('TYPEFORM_SECRET_KEY')), msg=force_bytes(request.body), digestmod=hashlib.sha256)
if not hmac.compare_digest(force_bytes(base64.b64encode(mac.digest()).decode()), force_bytes(signature)):
return HttpResponseForbidden('Permission denied.')
return HttpResponse('pong')
我正在尝试使用 Python/Django/DRF 接受来自 Typeform 的表单响应,但由于无法获得匹配的哈希值,我无法验证 webhook 请求。
以下是来自 Typeform 的说明:
1. Using the HMAC SHA-256 algorithm, create a hash (using created_token as a key) of the entire received payload as binary.
2. Encode the binary hash in base64 format.
3. Add prefix sha256= to the binary hash.
4. Compare the created value with the signature you received in the Typeform-Signature header from Typeform.
authentication.py
class TypeformAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
typeform_signature = request.META.get('HTTP_TYPEFORM_SIGNATURE')
data = request.body
secret_key = os.environ.get('TYPEFORM_SECRET_KEY')
if not typeform_signature:
return None
if typeform_signature:
hash = hmac.new(bytes(secret_key, encoding='utf-8'), data, hashlib.sha256)
actual_signature = 'sha256={}'.format(base64.b64encode(hash.digest()).decode())
user = User.objects.get(username='typeform-user')
if actual_signature == typeform_signature:
return(user, None)
else:
raise exceptions.AuthenticationFailed('Typeform signature does not match.')
else:
return None
示例负载
{
"event_id": "01DTXE27VQSA3JP8ZMP0GF9HCP",
"event_type": "form_response",
"form_response": {
"form_id": "OOMZur",
"token": "01DTXE27VQSA3JP8ZMP0GF9HCP",
"landed_at": "2019-11-30T05:55:46Z",
"submitted_at": "2019-11-30T05:55:46Z",
"definition": {
"id": "OOMZur",
"title": "Auto Liability (New Company)",
"fields": [
{
"id": "GnpcIrevGZQP",
"title": "What is your business name?",
"type": "short_text",
"ref": "3e60e064-f14c-4787-9968-0358e8f34468",
"properties": {}
}
]
},
"answers": [
{
"type": "text",
"text": "Lorem ipsum dolor",
"field": {
"id": "GnpcIrevGZQP",
"type": "short_text",
"ref": "3e60e064-f14c-4787-9968-0358e8f34468"
}
}
]
}
}
Typeform 生成的哈希
sha256=jdzKuFkijyBIMvmGyveHfcfzcNXUeQCuveNGP6CEdXk=
authentication.py 生成的哈希值
sha256=at4SsBIi2IXJ8vr1Ix3tHW7iK9q5KQfx20EBa+l9wKU=
我能够让它工作。以防万一有人在验证网络书时遇到问题。我找到了本指南并进行了改编。
而不是在身份验证内部处理它。我改为用视图处理它。
import hashlib
import hmac
import json
import base64
import os
@csrf_exempt
def inbound_application_create_view(request):
header_signature = request.META.get('HTTP_TYPEFORM_SIGNATURE')
if header_signature is None:
return HttpResponseForbidden('Permission denied.')
sha_name, signature = header_signature.split('=', 1)
if sha_name != 'sha256':
return HttpResponseServerError('Operation not supported.', status=501)
mac = hmac.new(force_bytes(os.environ.get('TYPEFORM_SECRET_KEY')), msg=force_bytes(request.body), digestmod=hashlib.sha256)
if not hmac.compare_digest(force_bytes(base64.b64encode(mac.digest()).decode()), force_bytes(signature)):
return HttpResponseForbidden('Permission denied.')
return HttpResponse('pong')