Skip to content

Commit 1ebab34

Browse files
committed
fix(cookies): add SameSite=Lax to all Set-Cookie headers
1 parent 0ae4d58 commit 1ebab34

3 files changed

Lines changed: 40 additions & 60 deletions

File tree

src/server.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,13 +275,43 @@ impl ResponseExt for Response<Body> {
275275
}
276276

277277
fn remove_cookie(&mut self, name: String) {
278-
let removal_cookie = Cookie::build(name).path("/").http_only(true).secure(true).expires(OffsetDateTime::now_utc());
278+
let removal_cookie = Cookie::build(name)
279+
.path("/")
280+
.http_only(true)
281+
.secure(true)
282+
.same_site(cookie::SameSite::Lax)
283+
.expires(OffsetDateTime::now_utc());
279284
if let Ok(val) = header::HeaderValue::from_str(&removal_cookie.to_string()) {
280285
self.headers_mut().append("Set-Cookie", val);
281286
}
282287
}
283288
}
284289

290+
/// Build a persistent [`Cookie`] with standardised privacy/security attributes.
291+
///
292+
/// Sets `Path=/`, `HttpOnly`, `Secure`, and `SameSite=Lax`.
293+
pub fn build_cookie(name: impl Into<String>, value: impl Into<String>, expires: OffsetDateTime) -> Cookie<'static> {
294+
Cookie::build((name.into(), value.into()))
295+
.path("/")
296+
.http_only(true)
297+
.secure(true)
298+
.same_site(cookie::SameSite::Lax)
299+
.expires(expires)
300+
.build()
301+
}
302+
303+
/// Build a session-scoped [`Cookie`] with standardised privacy/security attributes.
304+
///
305+
/// Sets `Path=/`, `HttpOnly`, `Secure`, and `SameSite=Lax`.
306+
pub fn build_session_cookie(name: impl Into<String>, value: impl Into<String>) -> Cookie<'static> {
307+
Cookie::build((name.into(), value.into()))
308+
.path("/")
309+
.http_only(true)
310+
.secure(true)
311+
.same_site(cookie::SameSite::Lax)
312+
.build()
313+
}
314+
285315
impl Route<'_> {
286316
fn method(&mut self, method: &Method, dest: fn(Request<Body>) -> BoxResponse) -> &mut Self {
287317
self.router.add(&format!("/{}{}", method.as_str(), self.path), dest);

src/settings.rs

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::collections::HashMap;
44

55
// CRATES
6-
use crate::server::ResponseExt;
6+
use crate::server::{build_cookie, ResponseExt};
77
use crate::subreddit::join_until_size_limit;
88
use crate::utils::{deflate_decompress, redirect, template, Preferences};
99
use askama::Template;
@@ -80,14 +80,7 @@ pub async fn set(req: Request<Body>) -> Result<Response<Body>, String> {
8080

8181
for &name in &PREFS {
8282
match form.get(name) {
83-
Some(value) => response.insert_cookie(
84-
Cookie::build((name.to_owned(), value.clone()))
85-
.path("/")
86-
.http_only(true)
87-
.secure(true)
88-
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
89-
.into(),
90-
),
83+
Some(value) => response.insert_cookie(build_cookie(name.to_owned(), value.clone(), OffsetDateTime::now_utc() + Duration::weeks(52))),
9184
None => response.remove_cookie(name.to_string()),
9285
};
9386
}
@@ -123,14 +116,7 @@ fn set_cookies_method(req: Request<Body>, remove_cookies: bool) -> Response<Body
123116

124117
for name in PREFS {
125118
match form.get(name) {
126-
Some(value) => response.insert_cookie(
127-
Cookie::build((name.to_owned(), value.clone()))
128-
.path("/")
129-
.http_only(true)
130-
.secure(true)
131-
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
132-
.into(),
133-
),
119+
Some(value) => response.insert_cookie(build_cookie(name.to_owned(), value.clone(), OffsetDateTime::now_utc() + Duration::weeks(52))),
134120
None => {
135121
if remove_cookies {
136122
response.remove_cookie(name.to_string());
@@ -165,14 +151,7 @@ fn set_cookies_method(req: Request<Body>, remove_cookies: bool) -> Response<Body
165151
format!("subscriptions{subscriptions_number}")
166152
};
167153

168-
response.insert_cookie(
169-
Cookie::build((subscriptions_cookie, list))
170-
.path("/")
171-
.http_only(true)
172-
.secure(true)
173-
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
174-
.into(),
175-
);
154+
response.insert_cookie(build_cookie(subscriptions_cookie, list, OffsetDateTime::now_utc() + Duration::weeks(52)));
176155

177156
subscriptions_number_to_delete_from += 1;
178157
}
@@ -217,14 +196,7 @@ fn set_cookies_method(req: Request<Body>, remove_cookies: bool) -> Response<Body
217196
format!("filters{filters_number}")
218197
};
219198

220-
response.insert_cookie(
221-
Cookie::build((filters_cookie, list))
222-
.path("/")
223-
.http_only(true)
224-
.secure(true)
225-
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
226-
.into(),
227-
);
199+
response.insert_cookie(build_cookie(filters_cookie, list, OffsetDateTime::now_utc() + Duration::weeks(52)));
228200

229201
filters_number_to_delete_from += 1;
230202
}

src/subreddit.rs

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
use crate::utils::{
44
Post, Preferences, Subreddit, catch_random, error, filter_posts, format_num, format_url, get_filters, info, nsfw_landing, param, redirect, rewrite_urls, setting, setting_or_default, template, to_absolute_url, val
55
};
6-
use crate::{client::json, server::RequestExt, server::ResponseExt};
6+
use crate::{client::json, server::{build_cookie, build_session_cookie, RequestExt, ResponseExt}};
77
use crate::{config, utils};
88
use askama::Template;
9-
use cookie::Cookie;
109
use htmlescape::decode_html;
1110
use hyper::{Request, Response};
1211
use crate::server::{full, Body};
@@ -215,14 +214,7 @@ pub async fn add_quarantine_exception(req: Request<Body>) -> Result<Response<Bod
215214
let subreddit = req.param("sub").ok_or("Invalid URL")?;
216215
let redir = param(&format!("?{}", req.uri().query().unwrap_or_default()), "redir").ok_or("Invalid URL")?;
217216
let mut response = redirect(&redir);
218-
response.insert_cookie(
219-
Cookie::build((&format!("allow_quaran_{}", subreddit.to_lowercase()), "true"))
220-
.path("/")
221-
.http_only(true)
222-
.secure(true)
223-
.expires(cookie::Expiration::Session)
224-
.into(),
225-
);
217+
response.insert_cookie(build_session_cookie(format!("allow_quaran_{}", subreddit.to_lowercase()), "true"));
226218
Ok(response)
227219
}
228220

@@ -386,14 +378,7 @@ pub async fn subscriptions_filters(req: Request<Body>) -> Result<Response<Body>,
386378
format!("subscriptions{subscriptions_number}")
387379
};
388380

389-
response.insert_cookie(
390-
Cookie::build((subscriptions_cookie, list))
391-
.path("/")
392-
.http_only(true)
393-
.secure(true)
394-
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
395-
.into(),
396-
);
381+
response.insert_cookie(build_cookie(subscriptions_cookie, list, OffsetDateTime::now_utc() + Duration::weeks(52)));
397382

398383
subscriptions_number_to_delete_from += 1;
399384
}
@@ -435,14 +420,7 @@ pub async fn subscriptions_filters(req: Request<Body>) -> Result<Response<Body>,
435420
format!("filters{filters_number}")
436421
};
437422

438-
response.insert_cookie(
439-
Cookie::build((filters_cookie, list))
440-
.path("/")
441-
.http_only(true)
442-
.secure(true)
443-
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
444-
.into(),
445-
);
423+
response.insert_cookie(build_cookie(filters_cookie, list, OffsetDateTime::now_utc() + Duration::weeks(52)));
446424

447425
filters_number_to_delete_from += 1;
448426
}

0 commit comments

Comments
 (0)