Skip to content

Commit 219d09b

Browse files
authored
Merge pull request #1 from wiseaidev/fix-bugs
fix bugs, bump crates versions, fix base64 warnings
2 parents 58cebba + 55b39ba commit 219d09b

File tree

7 files changed

+122
-126
lines changed

7 files changed

+122
-126
lines changed

Cargo.toml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
[package]
2-
name = "rocket_csrf"
3-
version = "0.3.0"
4-
authors = ["Alex Kotov <kotovalexarian@gmail.com>"]
5-
edition = "2018"
2+
name = "rocket_csrf_token"
3+
version = "0.3.1"
4+
authors = ["Alex Kotov <kotovalexarian@gmail.com>", "Mahmoud Harmouch <oss@wiseai.dev>"]
5+
edition = "2021"
66
description = "CSRF (Cross-Site Request Forgery) protection for Rocket web framework"
77
readme = true
8-
homepage = "https://github.com/kotovalexarian/rocket_csrf"
9-
repository = "https://github.com/kotovalexarian/rocket_csrf.git"
8+
homepage = "https://github.com/wiseaidev/rocket_csrf_token "
9+
repository = "https://github.com/wiseaidev/rocket_csrf_token .git"
1010
license = "MIT"
1111
keywords = ["csrf", "http", "rocket", "security", "web"]
1212
categories = ["web-programming"]
1313
publish = true
1414

1515
[dependencies]
16-
base64 = { version = "0.13.0" }
17-
bcrypt = { version = "0.9" }
18-
rand = { version = "0.8.3" }
19-
rocket = { version = "0.5.0-rc.2", features = ["secrets"] }
16+
base64 = "0.21.5"
17+
bcrypt = "0.15.0"
18+
rand = "0.8.5"
19+
rocket = { version = "=0.5.0-rc.3", features = ["secrets"] }

README.md

Lines changed: 56 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,125 @@
1-
rocket_csrf
2-
===========
1+
# rocket_csrf_token
32

