use cookies properly, fixes #31

This commit is contained in:
neil 2021-04-13 00:01:08 +02:00
parent 5a521b0497
commit c24b98bcca
1 changed files with 49 additions and 76 deletions

View File

@ -1,9 +1,9 @@
use actix_web::client::{Client, ClientRequest};
use actix_web::{http, web, HttpRequest, HttpResponse};
use actix_session::Session;
use askama::Template;
use chrono::Utc;
use csrf::{AesGcmCsrfProtection, CsrfProtection};
use regex::Regex;
use std::time::Duration;
use url::Url;
@ -180,8 +180,10 @@ pub async fn forward_login(
// creates a NC account using a random name and password.
// the account gets associated with a token in sqlite DB.
// POST /link route
pub async fn forward_register(
req: HttpRequest,
s: Session,
csrf_post: web::Form<CsrfToken>,
client: web::Data<Client>,
dbpool: web::Data<DbPool>,
@ -196,62 +198,30 @@ pub async fn forward_register(
})?);
}
let cookie_admin_token = s.get::<String>("sncf_admin_token").map_err(|e| {
eprintln!("error_forwardregister_tokenparse: {}", e);
crash(get_lang(&req), "error_forwardregister_tokenparse")
})?;
// if the user has already generated an admin token, redirect too
if let Some(token) = has_admintoken(&req) {
lazy_static! {
static ref RE: Regex = Regex::new(r#"sncf_admin_token=(?P<token>[0-9A-Za-z_\-]*)"#)
.expect("Error while parsing the sncf_admin_token regex");
}
let admin_token = RE
.captures(&token)
.ok_or_else(|| {
eprintln!("error_forwardregister_tokenparse (no capture)");
crash(get_lang(&req), "error_forwardregister_tokenparse")
})?
.name("token")
.ok_or_else(|| {
eprintln!("error_forwardregister_tokenparse (no capture named token)");
crash(get_lang(&req), "error_forwardregister_tokenparse")
})?
.as_str();
// sanitize the token beforehand, cookies are unsafe
if check_token(&admin_token) {
return Ok(
web_redir(&format!("{}/admin/{}", CONFIG.sncf_url, &admin_token))
.await
.map_err(|e| {
eprintln!("error_redirect (admin): {}", e);
crash(get_lang(&req), "error_redirect")
})?,
);
} else {
debug("Incorrect admin token given in cookies.");
debug(&format!("Token: {:#?}", &admin_token));
return Err(crash(lang, "error_dirtyhacker"));
}
if let Some(admin_token) = cookie_admin_token {
return Ok(
web_redir(&format!("{}/admin/{}", CONFIG.sncf_url, &admin_token))
.await
.map_err(|e| {
eprintln!("error_redirect (admin): {}", e);
crash(get_lang(&req), "error_redirect")
})?,
);
}
// check if the csrf token is OK
if let Some(cookie_token) = has_csrftoken(&req) {
lazy_static! {
static ref RE: Regex = Regex::new(r#"sncf_csrf_cookie=(?P<token>[0-9A-Za-z_\-]*)"#)
.expect("Error while parsing the sncf_csrf_cookie regex");
}
let cookie_csrf_token = RE
.captures(&cookie_token)
.ok_or_else(|| {
eprintln!("error_csrf_cookie: no capture");
crash(get_lang(&req), "error_csrf_cookie")
})?
.name("token")
.ok_or_else(|| {
eprintln!("error_csrf_cookie: no capture named token");
crash(get_lang(&req), "error_csrf_cookie")
})?
.as_str();
let cookie_csrf_token = s.get::<String>("sncf_csrf_token").map_err(|e| {
eprintln!("error_csrf_cookie: {}", e);
crash(get_lang(&req), "error_csrf_cookie")
})?;
if let Some(cookie_token) = cookie_csrf_token {
let raw_ctoken =
base64::decode_config(cookie_csrf_token.as_bytes(), base64::URL_SAFE_NO_PAD).map_err(
base64::decode_config(cookie_token.as_bytes(), base64::URL_SAFE_NO_PAD).map_err(
|e| {
eprintln!("error_csrf_cookie (base64): {}", e);
crash(get_lang(&req), "error_csrf_cookie")
@ -260,14 +230,14 @@ pub async fn forward_register(
let raw_token =
base64::decode_config(csrf_post.csrf_token.as_bytes(), base64::URL_SAFE_NO_PAD)
.map_err(|e| {
eprintln!("error_csrf_token (base64): {}", e);
crash(get_lang(&req), "error_csrf_token")
})?;
.map_err(|e| {
eprintln!("error_csrf_token (base64): {}", e);
crash(get_lang(&req), "error_csrf_token")
})?;
let seed = AesGcmCsrfProtection::from_key(get_csrf_key());
let parsed_token = seed.parse_token(&raw_token).expect("token not parsed");
let parsed_cookie = seed.parse_cookie(&raw_ctoken).expect("cookie not parsed");
let parsed_token = seed.parse_token(&raw_token).expect("error: token not parsed");
let parsed_cookie = seed.parse_cookie(&raw_ctoken).expect("error: cookie not parsed");
if !seed.verify_token_pair(&parsed_token, &parsed_cookie) {
debug("warn: CSRF token doesn't match.");
return Err(crash(lang, "error_csrf_token"));
@ -313,23 +283,23 @@ pub async fn forward_register(
return Err(crash(lang, "error_forwardregister_db"));
}
s.set("sncf_admin_token", &token).map_err(|e| {
eprintln!("error_login_setcookie (in register): {}", e);
crash(lang.clone(), "error_login_setcookie")
})?;
Ok(HttpResponse::Ok()
.content_type("text/html")
.set_header(
"Set-Cookie",
format!("sncf_admin_token={}; HttpOnly; Secure; SameSite=Strict", &token),
)
.body(
TplLink {
lang: &lang,
admin_token: &token,
config: &CONFIG,
}
.render()
.map_err(|e| {
eprintln!("error_tplrender (TplLink): {}", e);
crash(lang.clone(), "error_tplrender")
})?,
}
.render()
.map_err(|e| {
eprintln!("error_tplrender (TplLink): {}", e);
crash(lang.clone(), "error_tplrender")
})?,
)
.await
.map_err(|e| {
@ -370,25 +340,28 @@ fn web_redir(location: &str) -> HttpResponse {
.finish()
}
pub async fn index(req: HttpRequest) -> Result<HttpResponse, TrainCrash> {
pub async fn index(req: HttpRequest, s: Session) -> Result<HttpResponse, TrainCrash> {
let seed = AesGcmCsrfProtection::from_key(get_csrf_key());
let (csrf_token, csrf_cookie) = seed
.generate_token_pair(None, 43200)
.expect("couldn't generate token/cookie pair");
s.set("sncf_csrf_token", &base64::encode_config(&csrf_cookie.value(), base64::URL_SAFE_NO_PAD)).map_err(|e| {
eprintln!("error_login_setcookie (in index): {}", e);
crash(get_lang(&req), "error_login_setcookie")
})?;
let cookie_admin_token = s.get::<String>("sncf_admin_token").map_err(|e| {
eprintln!("error_forwardregister_tokenparse (index): {}", e);
crash(get_lang(&req), "error_forwardregister_tokenparse")
})?;
Ok(HttpResponse::Ok()
.content_type("text/html")
.set_header(
"Set-Cookie",
format!(
"sncf_csrf_cookie={}; HttpOnly; Secure; SameSite=Strict",
base64::encode_config(&csrf_cookie.value(), base64::URL_SAFE_NO_PAD)
),
)
.body(
TplIndex {
lang: &get_lang(&req),
csrf_token: &base64::encode_config(&csrf_token.value(), base64::URL_SAFE_NO_PAD),
sncf_admin_token: cookie_admin_token,
}
.render()
.map_err(|e| {