feat(auth): 添加 Turnstile 注册防护

This commit is contained in:
Entropy.Xu
2026-05-15 01:54:20 +08:00
parent bf511f9f8c
commit 85f48123b6
25 changed files with 1705 additions and 22 deletions

View File

@@ -6,5 +6,5 @@ pub(crate) use access_log::{
access_log_middleware, should_downgrade_access_log, RequestLogEmitted,
};
pub(crate) use frontdoor_cors::frontdoor_cors_middleware;
pub(crate) use strip_cf_headers::apply_cf_header_stripping;
pub use strip_cf_headers::strip_cf_headers_middleware;
pub(crate) use strip_cf_headers::{apply_cf_header_stripping, CfConnectingIp};

View File

@@ -4,11 +4,23 @@ use http::{header::HeaderName, HeaderMap};
/// Cloudflare-specific headers that are not part of the `cf-*` prefix family.
const CF_EXACT_HEADERS: &[&str] = &["cdn-loop", "true-client-ip"];
#[derive(Clone, Debug)]
pub(crate) struct CfConnectingIp(pub(crate) String);
fn should_strip_cf_header(name: &HeaderName) -> bool {
let normalized = name.as_str();
normalized.starts_with("cf-") || CF_EXACT_HEADERS.contains(&normalized)
}
fn cf_connecting_ip(headers: &HeaderMap) -> Option<String> {
headers
.get("cf-connecting-ip")
.and_then(|value| value.to_str().ok())
.map(str::trim)
.filter(|value| !value.is_empty())
.map(|value| value.chars().take(45).collect())
}
fn strip_cf_headers(headers: &mut HeaderMap) {
let to_remove: Vec<_> = headers
.keys()
@@ -25,6 +37,9 @@ pub(crate) fn apply_cf_header_stripping(router: Router) -> Router {
}
pub async fn strip_cf_headers_middleware(mut request: Request, next: Next) -> Response {
if let Some(client_ip) = cf_connecting_ip(request.headers()) {
request.extensions_mut().insert(CfConnectingIp(client_ip));
}
strip_cf_headers(request.headers_mut());
let mut response = next.run(request).await;
@@ -51,6 +66,7 @@ mod tests {
any(|headers: http::HeaderMap| async move {
let leaked = headers.contains_key("cf-ipcity")
|| headers.contains_key("cf-ray")
|| headers.contains_key("cf-connecting-ip")
|| headers.contains_key("true-client-ip")
|| headers.contains_key("cdn-loop");
let mut response =
@@ -81,6 +97,7 @@ mod tests {
.uri("/")
.header("cf-ipcity", "Shanghai")
.header("cf-ray", "abc123")
.header("cf-connecting-ip", "203.0.113.10")
.header("true-client-ip", "1.1.1.1")
.header("cdn-loop", "cloudflare")
.body(Body::empty())
@@ -91,6 +108,7 @@ mod tests {
assert!(response.headers().get("cf-ipcity").is_none());
assert!(response.headers().get("cf-cache-status").is_none());
assert!(response.headers().get("cf-connecting-ip").is_none());
assert!(response.headers().get("true-client-ip").is_none());
assert!(response.headers().get("cdn-loop").is_none());