| Crates.io | axum-csrf-simple |
| lib.rs | axum-csrf-simple |
| version | 0.1.3 |
| created_at | 2025-03-02 08:23:51.83048+00 |
| updated_at | 2025-03-02 10:48:43.3849+00 |
| description | Simple CSRF protection for axum apps |
| homepage | |
| repository | https://github.com/wushilin/axum-csrf.git |
| max_upload_size | |
| id | 1574390 |
| size | 29,136 |
Simple, automatic CSRF handling for Axum
All requests that has no CSRF cookie will get set-cookie header automatically.
All state changing requests (PUT,DELETE,POST,PATCH) will request x-csrf-token with a valid header.
The x-csrf-token is compared with the request cookie csrf_token. If not matched, request will be rejected with 401.
The rejection body will tell the reason.
No need to do any other change
The code does not inspect POST param for CSRF retrieval. This is generally very expensive and less convenient. If you need to use post
you can append form action with query string like ?csrf_token=xxxx to submit.
If both x-csrf-token header and ?csrf_token=xxxx query string are specified, the x-csrf-token will be used.
GET /api/get_csrf_token to retrieve the token programmatically, and use it in header or query_string when submitting.Add the dependency:
$ cargo add axum-csrf-simple
/// import as alias (optional)
use axum_csrf_simple as csrf;
/// Set your preferred token signing key.
/// All token is <uuid>-<hmac_signature> format. Invalid tokens won't be trusted.
// if not set, the default signing key is 32 char auto generated signed key
// note token signed by different sign key will be invalid.
// if you did not specify the signing key, it could be an issue if you have multiple instance of apps
// as the token won't be compatible with other server's token
csrf::set_csrf_token_sign_key("my-signing-key").await;
// set to true to only transfer CSRF cookie over https (for dev, set to false or it won't work - false is the default)
csrf::set_csrf_secure_cookie_enable(true);
let app = Router::new()
.route("/admin/endpoint1", get(handle1).post(handle1).put(handle1)) // this url, when POST/PUT, is CSRF protected
.route("/api/get_csrf_token", get(csrf::get_csrf_token)) // expose the API for your client to retrieve CSRF token. One time only, and it can be cached.
.route_layer(middleware::from_fn(csrf::csrf_protect));
/// Test accessing /admin/endpoint1 with post, it will require CSRF validation.
Note the /api/get_csrf_token API uri can be customized based on your need.
Client has to submit request with x-csrf-token header value to be able to submit successfully.
Example using plain javascript.
It is pretty easy to write for react, angular, nodejs.
The result payload of the /api/get_csrf_token is like
{
"token":"xzP14JrA9qHFaLwKHpFYYj-a209dda82c379b55542b4ed2f977e5464be79b8b7f2009ecb08d36b92599b13f",
"is_new":false
}
token: The token
is_new: Tells if the server did create a new token for client
The token can't be used across servers with different signature key.
When requesting the token, server automatically set a cookie csrf_token.
<script>
var CSRF_TOKEN = "";
(function() {
fetch("/api/get_csrf_token")
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json(); // Convert response to JSON
})
.then(data => {
console.log("CSRF Token:", data.token);
CSRF_TOKEN = data.token; /// now we can reuse the token
})
.catch(error => console.error("Error fetching CSRF token:", error));
})();
/// CSRF_TOKEN can be used
</script>
async function submitMyForm(event) {
event.preventDefault();
console.log("Submit called");
const form = event.target;
const formData = new FormData(form); // Collect form data
try {
const response = await fetch(form.action, {
method: "POST",
headers: {
"x-csrf-token": CSRF_TOKEN // set the token
},
body: formData // Send form data
});
const result = await response.json(); // Assuming server returns JSON
} catch (error) {
console.error("Error:", error);
}
}
Note that this is very suitable for AJAX based submission. For normal submission, you will need to append a query string csrf_token=xxx to the POST URL.
This is not preferred as it is less secure in general, but you can use if you have no choice.
<script>
async function submitMyForm(event) {
event.target.action = event.target.action + "?csrf_token=" + CSRF_TOKEN + "&other=aaa"; /// Append the CSRF token
return;
</script>
Not required. All requests that reaches your code is CSRF validated and can be assumed to be safe.