add native csrf helpers

This commit is contained in:
udo
2026-07-06 09:58:25 +00:00
parent 125a24e2f9
commit b8c2efe976
7 changed files with 129 additions and 1 deletions
+21
View File
@@ -0,0 +1,21 @@
:sig
void csrf_rotate(String session_name = "uce-session", String token_name = "csrf_token")
:params
session_name : session/cookie name used by `csrf_token()`
token_name : form/action token namespace to clear
:content
Starts the named session if needed and clears the stored CSRF token for `token_name`. The next `csrf_token()` call creates a new token. Rotate after sensitive successful mutations when replaying the same submitted form should fail.
:example
String old_token = csrf_token();
csrf_rotate();
String new_token = csrf_token();
print(old_token != new_token ? "rotated\n" : "unchanged\n");
:see
>http
csrf_token
csrf_valid
session_start
+26
View File
@@ -0,0 +1,26 @@
:sig
String csrf_token(String session_name = "uce-session", String token_name = "csrf_token")
:params
session_name : session/cookie name to store the token under
token_name : form/action token namespace inside the session
return value : printable CSRF token for the active session
:content
Starts the named session if needed and returns a stable per-session CSRF token for `token_name`. Use it in hidden fields on forms that mutate server state, then verify the submitted value with `csrf_valid()` before applying the change.
Tokens are generated from cryptographic randomness and stored in `context.session` under an internal key. Use a different `token_name` when independent forms should have independent tokens.
:example
String token = csrf_token("uce-doc-session");
?><form method="post">
<input type="hidden" name="csrf_token" value="<?= token ?>">
<button type="submit">Save</button>
</form><?
:see
>http
csrf_valid
csrf_rotate
session_start
random_bytes
+25
View File
@@ -0,0 +1,25 @@
:sig
bool csrf_valid(String submitted_token, String session_name = "uce-session", String token_name = "csrf_token")
:params
submitted_token : value received from the request, usually `context.post["csrf_token"]`
session_name : session/cookie name used by `csrf_token()`
token_name : form/action token namespace used by `csrf_token()`
return value : `true` when the submitted token matches the session token
:content
Starts the named session if needed and checks `submitted_token` against the stored CSRF token using constant-time comparison. It does not create a token when none exists, so a request without a previously rendered form token fails closed.
Use this before mutating state from POST forms or other browser-submitted requests.
:example
String token = csrf_token("uce-doc-session");
print(csrf_valid(token, "uce-doc-session") ? "valid\n" : "invalid\n");
print(csrf_valid("bad-token", "uce-doc-session") ? "bad valid\n" : "bad invalid\n");
:see
>http
csrf_token
csrf_rotate
crypto_equal
session_start