19个不稳定版本 (8个破坏性更新)
0.9.0 | 2023年11月27日 |
---|---|
0.7.2 | 2023年7月13日 |
0.6.2 | 2023年3月31日 |
0.6.0 | 2022年11月26日 |
0.1.1 | 2021年11月24日 |
#123 in HTTP客户端
每月160次下载
用于 product-os-server
28KB
361 代码行
Axum_CSRF
为基于axum的Web应用程序提供CSRF(跨站请求伪造)保护层的库。目前支持Axum 0.6版本。
帮助
如果您需要关于此库的帮助,请加入我们的 Discord群组
安装
# Cargo.toml
[dependencies]
axum_csrf = "0.9.0"
Cargo功能标志
默认
: []
layer
:禁用状态并启用服务层。对于中间件交互很有用。
示例
通过共享状态将其添加到axum
use askama::Template;
use axum::{Form, response::IntoResponse, routing::get, Router};
use axum_csrf::{CsrfConfig, CsrfToken};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
#[derive(Template, Deserialize, Serialize)]
#[template(path = "template.html")]
struct Keys {
authenticity_token: String,
// Your attributes...
}
#[tokio::main]
async fn main() {
// initialize tracing
tracing_subscriber::fmt::init();
let config = CsrfConfig::default();
// build our application with a route
let app = Router::new()
// `GET /` goes to `root` and Post Goes to check key
.route("/", get(root).post(check_key))
.with_state(config);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
// root creates the CSRF Token and sends it into the page for return.
async fn root(token: CsrfToken) -> impl IntoResponse {
let keys = Keys {
//this Token is a hashed Token. it is returned and the original token is hashed for comparison.
authenticity_token: token.authenticity_token().unwrap(),
};
// We must return the token so that into_response will run and add it to our response cookies.
(token, keys).into_response()
}
async fn check_key(token: CsrfToken, Form(payload): Form<Keys>) -> &'static str {
// Verfiy the Hash and return the String message.
if token.verify(&payload.authenticity_token).is_err() {
"Token is invalid"
} else {
"Token is Valid lets do stuff!"
}
}
模板文件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Example</title>
</head>
<body>
<form method="post" action="/">
<input type="hidden" name="authenticity_token" value="{{ authenticity_token }}"/>
<input id="button" type="submit" value="Submit" tabindex="4" />
</form>
</body>
</html>
如果您不想使用状态,请使用“layer”功能
use askama::Template;
use axum::{Form, response::IntoResponse, routing::get, Router};
use axum_csrf::{CsrfConfig, CsrfLayer, CsrfToken };
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
#[derive(Template, Deserialize, Serialize)]
#[template(path = "template.html")]
struct Keys {
authenticity_token: String,
// Your attributes...
}
#[tokio::main]
async fn main() {
// initialize tracing
tracing_subscriber::fmt::init();
let config = CsrfConfig::default();
// build our application with a route
let app = Router::new()
// `GET /` goes to `root` and Post Goes to check key
.route("/", get(root).post(check_key))
.layer(CsrfLayer::new(config));
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
// root creates the CSRF Token and sends it into the page for return.
async fn root(token: CsrfToken) -> impl IntoResponse {
let keys = Keys {
//this Token is a hashed Token. it is returned and the original token is hashed for comparison.
authenticity_token: token.authenticity_token().unwrap(),
};
// We must return the token so that into_response will run and add it to our response cookies.
(token, keys).into_response()
}
async fn check_key(token: CsrfToken, Form(payload): Form<Keys>) -> &'static str {
// Verfiy the Hash and return the String message.
if token.verify(&payload.authenticity_token).is_err() {
"Token is invalid"
} else {
"Token is Valid lets do stuff!"
}
}
如果您已经有了用于私有cookie的加密密钥,请以不同的方式构建CSRF配置
let cookie_key = cookie::Key::generate();
let config = CsrfConfig::default().with_key(Some(cookie_key));
let app = Router::new().with_state(config)
使用CSRF防止Post重放攻击。
如果您想防止Post重放攻击,则应使用会话存储方法。您将散列存储在服务器端会话存储中,并将其与表单一起发送。当它们提交数据时,您将首先检查表单的散列,然后将其与内部会话数据进行比较。当第二个散列有效时,您将然后从会话中删除散列。这防止了重放攻击并确保没有数据被篡改。如果您需要会话数据库,我建议使用 axum_session
使用 axum_session
的更改。
async fn greet(token: CsrfToken, session: Session<SessionPgPool>) -> impl IntoResponse {
let authenticity_token = token.authenticity_token();
session.set("authenticity_token", authenticity_token.clone()).await;
let keys = Keys {
authenticity_token,
}
//we must return the token so that into_response will run and add it to our response cookies.
(token, keys).into_response()
}
验证CSRF密钥并验证Post重放攻击
async fn check_key(token: CsrfToken, session: Session<SessionPgPool>, Form(payload): Form<Keys>,) -> &'static str {
let authenticity_token: String = session.get("authenticity_token").await.unwrap_or_default();
if let Err(_) = token.verify(&payload.authenticity_token) {
"Token is invalid"
} else if let Err(_) = token.verify(&authenticity_token) {
"Modification of both Cookie/token OR a replay attack occured"
} else {
// we remove it to only allow one post per generated token.
session.remove("authenticity_token").await;
"Token is Valid lets do stuff!"
}
}
依赖项
~5MB
~94K SLoC