#token #csrf #tower #secret #middleware #stateless #cookies

tower-surf

🌊 tower的一个无状态CSRF中间件

3个版本 (重大变更)

0.3.0 2024年8月20日
0.2.0 2024年8月19日
0.1.0 2024年8月19日

#496 in 密码学

Download history 329/week @ 2024-08-14

每月 338次下载

MIT/Apache

28KB
410

🌊 tower-surf

tower提供无状态CSRF中间件。

Crates MSRV Docs CI

🏄‍♂️ 概述

这个crate使用双重提交Cookie模式来减轻CSRF。

工作原理

  • 密钥:您提供一个用于签名CSRF令牌的密钥。此密钥通过secstr加密,并在签名和验证过程中仅在内存中以明文形式存储。有关密钥管理的更多信息,请参阅OWASP的加密存储便签
  • 令牌创建:
    • 我们通过结合一个唯一的会话标识符和一个加密安全的随机值(使用rand crate)生成一个消息。
    • 然后我们使用密钥和消息创建一个签名。
    • 令牌通过连接签名和消息形成。
  • 令牌存储:
    • 服务器以两种方式将令牌发送到客户端
      • 作为cookie(由我们处理)。
      • 在后续请求的头部(由您处理)。
  • 令牌验证:
    • 对于每个即将改变状态的传入请求
      • 我们从请求头部提取令牌。
      • 我们将令牌分成签名和消息。
      • 我们使用密钥和消息重新计算签名并比较它们。
    • 如果签名有效且令牌与存储在cookie中的值匹配,则允许请求继续。

Cookie

默认情况下,cookie被设置为HTTPOnlySameSite: StrictSecure

📦 安装

[dependencies]
tower-surf = "0.3.0"

🗝️ 使用方法

使用axum

use std::net::SocketAddr;

use axum::{routing::get, Router};
use http::StatusCode;
use tower_surf::{Surf, Token};

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/login", get(login)).route("/logout", get(logout))
        .layer(Surf::new("secret-key").secure(false));

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();

    axum::serve(listener, app.into_make_service())
        .await
        .unwrap();
}

async fn login(token: Token) -> Result<StatusCode, StatusCode> {
    token.set("unique-session-id").map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(StatusCode::OK)
}

async fn logout(token: Token) -> StatusCode {
    token.reset();

    StatusCode::OK
}

[!注意] 请参阅示例以获取完整示例。

🥰 感谢

  • 我阅读了很多tower-sessions代码库,以了解如何创建一个tower项目。
  • tokio社区回答了很多愚蠢的问题。

依赖项

~4-10MB
~101K SLoC