| name | salvo-csrf |
| description | Implement CSRF (Cross-Site Request Forgery) protection using cookie or session storage. Use for protecting forms and state-changing endpoints. |
| version | 0.89.3 |
| tags | ["security","csrf","protection"] |
Salvo CSRF Protection
This skill helps implement CSRF protection in Salvo applications.
Setup
[dependencies]
salvo = { version = "0.89.3", features = ["csrf"] }
Basic CSRF with Cookie Store
use salvo::csrf::*;
use salvo::prelude::*;
use serde::Deserialize;
#[derive(Deserialize)]
struct FormData {
csrf_token: String,
message: String,
}
#[handler]
async fn show_form(depot: &mut Depot, res: &mut Response) {
let token = depot.csrf_token().unwrap_or_default();
res.render(Text::Html(format!(r#"
<form method="post">
<input type="hidden" name="csrf_token" value="{token}" />
<input type="text" name="message" />
<button type="submit">Submit</button>
</form>
"#)));
}
#[handler]
async fn handle_form(req: &mut Request, res: &mut Response) {
let data = req.parse_form::<FormData>().await.unwrap();
res.render(format!("Message received: {}", data.message));
}
#[tokio::main]
async fn main() {
let form_finder = FormFinder::new("csrf_token");
let csrf_handler = bcrypt_cookie_csrf(form_finder);
let router = Router::new()
.hoop(csrf_handler)
.get(show_form)
.post(handle_form);
let acceptor = TcpListener::new("0.0.0.0:8080").bind().await;
Server::new(acceptor).serve(router).await;
}
CSRF Methods
Bcrypt (No Key Required)
use salvo::csrf::{bcrypt_cookie_csrf, FormFinder};
let form_finder = FormFinder::new("csrf_token");
let csrf_handler = bcrypt_cookie_csrf(form_finder);
HMAC (32-byte Key)
use salvo::csrf::{hmac_cookie_csrf, FormFinder};
let key = *b"01234567012345670123456701234567";
let form_finder = FormFinder::new("csrf_token");
let csrf_handler = hmac_cookie_csrf(key, form_finder);
AES-GCM (32-byte Key)
use salvo::csrf::{aes_gcm_cookie_csrf, FormFinder};
let key = *b"01234567012345670123456701234567";
let form_finder = FormFinder::new("csrf_token");
let csrf_handler = aes_gcm_cookie_csrf(key, form_finder);
CSRF with Session Store
use salvo::csrf::*;
use salvo::session::{CookieStore as SessionCookieStore, SessionHandler};
use salvo::prelude::*;
#[tokio::main]
async fn main() {
let session_handler = SessionHandler::builder(
SessionCookieStore::new(),
b"secretabsecretabsecretabsecretabsecretabsecretabsecretabsecretab",
)
.build()
.unwrap();
let form_finder = FormFinder::new("csrf_token");
let csrf_handler = bcrypt_session_csrf(form_finder);
let router = Router::new()
.hoop(session_handler)
.hoop(csrf_handler)
.get(show_form)
.post(handle_form);
let acceptor = TcpListener::new("0.0.0.0:8080").bind().await;
Server::new(acceptor).serve(router).await;
}
Token Finders
Form Finder (POST Body)
use salvo::csrf::FormFinder;
let finder = FormFinder::new("csrf_token");
Header Finder
use salvo::csrf::HeaderFinder;
let finder = HeaderFinder::new("X-CSRF-Token");
Query Finder
use salvo::csrf::QueryFinder;
let finder = QueryFinder::new("csrf_token");
Getting CSRF Token
use salvo::csrf::CsrfDepotExt;
#[handler]
async fn show_form(depot: &mut Depot, res: &mut Response) {
let token = depot.csrf_token().unwrap_or_default();
res.render(Text::Html(format!(r#"
<form method="post">
<input type="hidden" name="csrf_token" value="{token}" />
<!-- form fields -->
</form>
"#)));
}
CSRF for AJAX Requests
use salvo::csrf::{HeaderFinder, hmac_cookie_csrf};
let header_finder = HeaderFinder::new("X-CSRF-Token");
let csrf_handler = hmac_cookie_csrf(*b"01234567012345670123456701234567", header_finder);
Best Practices
- Use HMAC or AES-GCM in production (Bcrypt is slow)
- Generate secure keys
- Session store is more secure than cookie-based
- Include token in all forms
- Validate on all state-changing requests
- Combine with SameSite cookies
- Rotate tokens after successful submission
Related Skills
- salvo-cors: CORS for cross-origin requests
- salvo-session: CSRF with session storage