【Rust】フレームワーク自作: 匿名セッションによるCSRF対策を実装したい

get methodもhashmapではなく、HttpResponseに変更することで、get methodでもcookieをセットできるようになる。

use std::collections::HashMap;
mod ares;
use ares::{Router, HttpResponse, parse, signup, logup, redirect, render_template_from_file, render_template_from_file_empty};

fn main() {
    let mut router = Router::new();

    router.get("index", handle_index);
    router.get("hello", handle_hello);
    router.post("test", handle_test);

    router.get("signin", handle_signin);
    router.post("signup", handle_signup);

    router.get("login", handle_login);
    router.post("logup", handle_logup);
    router.get("logout", handle_logout);

    let needs_auth = Some(vec!["/index", "/hello"]);
    router.up("192.168.33.10:8000", needs_auth);
}


fn handle_index() -> HttpResponse {
    let mut content = HashMap::new();
    content.insert(
        "title",
        "This is index page!",
    );
    let html = render_template_from_file("index", &content);
    HttpResponse::new(200, &html)
}

fn handle_hello() -> HttpResponse {
    let mut data = HashMap::new();
    data.insert("name", "taro");
    let html = render_template_from_file("hello", &data);
    HttpResponse::new(200, &html)
}

fn handle_signin() -> HttpResponse {
    let html = render_template_from_file_empty("signin");
    HttpResponse::new(200, &html)
}

fn handle_signup(body: String) -> HttpResponse {
    let form =  parse(&body);
    let binding = "<unknown>".to_string();
    let name = form.get("name").unwrap_or(&binding);
    let password = form.get("password").unwrap_or(&binding);
    let _csrf_token = form.get("csrf_token").unwrap_or(&"".to_string());
    let redirect_path = "/index";

    signup(name, password, redirect_path)
}

fn handle_login() -> HttpResponse {
    let html = render_template_from_file_empty("login");
    HttpResponse::new(200, &html)
}

fn handle_logup(body: String) -> HttpResponse {
    let form =  parse(&body);
    let binding = "<unknown>".to_string();
    let name = form.get("name").unwrap_or(&binding);
    let password = form.get("password").unwrap_or(&binding);

    let login_success_path = "/index";
    let login_failure_path = "/login";

    logup(name, password, login_success_path, login_failure_path)
}

fn handle_test(_body: String) -> HttpResponse {
    redirect("/index");
    HttpResponse::new(200, "<h1>This is Test!</h1>")
}

fn handle_logout() -> HttpResponse {
    let html = render_template_from_file_empty("logout");
    HttpResponse::new(200, &html)
}

以下のようにする

fn handle_login() -> HttpResponse {
    let (csrf_token, mut response) = create_csrf_token();
    let mut context = HashMap::new();
    context.insert("csrf_token", csrf_token);

    let html = render_template_from_file("login", &context);
    response.body = html;
    response
}
pub fn create_csrf_token() -> (&'static str, HttpResponse) {
    let (session_token, csrf_token) = create_session_token_and_csrf();
    if let Ok(mut client) = psql_connect() {
        let _ = client.execute(
            "INSERT INTO ares_sessions (session_token, csrf_token) VALUES ($1, $2)",
            &[&session_token, &csrf_token],
        );
    }
    let mut response = HttpResponse::new(200, "");
    response.headers.insert(
        "Set-Cookie".to_string(),
        format!("session_token={}; Path=/; HttpOnly", session_token),
    );
    let csrf_token_leaked = Box::leak(csrf_token.into_boxed_str());
    (csrf_token_leaked, response)
}

CSRFとセッションのチェックはライブラリ側で行いたい