diff options
Diffstat (limited to 'README.md')
-rw-r--r-- | README.md | 27 |
1 files changed, 15 insertions, 12 deletions
@@ -3,7 +3,7 @@ A microframework based on [Hyper](https://hyper.rs/) providing traits to: * extend `http::request::Parts` with query parameter deserialization & cookie parsing -* extend `hyper::Body` with form deserialization (and optional [CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection) +* extend `hyper::Body` with form deserialization (and JSON deserialization with the `json` feature) * extend `http::response::Builder` with methods to set & delete cookies and set the Content-Type Furthermore Sputnik provides what's necessary to implement [signed & expiring @@ -20,6 +20,12 @@ conversions for every error type, which you want to short-circuit with the `?` operator. This can be easily done with [thiserror](https://crates.io/crates/thiserror) because Sputnik restricts its error types to the `'static` lifetime. +## Security Considerations + +Protect your application against [CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) +by setting `SameSite` to `Lax` or `Strict` for your cookies and checking that the `Origin` +header matches your domain name (especially if you have unauthenticated POST endpoints). + ## Example ```rust @@ -29,8 +35,8 @@ use hyper::{Method, Server, StatusCode, Body}; use hyper::http::request::Parts; use hyper::http::response::Builder; use serde::Deserialize; -use sputnik::{mime, request::{SputnikParts, SputnikBody, CsrfToken}, response::SputnikBuilder}; -use sputnik::request::CsrfProtectedFormError; +use sputnik::{mime, request::{SputnikParts, SputnikBody}, response::SputnikBuilder}; +use sputnik::request::FormError; type Response = hyper::Response<Body>; @@ -39,13 +45,13 @@ enum Error { #[error("page not found")] NotFound(String), #[error("{0}")] - CsrfError(#[from] CsrfProtectedFormError) + FormError(#[from] FormError) } fn render_error(err: Error) -> (StatusCode, String) { match err { Error::NotFound(msg) => (StatusCode::NOT_FOUND, msg), - Error::CsrfError(err) => (StatusCode::BAD_REQUEST, err.to_string()), + Error::FormError(err) => (StatusCode::BAD_REQUEST, err.to_string()), } } @@ -57,22 +63,19 @@ async fn route(req: &mut Parts, body: Body) -> Result<Response, Error> { } } -fn get_form(req: &mut Parts) -> Response { +fn get_form(_req: &mut Parts) -> Response { Builder::new() .content_type(mime::TEXT_HTML) .body( - format!( - "<form method=post><input name=text>{}<button>Submit</button></form>", - CsrfToken::from_request(req).html_input() - ).into() + "<form method=post><input name=text> <button>Submit</button></form>".into() ).unwrap() } #[derive(Deserialize)] struct FormData {text: String} -async fn post_form(req: &mut Parts, body: Body) -> Result<Response, Error> { - let msg: FormData = body.into_form_csrf(req).await?; +async fn post_form(_req: &mut Parts, body: Body) -> Result<Response, Error> { + let msg: FormData = body.into_form().await?; Ok(Builder::new().body( format!("hello {}", msg.text).into() ).unwrap()) |