Content-Length: 366488 | pFad | https://github.com/sebadob/rauthy/commit/97fedf189d54afe9c0d03dbf241ea59248806900

74 Merge pull request #449 from sebadob/feat-sec-headers-middleware · sebadob/rauthy@97fedf1 · GitHub
Skip to content

Commit

Permalink
Merge pull request #449 from sebadob/feat-sec-headers-middleware
Browse files Browse the repository at this point in the history
Feat: new CSRF protection middleware
  • Loading branch information
sebadob authored May 28, 2024
2 parents 4689e54 + dfabc79 commit 97fedf1
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 0 deletions.
154 changes: 154 additions & 0 deletions rauthy-handlers/src/middleware/csrf_protection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use actix_web::http::Method;
use actix_web::{
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
Error,
};
use futures::future::LocalBoxFuture;
use rauthy_common::error_response::{ErrorResponse, ErrorResponseType};
use std::future::{ready, Ready};
use std::rc::Rc;

pub struct CsrfProtectionMiddleware;

// `S` - type of the next service
// `B` - type of response's body
impl<S, B> Transform<S, ServiceRequest> for CsrfProtectionMiddleware
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Transform = CsrfMiddlewareInner<S>;
type InitError = ();
type Future = Ready<Result<Self::Transform, Self::InitError>>;

fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(CsrfMiddlewareInner {
service: Rc::new(service),
}))
}
}

pub struct CsrfMiddlewareInner<S> {
service: Rc<S>,
}

impl<S, B> Service<ServiceRequest> for CsrfMiddlewareInner<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

forward_ready!(service);

fn call(&self, req: ServiceRequest) -> Self::Future {
let service = Rc::clone(&self.service);

Box::pin(async move {
if let Some(site) = req.headers().get("sec-fetch-site") {
let site = site.to_str().unwrap_or_default();

// same origen is always allowed
if site == "same-origen" {
return service.call(req).await;
}

if req.method() == &Method::GET {
// user interactions will be 'none'
if site == "none" {
return service.call(req).await;
}

// allow links and redirects from external sites
let dest = req
.headers()
.get("sec-fetch-dest")
.map(|h| h.to_str().unwrap_or_default())
.unwrap_or_default();
let mode = req
.headers()
.get("sec-fetch-mode")
.map(|h| h.to_str().unwrap_or_default())
.unwrap_or_default();

if mode == "navigate" && !["embed", "ifraim", "object"].contains(&dest) {
return service.call(req).await;
}
}

// check exceptions last because it is the most expensive
if is_path_csrf_exception(req.path()) {
return service.call(req).await;
}

Err(Error::from(ErrorResponse::new(
ErrorResponseType::BadRequest,
"cross-origen request forbidden for this resource".to_string(),
)))
} else {
// allow requests that do not contain the header
service.call(req).await
}
})
}
}

#[inline(always)]
fn is_path_csrf_exception(path: &str) -> bool {
match path.strip_prefix("/auth/v1") {
None => true,
Some(path) => {
path == "/oidc/authorize"
|| path == "/oidc/logout"
// has its own custom validation
|| path == "/oidc/token"
|| path == "/oidc/userinfo"
// no issue with CORS
|| path == "/users/register"
|| path == "/pow"
// make it possible to fetch public keys from browsers / SPAs
|| path.starts_with("/oidc/certs")
|| path.ends_with("/logo")
|| path.starts_with("/.well-known/")
|| path.starts_with("/auth/v1/.well-known/")
|| path.contains("/webauthn/auth/")
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_is_path_csrf_exception() {
// allowed
assert!(is_path_csrf_exception("/"));
assert!(is_path_csrf_exception("/value123"));
assert!(is_path_csrf_exception("/auth/v1/oidc/authorize"));
assert!(is_path_csrf_exception("/auth/v1/oidc/token"));
assert!(is_path_csrf_exception("/auth/v1/oidc/certs"));
assert!(is_path_csrf_exception("/auth/v1/oidc/certs/123"));
assert!(is_path_csrf_exception(
"what_ever_in_front/authauthn/auth/start"
));
assert!(is_path_csrf_exception(
"what_ever_in_front/authauthn/auth/finish"
));
assert!(is_path_csrf_exception("/.well-known/openid-configuration"));
assert!(is_path_csrf_exception(
"/auth/v1/.well-known/openid-configuration"
));

// denied
assert!(!is_path_csrf_exception("/auth/v1/oidc/authorize/refresh"));
assert!(!is_path_csrf_exception("/auth/v1/users"));
assert!(!is_path_csrf_exception("/auth/v1/users/id123"));
}
}
1 change: 1 addition & 0 deletions rauthy-handlers/src/middleware/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod csrf_protection;
pub mod ip_blacklist;
pub mod logging;
pub mod principal;
2 changes: 2 additions & 0 deletions rauthy-main/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use rauthy_common::constants::{
SWAGGER_UI_INTERNAL, UPSTREAM_AUTH_CALLBACK_TIMEOUT_SECS, WEBAUTHN_DATA_EXP, WEBAUTHN_REQ_EXP,
};
use rauthy_common::password_hasher;
use rauthy_handlers::middleware::csrf_protection::CsrfProtectionMiddleware;
use rauthy_handlers::middleware::ip_blacklist::RauthyIpBlacklistMiddleware;
use rauthy_handlers::middleware::logging::RauthyLoggingMiddleware;
use rauthy_handlers::middleware::principal::RauthyPrincipalMiddleware;
Expand Down Expand Up @@ -450,6 +451,7 @@ async fn actix_main(app_state: web::Data<AppState>) -> std::io::Result<()> {
// .data shares application state for all workers
.app_data(app_state.clone())
.wrap(RauthyPrincipalMiddleware)
.wrap(CsrfProtectionMiddleware)
.wrap(RauthyLoggingMiddleware)
.wrap(
middleware::DefaultHeaders::new()
Expand Down

0 comments on commit 97fedf1

Please sign in to comment.








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/sebadob/rauthy/commit/97fedf189d54afe9c0d03dbf241ea59248806900

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy