06/01 App enhanced

This commit is contained in:
2026-06-01 20:34:21 -04:00
parent ea79ae5fdb
commit 5bdeb7b13a
5 changed files with 114 additions and 38 deletions
+38 -35
View File
@@ -359,47 +359,50 @@ def webhook():
# Header format: Teller-Signature: t=<timestamp>,v1=<sig1>,v1=<sig2>
# Signed message: <timestamp>.<raw_body>
signing_secret = current_app.config.get('TELLER_WEBHOOK_SECRET', '')
if signing_secret:
sig_header = request.headers.get('Teller-Signature', '')
body = request.get_data()
if not signing_secret:
log.error('[teller] TELLER_WEBHOOK_SECRET not configured — rejecting webhook')
return jsonify({'error': 'Webhook verification not configured'}), 403
if not sig_header:
log.warning('[teller] missing Teller-Signature header')
return jsonify({'error': 'Missing signature'}), 401
sig_header = request.headers.get('Teller-Signature', '')
body = request.get_data()
# Parse header: t=<timestamp>,v1=<sig>,v1=<sig2>...
parts = dict(
(p.split('=', 1) if '=' in p else (p, ''))
for p in sig_header.split(',')
)
timestamp = parts.get('t', '')
# Collect all v1 signatures (may be multiple during key rotation)
signatures = [v for k, v in
[p.split('=', 1) for p in sig_header.split(',') if p.startswith('v1=')]
]
if not sig_header:
log.warning('[teller] missing Teller-Signature header')
return jsonify({'error': 'Missing signature'}), 401
if not timestamp or not signatures:
log.warning('[teller] malformed Teller-Signature header')
return jsonify({'error': 'Invalid signature'}), 401
# Parse header: t=<timestamp>,v1=<sig>,v1=<sig2>...
parts = dict(
(p.split('=', 1) if '=' in p else (p, ''))
for p in sig_header.split(',')
)
timestamp = parts.get('t', '')
# Collect all v1 signatures (may be multiple during key rotation)
signatures = [v for k, v in
[p.split('=', 1) for p in sig_header.split(',') if p.startswith('v1=')]
]
# Reject replays older than 5 minutes
import time
try:
if abs(time.time() - int(timestamp)) > 300:
log.warning('[teller] webhook replay attack detected')
return jsonify({'error': 'Timestamp too old'}), 401
except ValueError:
pass
if not timestamp or not signatures:
log.warning('[teller] malformed Teller-Signature header')
return jsonify({'error': 'Invalid signature'}), 401
# signed_message = timestamp + "." + raw_body
signed_message = f'{timestamp}.'.encode() + body
expected = hmac.new(
signing_secret.encode(), signed_message, hashlib.sha256
).hexdigest()
# Reject replays older than 5 minutes
import time
try:
if abs(time.time() - int(timestamp)) > 300:
log.warning('[teller] webhook replay attack detected')
return jsonify({'error': 'Timestamp too old'}), 401
except ValueError:
pass
if not any(hmac.compare_digest(expected, sig) for sig in signatures):
log.warning('[teller] webhook signature mismatch')
return jsonify({'error': 'Invalid signature'}), 401
# signed_message = timestamp + "." + raw_body
signed_message = f'{timestamp}.'.encode() + body
expected = hmac.new(
signing_secret.encode(), signed_message, hashlib.sha256
).hexdigest()
if not any(hmac.compare_digest(expected, sig) for sig in signatures):
log.warning('[teller] webhook signature mismatch')
return jsonify({'error': 'Invalid signature'}), 401
# Teller sends GET to verify the endpoint is reachable
if request.method == 'GET':