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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
pub use cookie::{Key, SameSite};
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use std::borrow::Cow;
use time::Duration;
///This is the CSRF Config it is used to manage how we set the Restricted Cookie.
#[derive(Clone)]
pub struct CsrfConfig {
/// CSRF Cookie lifespan
pub(crate) lifespan: Duration,
/// CSRF cookie name
pub(crate) cookie_name: String,
/// CSRF Token character length
pub(crate) cookie_len: usize,
/// Session cookie domain
pub(crate) cookie_domain: Option<Cow<'static, str>>,
/// Session cookie http only flag
pub(crate) cookie_http_only: bool,
/// Session cookie http only flag
pub(crate) cookie_path: Cow<'static, str>,
/// Resticts how Cookies are sent cross-site. Default is `SameSite::None`
/// Only works if domain is also set.
pub(crate) cookie_same_site: SameSite,
/// Session cookie secure flag
pub(crate) cookie_secure: bool,
///Encyption Key used to encypt cookies for confidentiality, integrity, and authenticity.
pub(crate) key: Option<Key>,
///Hashing Salt.
pub(crate) salt: Cow<'static, str>,
/// This is used to append __Host- to the front of all Cookie names to prevent sub domain usage.
/// It is disabled by default.
pub(crate) prefix_with_host: bool,
}
impl std::fmt::Debug for CsrfConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CsrfConfig")
.field("lifespan", &self.lifespan)
.field("cookie_name", &self.cookie_name)
.field("cookie_len", &self.cookie_len)
.field("cookie_domain", &self.cookie_domain)
.field("cookie_http_only", &self.cookie_http_only)
.field("cookie_path", &self.cookie_path)
.field("cookie_same_site", &self.cookie_same_site)
.field("cookie_secure", &self.cookie_secure)
.field("key", &"key hidden")
.field("salt", &"salt hidden")
.field("prefix_with_host", &self.prefix_with_host)
.finish()
}
}
impl CsrfConfig {
/// Creates [`Default`] configuration of [`CsrfConfig`].
/// This is equivalent to the [`CsrfConfig::default()`].
#[must_use]
pub fn new() -> Self {
Default::default()
}
/// Set's the csrf's cookie's domain name.
///
/// # Examples
/// ```rust
/// use axum_csrf::CsrfConfig;
///
/// let config = CsrfConfig::default().with_cookie_domain(Some("www.helpme.com"));
/// ```
///
#[must_use]
pub fn with_cookie_domain<T>(mut self, name: Option<T>) -> Self
where
T: Into<Cow<'static, str>>,
{
self.cookie_domain = name.map(|v| v.into());
self
}
/// Set's the csrf's lifetime (expiration time).
///
/// # Examples
/// ```rust
/// use axum_csrf::CsrfConfig;
/// use chrono::Duration;
///
/// let config = CsrfConfig::default().with_lifetime(Duration::days(32));
/// ```
///
#[must_use]
pub fn with_lifetime(mut self, time: Duration) -> Self {
self.lifespan = time;
self
}
/// Set's the csrf's cookie's name.
///
/// # Examples
/// ```rust
/// use axum_csrf::CsrfConfig;
///
/// let config = CsrfConfig::default().with_cookie_name("my_cookie");
/// ```
///
#[must_use]
pub fn with_cookie_name(mut self, name: &str) -> Self {
self.cookie_name = name.into();
self
}
/// Set's the csrf's cookie's path.
///
/// This is used to deturmine when the cookie takes effect within the website path.
/// Leave as default ("/") for cookie to be used site wide.
///
/// # Examples
/// ```rust
/// use axum_csrf::CsrfConfig;
///
/// let config = CsrfConfig::default().with_cookie_path("/");
/// ```
///
#[must_use]
pub fn with_cookie_path(mut self, path: impl Into<Cow<'static, str>>) -> Self {
self.cookie_path = path.into();
self
}
/// Set's the csrf's cookie's Same Site Setting for Cross-Site restrictions.
///
/// Only works if Domain is also set to restrict it to that domain only.
///
/// # Examples
/// ```rust
/// use axum_csrf::CsrfConfig;
/// use cookie::SameSite;
///
/// let config = CsrfConfig::default().with_cookie_same_site(SameSite::Strict);
/// ```
///
#[must_use]
pub fn with_cookie_same_site(mut self, same_site: SameSite) -> Self {
self.cookie_same_site = same_site;
self
}
/// Set's the csrf's cookie's to http only.
///
/// # Examples
/// ```rust
/// use axum_csrf::CsrfConfig;
///
/// let config = CsrfConfig::default().with_http_only(false);
/// ```
///
#[must_use]
pub fn with_http_only(mut self, is_set: bool) -> Self {
self.cookie_http_only = is_set;
self
}
/// Set's the csrf's secure flag for if it gets sent over https.
///
/// # Examples
/// ```rust
/// use axum_csrf::CsrfConfig;
///
/// let config = CsrfConfig::default().with_secure(true);
/// ```
///
#[must_use]
pub fn with_secure(mut self, is_set: bool) -> Self {
self.cookie_secure = is_set;
self
}
/// Set's the csrf's token length.
///
/// # Examples
/// ```rust
/// use axum_csrf::CsrfConfig;
///
/// let config = CsrfConfig::default().with_cookie_len(16);
/// ```
///
#[must_use]
pub fn with_cookie_len(mut self, length: usize) -> Self {
self.cookie_len = length;
self
}
/// Set's the csrf's cookie encyption key enabling private cookies.
///
/// When Set it will enforce Private cookies across all Sessions.
/// If you use Key::generate() it will make a new key each server reboot.
/// To prevent this make and save a key to a config file for long term usage.
/// For Extra Security Regenerate the key every so many months to a year.
///
/// # Examples
/// ```rust
/// use axum_csrf::{Key, CsrfConfig};
///
/// let config = CsrfConfig::default().with_key(Key::generate());
/// ```
///
#[must_use]
pub fn with_key(mut self, key: Option<Key>) -> Self {
self.key = key;
self
}
/// Set's the csrf's cookie's salt.
///
/// This is used to hash the CSRF key for the html insertion.
///
/// # Examples
/// ```rust
/// use axum_csrf::CsrfConfig;
///
/// let config = CsrfConfig::default().with_salt("somesalthere");
/// ```
///
#[must_use]
pub fn with_salt(mut self, salt: impl Into<Cow<'static, str>>) -> Self {
self.salt = salt.into();
self
}
/// Set's the CSRF's prefix_with_host to either true: __Host- gets prefixed to the cookie names false: __Host- does not get prepended.
///
/// __Host- prefix: Cookies with names starting with __Host- must be set with the secure flag, must be from a secure page (HTTPS),
/// must not have a domain specified (and therefore, are not sent to subdomains), and the path must be /.
///
/// # Examples
/// ```rust
/// use axum_csrf::CsrfConfig;
///
/// let config = CsrfConfig::default().with_prefix_with_host(true);
/// ```
///
#[must_use]
pub fn with_prefix_with_host(mut self, enable: bool) -> Self {
self.prefix_with_host = enable;
self
}
}
impl Default for CsrfConfig {
fn default() -> Self {
Self {
// Set to 6hour for default in Database Session stores.
lifespan: Duration::hours(6),
cookie_name: "Csrf_Token".into(),
cookie_path: "/".into(),
cookie_http_only: true,
cookie_secure: false,
cookie_domain: None,
cookie_same_site: SameSite::Lax,
cookie_len: 32,
//We do this by default since we always want this to be secure.
key: Some(Key::generate()),
salt: thread_rng()
.sample_iter(&Alphanumeric)
.take(32)
.map(char::from)
.collect::<String>()
.into(),
prefix_with_host: false,
}
}
}