4-
CSRF (Cross-Site Request Forgery) protection for [Rocket](https://rocket.rs)
5-
web framework.
3+
A slightly more maintained version of [rocket_csrf](https://github.com/kotovalexarian/rocket_csrf).
64

7-
> **WARNING!**
8-
> The implementation is very simple for now and may not be ready for production.
5+
## Usage
96

10-
Discussion about CSRF protection in Rocket is
11-
[here](https://github.com/SergioBenitez/Rocket/issues/14).
12-
13-
14-
15-
Table of contents
16-
-----------------
17-
18-
* [Overview](#rocket_csrf)
19-
* [Table of contents](#table-of-contents)
20-
* [Usage](#usage)
21-
* [TODO](#todo)
22-
23-
24-
25-
Usage
26-
-----
27-
28-
Attach [fairing](https://rocket.rs/v0.5-rc/guide/fairings/#fairings) to the Rocket
29-
instance:
7+
Attach [fairing](https://rocket.rs/v0.5-rc/guide/fairings/#fairings) to the Rocket instance:
308

319
```rust
3210
#![feature(decl_macro)]
3311

34-
#[macro_use] extern crate rocket;
35-
#[macro_use] extern crate serde_derive;
12+
#[macro_use]
13+
extern crate rocket;
14+
#[macro_use]
15+
extern crate serde_derive;
3616

3717
use rocket_dyn_templates::Template;
3818

3919
#[launch]
4020
fn rocket() -> _ {
41-
rocket::ignite()
42-
.attach(rocket_csrf::Fairing::default())
43-
.attach(Template::fairing())
44-
.mount("/", routes![new, create])
21+
rocket::build()
22+
.attach(rocket_csrf_token::Fairing::default())
23+
.attach(Template::fairing())
24+
.mount("/", routes![new, create])
4525
}
4626
```
4727

48-
You also can configure
49-
[fairing](https://rocket.rs/v0.5-rc/guide/fairings/#fairings):
28+
You also can configure [fairing](https://rocket.rs/v0.5-rc/guide/fairings/#fairings):
5029

5130
```rust
5231
#[launch]
5332
fn rocket() -> _ {
54-
rocket::ignite()
55-
.attach(rocket_csrf::Fairing::new(
56-
rocket_csrf::CsrfConfig::default()
57-
.with_cookie_name("foobar")
58-
.with_cookie_len(64)
59-
.with_lifetime(time::Duration::days(3)),
60-
))
61-
.attach(Template::fairing())
62-
.mount("/", routes![new, create])
33+
rocket::build()
34+
.attach(
35+
rocket_csrf_token::Fairing::new(
36+
rocket_csrf_token::CsrfConfig
37+
::default()
38+
.with_cookie_name("foobar")
39+
.with_cookie_len(64)
40+
.with_lifetime(rocket::time::Duration::days(3))
41+
)
42+
)
43+
.attach(Template::fairing())
44+
.mount("/", routes![new, create])
6345
}
6446
```
6547

66-
Add [guard](https://rocket.rs/v0.5-rc/guide/requests/#request-guards) to any
67-
request where you want to have access to session's CSRF token (e.g. to include
68-
it in forms) or verify it (e.g. to validate form):
48+
Add [guard](https://rocket.rs/v0.5-rc/guide/requests/#request-guards) to any request where you want to have access to session's CSRF token (e.g. to include it in forms) or verify it (e.g. to validate form):
6949

7050
```rust
7151
use rocket::form::Form;
7252
use rocket::response::Redirect;
73-
use rocket_csrf::CsrfToken;
53+
use rocket_csrf_token::CsrfToken;
7454
use rocket_dyn_templates::Template;
7555

7656
#[get("/comments/new")]
7757
fn new(csrf_token: CsrfToken) -> Template {
78-
// your code
58+
// your code
7959
}
8060

8161
#[post("/comments", data = "<form>")]
8262
fn create(csrf_token: CsrfToken, form: Form<Comment>) -> Redirect {
83-
// your code
63+
// your code
8464
}
8565
```
8666

87-
Get CSRF token from
88-
[guard](https://rocket.rs/v0.5-rc/guide/requests/#request-guards)
89-
to use it in [templates](https://rocket.rs/v0.5-rc/guide/responses/#templates):
67+
Get CSRF token from [guard](https://rocket.rs/v0.5-rc/guide/requests/#request-guards) to use it in [templates](https://rocket.rs/v0.5-rc/guide/responses/#templates):
9068

9169
```rust
9270
#[get("/comments/new")]
9371
fn new(csrf_token: CsrfToken) -> Template {
94-
let authenticity_token: &str = csrf_token.authenticity_token();
72+
let authenticity_token: &str = csrf_token.authenticity_token();
9573

96-
// your code
74+
// your code
9775
}
9876
```
9977

100-
Add CSRF token to your HTML forms in
101-
[templates](https://rocket.rs/v0.5-rc/guide/responses/#templates):
78+
Add CSRF token to your HTML forms in [templates](https://rocket.rs/v0.5-rc/guide/responses/#templates):
10279

10380
```html
10481
<form method="post" action="/comments">
105-
<input type="hidden" name="authenticity_token" value="{{ authenticity_token }}"/>
106-
<!-- your fields -->
82+
<input
83+
type="hidden"
84+
name="authenticity_token"
85+
value="{{ authenticity_token }}"
86+
/>
87+
<!-- your fields -->
10788
</form>
10889
```
10990

110-
Add attribute `authenticity_token` to your
111-
[forms](https://rocket.rs/v0.5-rc/guide/requests/#forms):
91+
Add attribute `authenticity_token` to your [forms](https://rocket.rs/v0.5-rc/guide/requests/#forms):
11292

11393
```rust
11494
#[derive(FromForm)]
11595
struct Comment {
116-
authenticity_token: String,
117-
// your attributes
96+
authenticity_token: String,
97+
// your attributes
11898
}
11999
```
120100

121-
Validate [forms](https://rocket.rs/v0.5-rc/guide/requests/#forms) to have valid
122-
authenticity token:
101+
Validate [forms](https://rocket.rs/v0.5-rc/guide/requests/#forms) to have valid authenticity token:
123102

124103
```rust
125104
#[post("/comments", data = "<form>")]
126105
fn create(csrf_token: CsrfToken, form: Form<Comment>) -> Redirect {
127-
if let Err(_) = csrf_token.verify(&form.authenticity_token) {
128-
return Redirect::to(uri!(new));
129-
}
106+
if let Err(_) = csrf_token.verify(&form.authenticity_token) {
107+
return Redirect::to(uri!(new));
108+
}
130109

131-
// your code
110+
// your code
132111
}
133112
```
134113

135114
See the complete code in [minimal example](examples/minimal).
136115

116+
## TODO
137117

138-
139-
TODO
140-
----
141-
142-
* [ ] Add fairing to verify all requests as an option.
143-
* [ ] Add [data guard](https://api.rocket.rs/v0.5-rc/rocket/data/trait.FromData.html) to verify forms with a guard.
144-
* [ ] Add helpers to render form field.
145-
* [ ] Add helpers to add HTML meta tags for Ajax with `X-CSRF-Token` header.
146-
* [ ] Verify `X-CSRF-Token` header.
147-
* [ ] Use authenticity token encryption from [Ruby on Rails](https://github.com/rails/rails/blob/v6.0.3.4/actionpack/lib/action_controller/metal/request_forgery_protection.rb).
148-
* [ ] Allow to configure CSRF protection (CSRF token byte length, cookie name, etc.).
149-
* [ ] Set cookie to expire with session.
118+
- [ ] Add fairing to verify all requests as an option.
119+
- [ ] Add [data guard](https://api.rocket.rs/v0.5-rc/rocket/data/trait.FromData.html) to verify forms with a guard.
120+
- [ ] Add helpers to render form field.
121+
- [ ] Add helpers to add HTML meta tags for Ajax with `X-CSRF-Token` header.
122+
- [ ] Verify `X-CSRF-Token` header.
123+
- [ ] Use authenticity token encryption from [Ruby on Rails](https://github.com/rails/rails/blob/v6.0.3.4/actionpack/lib/action_controller/metal/request_forgery_protection.rb).
124+
- [ ] Allow to configure CSRF protection (CSRF token byte length, cookie name, etc.).
125+
- [ ] Set cookie to expire with session.

src/lib.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use base64::{engine::general_purpose, Engine as _};
12
use bcrypt::{hash, verify};
23
use rand::{distributions::Standard, Rng};
34
use rocket::{
@@ -42,9 +43,9 @@ impl Default for Fairing {
4243
}
4344

4445
impl Default for CsrfConfig {
46+
/// Set to 6hour for default in Database Session stores.
4547
fn default() -> Self {
4648
Self {
47-
/// Set to 6hour for default in Database Session stores.
4849
lifespan: Duration::days(1),
4950
cookie_name: "csrf_token".into(),
5051
cookie_len: 32,
@@ -120,7 +121,7 @@ impl RocketFairing for Fairing {
120121
.take(config.cookie_len)
121122
.collect();
122123

123-
let encoded = base64::encode(&values[..]);
124+
let encoded = general_purpose::STANDARD.encode(&values[..]);
124125

125126
let expires = OffsetDateTime::now_utc() + config.lifespan;
126127

@@ -141,7 +142,18 @@ impl<'r> FromRequest<'r> for CsrfToken {
141142

142143
match request.valid_csrf_token_from_session(&config) {
143144
None => Outcome::Failure((Status::Forbidden, ())),
144-
Some(token) => Outcome::Success(Self(base64::encode(token))),
145+
Some(token) => Outcome::Success(Self(general_purpose::STANDARD.encode(token))),
146+
}
147+
}
148+
}
149+
150+
#[async_trait]
151+
impl RocketFairing for CsrfToken {
152+
// This is a request fairing named "GET CsrfToken".
153+
fn info(&self) -> Info {
154+
Info {
155+
name: "GET/POST Counter",
156+
kind: Kind::Request,
145157
}
146158
}
147159
}
@@ -164,6 +176,6 @@ impl RequestCsrf for Request<'_> {
164176
fn csrf_token_from_session(&self, config: &CsrfConfig) -> Option<Vec<u8>> {
165177
self.cookies()
166178
.get_private(&config.cookie_name)
167-
.and_then(|cookie| base64::decode(cookie.value()).ok())
179+
.and_then(|cookie| general_purpose::STANDARD.decode(cookie.value()).ok())
168180
}
169181
}

tests/fairing_configured.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#[macro_use]
22
extern crate rocket;
33

4+
use base64::{engine::general_purpose, Engine as _};
45
const COOKIE_NAME: &str = "foobar";
56
const COOKIE_LEN: usize = 64;
67

@@ -10,8 +11,8 @@ fn client() -> rocket::local::blocking::Client {
1011

1112
fn rocket() -> rocket::Rocket<rocket::Build> {
1213
rocket::build()
13-
.attach(rocket_csrf::Fairing::new(
14-
rocket_csrf::CsrfConfig::default()
14+
.attach(rocket_csrf_token::Fairing::new(
15+
rocket_csrf_token::CsrfConfig::default()
1516
.with_cookie_name(COOKIE_NAME)
1617
.with_cookie_len(COOKIE_LEN)
1718
.with_lifetime(rocket::time::Duration::days(3)),
@@ -24,15 +25,16 @@ fn index() {}
2425

2526
#[test]
2627
fn add_csrf_token_to_cookies() {
27-
base64::decode(
28-
client()
29-
.get("/")
30-
.dispatch()
31-
.cookies()
32-
.iter()
33-
.find(|cookie| cookie.name() == COOKIE_NAME)
34-
.unwrap()
35-
.value(),
36-
)
37-
.unwrap();
28+
general_purpose::STANDARD
29+
.decode(
30+
client()
31+
.get("/")
32+
.dispatch()
33+
.cookies()
34+
.iter()
35+
.find(|cookie| cookie.name() == COOKIE_NAME)
36+
.unwrap()
37+
.value(),
38+
)
39+
.unwrap();
3840
}

tests/fairing_default.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
#[macro_use]
22
extern crate rocket;
33

4+
use base64::{engine::general_purpose, Engine as _};
45
fn client() -> rocket::local::blocking::Client {
56
rocket::local::blocking::Client::tracked(rocket()).unwrap()
67
}
78

89
fn rocket() -> rocket::Rocket<rocket::Build> {
910
rocket::build()
10-
.attach(rocket_csrf::Fairing::default())
11+
.attach(rocket_csrf_token::Fairing::default())
1112
.mount("/", routes![index])
1213
}
1314

@@ -16,15 +17,16 @@ fn index() {}
1617

1718
#[test]
1819
fn add_csrf_token_to_cookies() {
19-
base64::decode(
20-
client()
21-
.get("/")
22-
.dispatch()
23-
.cookies()
24-
.iter()
25-
.find(|cookie| cookie.name() == "csrf_token")
26-
.unwrap()
27-
.value(),
28-
)
29-
.unwrap();
20+
general_purpose::STANDARD
21+
.decode(
22+
client()
23+
.get("/")
24+
.dispatch()
25+
.cookies()
26+
.iter()
27+
.find(|cookie| cookie.name() == "csrf_token")
28+
.unwrap()
29+
.value(),
30+
)
31+
.unwrap();
3032
}

0 commit comments

Comments
 (0)