Rust安全防护库actix-csrf的使用,为Actix Web框架提供CSRF攻击防护与令牌验证功能

// 内容中提供的示例代码:

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        let csrf = Csrf::<StdRng>::new()
            .set_cookie(Method::GET, "/login");
        App::new().wrap(csrf).service(login_ui).service(login)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

#[derive(Deserialize)]
struct LoginForm {
    csrf_token: CsrfToken,
    username: String,
    password: String,
}

impl CsrfGuarded for LoginForm {
    fn csrf_token(&self) -> &CsrfToken {
        &self.csrf_token
    }
}

/// Validates a login form that has a CSRF token.
#[post("/login")]
async fn login(form: Csrf<Form<LoginForm>>) -> impl Responder {
    // At this point, we have a valid CSRF token, so we can treat the request
    // as legitimate.

    HttpResponse::Ok().finish()
}
// 完整示例demo:

use actix_csrf::{Csrf, CsrfGuarded, CsrfToken};
use actix_web::{
    web::{Form, ServiceConfig},
    App, HttpResponse, HttpServer, Responder,
};
use actix_web::http::Method;
use rand::rngs::StdRng;
use serde::Deserialize;

// 定义登录表单结构体
#[derive(Deserialize)]
struct LoginForm {
    csrf_token: CsrfToken,  // CSRF令牌字段
    username: String,       // 用户名
    password: String,       // 密码
}

// 为LoginForm实现CsrfGuarded trait
impl CsrfGuarded for LoginForm {
    fn csrf_token(&self) -> &CsrfToken {
        &self.csrf_token  // 返回CSRF令牌引用
    }
}

// 登录UI处理函数
#[get("/login")]
async fn login_ui() -> impl Responder {
    HttpResponse::Ok().body(
        r#"
        <form method="post" action="/login">
            <input type="hidden" name="csrf_token" value="{{csrf_token}}">
            <input type="text" name="username" placeholder="Username">
            <input type="password" name="password" placeholder="Password">
            <button type="submit">Login</button>
        </form>
        "#,
    )
}

// 登录处理函数,使用Csrf包装器验证CSRF令牌
#[post("/login")]
async fn login(form: Csrf<Form<LoginForm>>) -> impl Responder {
    // 此时CSRF令牌已验证通过,可以安全处理请求
    
    // 这里可以添加实际的登录验证逻辑
    println!("Login attempt for user: {}", form.username);
    
    HttpResponse::Ok().body("Login successful!")
}

// 主函数,配置HTTP服务器和CSRF中间件
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        // 创建CSRF中间件实例,使用StdRng作为随机数生成器
        let csrf = Csrf::<StdRng>::new()
            .set_cookie(Method::GET, "/login");  // 为GET /login请求设置CSRF cookie
        
        App::new()
            .wrap(csrf)  // 应用CSRF中间件
            .service(login_ui)  // 注册登录UI服务
            .service(login)     // 注册登录处理服务
    })
    .bind(("127.0.0.1", 8080))?  // 绑定到本地8080端口
    .run()
    .await
}

actix-csrf是为actix-web 4.0.0或更新版本提供的CSRF中间件,使用双重提交令牌模式。

此crate尚未经过审计。在生产环境中使用需自行承担风险。

使用方法: 安装中间件是标准的:指定要使用的密码学安全随机数生成器,并声明哪些路径应该设置CSRF cookie以及何时应该验证CSRF cookie。

然后,使用CsrfCookie提取器提取CSRF cookie,并使用作为受保护请求一部分提供的CSRF令牌进行验证。

这只是使用双重提交令牌模式的多种方式之一。

安全考虑: 使用双重提交令牌模式有优点和局限性。强烈建议用户在使用此中间件之前阅读关于CSRF防护的文章。

此crate尝试具有安全的默认设置,用户必须显式禁用纵深防御功能。

许可证: 根据以下任一许可证授权:

  • Apache许可证,版本2.0
  • MIT许可证

根据您的选择。

贡献: 除非您明确声明,否则任何有意提交包含在作品中的贡献,如Apache-2.0许可证中所定义,应按照上述双重许可,不附加任何额外条款或条件。


1 回复

Rust安全防护库actix-csrf的使用指南

概述

actix-csrf是为Actix Web框架设计的CSRF(跨站请求伪造)防护库,提供令牌生成、验证和中间件集成功能,帮助开发者有效防范CSRF攻击。

主要功能

  • 自动生成和验证CSRF令牌
  • 支持Cookie和Header两种令牌存储方式
  • 可配置的令牌生命周期和加密选项
  • 与Actix Web中间件无缝集成

安装方法

在Cargo.toml中添加依赖:

[dependencies]
actix-csrf = "0.4"
actix-web = "4.0"

基本使用方法

1. 基础配置示例

use actix_csrf::{Csrf, CsrfMiddleware, CsrfConfig};
use actix_web::{web, App, HttpServer, HttpResponse};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        let csrf_config = CsrfConfig::default()
            .with_cookie_name("csrf_token")
            .with_session_ttl(std::time::Duration::from_secs(3600));
        
        App::new()
            .wrap(CsrfMiddleware::new(csrf_config))
            .route("/", web::get().to(show_form))
            .route("/submit", web::post().to(handle_submit))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

