-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathjs_json.rs
108 lines (97 loc) · 3.35 KB
/
js_json.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! This example shows a bare bones example of adding CSRF protection to a JSON
//! endpoint by adding a CSRF field to the JSON request. Remember that these
//! examples are isolated, may not consider all security aspects, such as the
//! strengths and weaknesses of the double-submit technique used.
use actix_csrf::extractor::{Csrf, CsrfGuarded, CsrfToken};
use actix_csrf::CsrfMiddleware;
use actix_web::http::Method;
use actix_web::web::Json;
use actix_web::HttpResponse;
use actix_web::{get, post, App, HttpServer, Responder};
use rand::prelude::StdRng;
use serde::{Deserialize, Serialize};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
let csrf =
// Use the default CSRF token settings. Among other protections,
// this means that the CSRF token is inaccessible to Javascript.
CsrfMiddleware::<StdRng>::new()
// We need to disable HttpOnly, or else we can't access the cookie
// from Javascript.
.http_only(false)
// Our login form is at `/login`, and we want the middleware to set
// the csrf token when they reach the page. This also lets us access
// the newly set token with the `CrsfToken` extractor.
.set_cookie(Method::GET, "/login");
App::new().wrap(csrf).service(login_ui).service(login)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
/// Returns a simple login form with a CSRF token.
#[get("/login")]
async fn login_ui() -> impl Responder {
let body = r#"
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Example</title></head>
<body>
<script>
function submit() {
// Get the CSRF value from our cookie.
const csrfValue = document.cookie.split("=")[1];
let request = new Request("/login", {
method: "POST",
// Actix strictly requires the content type to be set.
headers: {
"Content-Type": "application/json",
},
// Set the CSRF token in the request body.
body: JSON.stringify({
csrf: csrfValue,
count: 0,
})
});
fetch(request)
.then(resp => resp.json())
.then(resp => console.log(resp.count));
}
</script>
<button onclick="submit()">Click me!</button>
</body>
</html>
"#;
HttpResponse::Ok().body(body)
}
#[derive(Deserialize)]
struct Request {
csrf: CsrfToken,
count: usize,
}
impl CsrfGuarded for Request {
fn csrf_token(&self) -> &CsrfToken {
&self.csrf
}
}
#[derive(Serialize)]
struct Response {
count: usize,
}
/// Validates a json endpoint that has a CSRF token.
#[post("/login")]
async fn login(
// `Csrf` will validate the field with the CSRF token. Since Csrf implements
// Deref and DerefMut, so you can directly access the actual form data as
// normal.
json: Csrf<Json<Request>>,
) -> impl Responder {
// At this point, we have a valid CSRF token, so we can treat the request
// as legitimate.
// NOTE: Remember that CSRF protections are only effective if there isn't
// an XSS vector.
HttpResponse::Ok().json(Response {
count: json.count + 1,
})
}