aboutsummaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md27
1 files changed, 15 insertions, 12 deletions
diff --git a/README.md b/README.md
index a3c0a71..9d61453 100644
--- a/README.md
+++ b/README.md
@@ -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())