Dernière activité 1 week ago

Révision f11faf40ecef25d53b4f943ec4eca3741e732986

hooks.yaml Brut
1- id: debug
2 execute-command: /usr/local/bin/log-hook.sh
3 include-command-output-in-response: true
4 pass-arguments-to-command:
5 - source: entire-headers
6 - source: raw-request-body
7- id: liberaforms
8 execute-command: /usr/local/bin/verify-hook.py
9 include-command-output-in-response: true
10 pass-arguments-to-command:
11 - source: header
12 name: X-Timestamp
13 - source: header
14 name: X-Signature-256
15 - source: raw-request-body
16 pass-environment-to-command:
17 - source: string
18 name: foobar
19 envname: WEBHOOK_SECRET
20 trigger-rule:
21 and:
22 - match:
23 type: regex
24 regex: '^[0-9a-f]{64}$'
25 parameter:
26 source: header
27 name: X-Signature-256
28 - match:
29 type: regex
30 regex: '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,6})?(Z|[+-]\d{2}:\d{2})$'
31 parameter:
32 source: header
33 name: X-Timestamp
verify-hook.py Brut
1#!/usr/bin/env python3
2import os
3import sys
4import hmac
5import hashlib
6from datetime import datetime, timezone
7
8MAX_SKEW = 300 # secondes
9
10def main():
11 if len(sys.argv) != 4:
12 print("INVALIDE: arguments manquants (attendu: timestamp signature raw_body)")
13 return 1
14
15 timestamp, signature, body_arg = sys.argv[1:4]
16 signature = signature.removeprefix("sha256=")
17
18 secret = os.environ.get("WEBHOOK_SECRET", "")
19 if not secret:
20 print("INVALIDE: WEBHOOK_SECRET non défini")
21 return 1
22
23 # 1. Fraîcheur du timestamp
24 try:
25 ts = datetime.fromisoformat(timestamp)
26 if ts.tzinfo is None:
27 ts = ts.replace(tzinfo=timezone.utc)
28 except ValueError:
29 print(f"INVALIDE: timestamp illisible ({timestamp})")
30 return 1
31
32 skew = abs((datetime.now(timezone.utc) - ts).total_seconds())
33 if skew > MAX_SKEW:
34 print(f"INVALIDE: timestamp trop ancien/futur ({skew:.0f}s d'écart)")
35 return 1
36
37 # 2. Signature sur les octets bruts + timestamp
38 # os.fsencode restitue les octets exacts reçus dans argv
39 raw_body = os.fsencode(body_arg)
40
41 expected = hmac.new(
42 secret.encode("utf-8"),
43 raw_body + timestamp.encode("utf-8"),
44 hashlib.sha256,
45 ).hexdigest()
46
47 if not hmac.compare_digest(expected, signature):
48 print("INVALIDE: signature incorrecte")
49 return 1
50
51 print("VALIDE")
52 return 0
53
54if __name__ == "__main__":
55 sys.exit(main())
56