async fn show_form(csrf: Csrf) -> HttpResponse {
    let token = csrf.create_token().unwrap();
    HttpResponse::Ok().body(format!(
        r#"<form action="/submit" method="post">
            <input type="hidden" name="csrf_token" value="{}">
            <input type="text" name="data">
            <button type="submit">Submit</button>
        </form>"#,
        token
    ))
}

async fn handle_submit(csrf: Csrf, form: web::Form<Data>) -> HttpResponse {
    if csrf.verify_token(&form.csrf_token).is_ok() {
        HttpResponse::Ok().body("Form submitted successfully!")
    } else {
        HttpResponse::BadRequest().body("Invalid CSRF token")
    }
}

#[derive(serde::Deserialize)]
struct Data {
    csrf_token: String,
    data: String,
}

2. 高级配置示例

use actix_csrf::{CsrfMiddleware, CsrfConfig, extraction::CookieExtractor};
use actix_web::cookie::Key;

let config = CsrfConfig::default()
    .with_extractor(CookieExtractor::default())
    .with_ttl(std::time::Duration::from_secs(1800))
    .with_key(Key::generate()); // 使用自定义加密密钥

let middleware = CsrfMiddleware::new(config);

3. 自定义错误处理

use actix_csrf::{CsrfError, Csrf};
use actix_web::{error, HttpResponse};

async fn protected_endpoint(csrf: Csrf) -> Result<HttpResponse, actix_web::Error> {
    match csrf.create_token() {
        Ok(token) => Ok(HttpResponse::Ok().body(token)),
        Err(CsrfError::TokenGeneration) => {
            Err(error::ErrorInternalServerError("Token generation failed"))
        }
        Err(_) => Err(error::ErrorBadRequest("CSRF validation failed")),
    }
}

配置选项

  • with_cookie_name(): 设置Cookie名称
  • with_ttl(): 设置令牌有效期
  • with_extractor(): 设置令牌提取方式(Cookie或Header)
  • with_key(): 设置加密密钥

注意事项

  1. 确保在生产环境中使用安全的加密密钥
  2. 对于API请求,建议使用Header方式传递令牌
  3. 令牌有效期应根据实际业务需求设置
  4. 在HTTPS环境下使用以获得最佳安全性

测试验证

建议使用actix-web的测试框架编写单元测试,验证CSRF防护功能正常工作。

这个库为Actix Web应用提供了简单易用的CSRF防护方案,通过中间件集成可以快速为现有应用添加安全防护层。

完整示例代码

use actix_csrf::{Csrf, CsrfMiddleware, CsrfConfig, extraction::CookieExtractor};
use actix_web::{web, App, HttpServer, HttpResponse, cookie::Key};
use serde::Deserialize;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // 配置CSRF中间件
    let csrf_config = CsrfConfig::default()
        .with_cookie_name("csrf_token") // 设置cookie名称
        .with_extractor(CookieExtractor::default()) // 使用cookie提取器
        .with_ttl(std::time::Duration::from_secs(3600)) // 设置令牌有效期1小时
        .with_key(Key::generate()); // 生成加密密钥

    HttpServer::new(move || {
        App::new()
            // 添加CSRF中间件
            .wrap(CsrfMiddleware::new(csrf_config.clone()))
            // 注册路由
            .route("/", web::get().to(show_form))
            .route("/submit", web::post().to(handle_submit))
            .route("/api/token", web::get().to(get_token))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

// 显示包含CSRF令牌的表单
async fn show_form(csrf: Csrf) -> HttpResponse {
    match csrf.create_token() {
        Ok(token) => {
            HttpResponse::Ok().body(format!(
                r#"<!DOCTYPE html>
                <html>
                <head>
                    <title>CSRF防护示例</title>
                </head>
                <body>
                    <h1>表单提交示例</h1>
                    <form action="/submit" method="post">
                        <input type="hidden" name="csrf_token" value="{}">
                        <div>
                            <label for="data">输入数据:</label>
                            <input type="text" id="data" name="data">
                        </div>
                        <button type="submit">提交</button>
                    </form>
                </body>
                </html>"#,
                token
            ))
        }
        Err(_) => HttpResponse::InternalServerError().body("令牌生成失败"),
    }
}

// 处理表单提交
async fn handle_submit(csrf: Csrf, form: web::Form<FormData>) -> HttpResponse {
    // 验证CSRF令牌
    match csrf.verify_token(&form.csrf_token) {
        Ok(_) => {
            HttpResponse::Ok().body(format!("表单提交成功! 数据: {}", form.data))
        }
        Err(_) => {
            HttpResponse::BadRequest().body("无效的CSRF令牌")
        }
    }
}

// API端点 - 获取CSRF令牌
async fn get_token(csrf: Csrf) -> Result<HttpResponse, actix_web::Error> {
    match csrf.create_token() {
        Ok(token) => Ok(HttpResponse::Ok().body(token)),
        Err(_) => Err(actix_web::error::ErrorInternalServerError("令牌生成失败")),
    }
}

// 表单数据结构
#[derive(Debug, Deserialize)]
struct FormData {
    csrf_token: String,
    data: String,
}

这个完整示例展示了如何使用actix-csrf库:

  1. 配置CSRF中间件并设置相关参数
  2. 创建包含CSRF令牌的HTML表单
  3. 验证提交的表单中的CSRF令牌
  4. 提供API端点获取CSRF令牌
  5. 包含完整的错误处理机制

要运行此示例,请确保在Cargo.toml中添加了正确的依赖项,并根据需要调整配置参数。

回到顶部