You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat: Add sqlpage.hmac function for cryptographic signing
Co-authored-by: contact <[email protected]>
* feat: Add sqlpage.hmac function for secure data signing
Co-authored-by: contact <[email protected]>
* Test HMAC function with RFC vectors and update tests
Co-authored-by: contact <[email protected]>
* feat: Add sqlpage.hmac() function for secure signatures
Co-authored-by: contact <[email protected]>
* feat: Add base64 output option to hmac function
Co-authored-by: contact <[email protected]>
* Refactor hmac function for cleaner output formatting
Co-authored-by: contact <[email protected]>
* Add webhook HMAC signature validation tests
Co-authored-by: contact <[email protected]>
* Refactor HMAC function and update SQL examples for clarity and consistency
- Changed function parameters to remove Option types for data and key in the HMAC function.
- Improved SQL documentation and examples for HMAC usage, including clearer descriptions and updated error handling.
- Enhanced test cases for webhook HMAC validation to ensure accurate signature checks and responses.
- Removed obsolete test file for HMAC with null values.
* Update HMAC validation logic to handle NULL values in SQL queries
- Modified conditions in SQL queries to check for NULL values alongside signature mismatches.
- Enhanced documentation on NULL handling for HMAC checks to improve clarity and portability.
* remove debug logging from ci
---------
Co-authored-by: Cursor Agent <[email protected]>
'Creates a unique "signature" for some data using a secret key.
15
+
This signature proves that the data hasn''t been tampered with and comes from someone who knows the secret.
16
+
17
+
### What is HMAC used for?
18
+
19
+
[**HMAC**](https://en.wikipedia.org/wiki/HMAC) (Hash-based Message Authentication Code) is commonly used to:
20
+
- **Verify webhooks**: Use HMAC to ensure only a given external service can call a given endpoint in your application.
21
+
The service signs their request with a secret key, and you verify the signature before processing the data they sent you.
22
+
Used for instance by [Stripe](https://docs.stripe.com/webhooks?verify=verify-manually), and [Shopify](https://shopify.dev/docs/apps/build/webhooks/subscribe/https#step-2-validate-the-origin-of-your-webhook-to-ensure-its-coming-from-shopify).
23
+
- **Secure API requests**: Prove that an API request comes from an authorized source
24
+
- **Generate secure tokens**: Create temporary access codes for downloads or password resets
25
+
- **Protect data**: Ensure data hasn''t been modified during transmission
26
+
27
+
### How to use it
28
+
29
+
The `sqlpage.hmac` function takes three inputs:
30
+
1. **Your data** - The text you want to sign (like a message or request body)
31
+
2. **Your secret key** - A password only you know (keep this safe!)
32
+
3. **Algorithm** (optional) - The hash algorithm and output format:
33
+
- `sha256` (default) - SHA-256 with hexadecimal output
34
+
- `sha256-base64` - SHA-256 with base64 output
35
+
- `sha512` - SHA-512 with hexadecimal output
36
+
- `sha512-base64` - SHA-512 with base64 output
37
+
38
+
It returns a signature string. If someone changes even one letter in your data, the signature will be completely different.
39
+
40
+
### Example: Verify a Webhooks signature
41
+
42
+
When Shopify sends you a webhook (like when someone places an order), it includes a signature. Here''s how to verify it''s really from Shopify.
43
+
This supposes you store the secret key in an [environment variable](https://en.wikipedia.org/wiki/Environment_variable) named `WEBHOOK_SECRET`.
44
+
45
+
```sql
46
+
SET body = sqlpage.request_body();
47
+
SET secret = sqlpage.environment_variable(''WEBHOOK_SECRET'');
48
+
SET expected_signature = sqlpage.hmac($body, $secret, ''sha256'');
49
+
SET actual_signature = sqlpage.header(''X-Webhook-Signature'');
50
+
51
+
-- redirect to an error page and stop execution if the signature does not match
52
+
SELECT
53
+
''redirect'' as component,
54
+
''/error.sql?err=bad_webhook_signature'' as link
55
+
WHERE $actual_signature != $expected_signature OR $actual_signature IS NULL;
56
+
57
+
-- If we reach here, the signature is valid - process the order
58
+
INSERT INTO orders (order_data) VALUES ($body);
59
+
60
+
SELECT ''json'' as component, ''jsonlines'' as type;
61
+
SELECT ''success'' as status;
62
+
```
63
+
64
+
### Example: Time-limited links
65
+
66
+
You can create links that will be valid only for a limited time by including a signature in them.
67
+
Let''s say we have a `download.sql` page we want to link to,
68
+
but we don''t want it to be accessible to anyone who can find the link.
69
+
Sign `file_id|expires_at` with a secret. Accept only if not expired and the signature matches.
SELECT ''redirect'' AS component, ''/error.sql?err=expired'' AS link
92
+
WHERE $expected != $token OR $token IS NULL OR $expires_at < datetime(''now'');
93
+
94
+
-- serve the file
95
+
```
96
+
97
+
### Important Security Notes
98
+
99
+
- **Keep your secret key safe**: If your secret leaks, anyone can forge signatures and access protected pages
100
+
- **The signature is case-sensitive**: Even a single wrong letter means the signature won''t match
101
+
- **NULL handling**: Always use `IS DISTINCT FROM`, not `=` to check for hmac matches.
102
+
- `SELECT ''redirect'' as component WHERE sqlpage.hmac(...) != $signature` will not redirect if `$signature` is NULL (the signature is absent).
103
+
- `SELECT ''redirect'' as component WHERE sqlpage.hmac(...) IS DISTINCT FROM $signature` checks for both NULL and non-NULL values (but is not available in all SQL dialects).
104
+
- `SELECT ''redirect'' as component WHERE sqlpage.hmac(...) != $signature OR $signature IS NULL` is the most portable solution.
105
+
'
106
+
);
107
+
108
+
INSERT INTO
109
+
sqlpage_function_parameters (
110
+
"function",
111
+
"index",
112
+
"name",
113
+
"description_md",
114
+
"type"
115
+
)
116
+
VALUES
117
+
(
118
+
'hmac',
119
+
1,
120
+
'data',
121
+
'The input data to compute the HMAC for. Can be any text string. Cannot be NULL.',
122
+
'TEXT'
123
+
),
124
+
(
125
+
'hmac',
126
+
2,
127
+
'key',
128
+
'The secret key used to compute the HMAC. Should be kept confidential. Cannot be NULL.',
129
+
'TEXT'
130
+
),
131
+
(
132
+
'hmac',
133
+
3,
134
+
'algorithm',
135
+
'The hash algorithm and output format. Optional, defaults to `sha256` (hex output). Supported values: `sha256`, `sha256-base64`, `sha512`, `sha512-base64`. Defaults to `sha256`.',
0 commit comments