-
-
Notifications
You must be signed in to change notification settings - Fork 160
fix: prevent SSRF and open redirect in alert targets and OIDC logout #1615
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -167,11 +167,23 @@ pub async fn login( | |
| } | ||
| } | ||
|
|
||
| pub async fn logout(req: HttpRequest, query: web::Query<RedirectAfterLogin>) -> HttpResponse { | ||
| pub async fn logout( | ||
| req: HttpRequest, | ||
| query: web::Query<RedirectAfterLogin>, | ||
| ) -> Result<HttpResponse, OIDCError> { | ||
| // Validate redirect URL against server host to prevent open redirect attacks | ||
| let conn = req.connection_info().clone(); | ||
| let base_url_without_scheme = format!("{}/", conn.host()); | ||
| if !is_valid_redirect_url(&base_url_without_scheme, query.redirect.as_str()) { | ||
| return Err(OIDCError::BadRequest( | ||
| "Bad Request, Invalid Redirect URL!".to_string(), | ||
| )); | ||
| } | ||
|
Comment on lines
+174
to
+181
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the configured external origin for logout redirects.
🔒 Suggested direction- let conn = req.connection_info().clone();
- let base_url_without_scheme = format!("{}/", conn.host());
- if !is_valid_redirect_url(&base_url_without_scheme, query.redirect.as_str()) {
+ let configured = Url::parse(&PARSEABLE.options.address)
+ .map_err(|_| OIDCError::BadRequest("Invalid server base URL".to_string()))?;
+ if query.redirect.scheme() != configured.scheme()
+ || query.redirect.host_str() != configured.host_str()
+ || query.redirect.port_or_known_default() != configured.port_or_known_default()
+ {
return Err(OIDCError::BadRequest(
"Bad Request, Invalid Redirect URL!".to_string(),
));
}Applying the same helper change to 🤖 Prompt for AI Agents |
||
|
|
||
| let oidc_client = OIDC_CLIENT.get(); | ||
|
|
||
| let Some(session) = extract_session_key_from_req(&req).ok() else { | ||
| return redirect_to_client(query.redirect.as_str(), None); | ||
| return Ok(redirect_to_client(query.redirect.as_str(), None)); | ||
| }; | ||
| let tenant_id = get_tenant_id_from_key(&session); | ||
| let user = Users.remove_session(&session); | ||
|
|
@@ -181,14 +193,14 @@ pub async fn logout(req: HttpRequest, query: web::Query<RedirectAfterLogin>) -> | |
| None | ||
| }; | ||
|
|
||
| match (user, logout_endpoint) { | ||
| Ok(match (user, logout_endpoint) { | ||
| (Some(username), Some(logout_endpoint)) | ||
| if Users.is_oauth(&username, &tenant_id).unwrap_or_default() => | ||
| { | ||
| redirect_to_oidc_logout(logout_endpoint, &query.redirect) | ||
| } | ||
| _ => redirect_to_client(query.redirect.as_str(), None), | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| /// Handler for code callback | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Doesreqwest::ClientBuilderfollow HTTP redirects by default for POST/307/308 responses, and isredirect(reqwest::redirect::Policy::none())the supported way to disable them?💡 Result:
Yes.
reqwest::ClientBuilder(andClient) usesredirect::Policy::default(), which automatically follows redirects up to 10 hops. This applies to redirects in general, including 307/308 responses. [1], [2]redirect(reqwest::redirect::Policy::none())is the supported way to disable all redirect behavior. [2], [4]Sources:
[1] https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html
[2] https://docs.rs/reqwest/latest/reqwest/redirect/
[3] https://docs.rs/reqwest/latest/src/reqwest/redirect.rs.html
[4] https://docs.rs/reqwest/latest/reqwest/redirect/struct.Policy.html
🏁 Script executed:
Repository: parseablehq/parseable
Length of output: 1306
🏁 Script executed:
Repository: parseablehq/parseable
Length of output: 91
🏁 Script executed:
Repository: parseablehq/parseable
Length of output: 1820
🏁 Script executed:
Repository: parseablehq/parseable
Length of output: 1413
🏁 Script executed:
Repository: parseablehq/parseable
Length of output: 290
Disable HTTP redirects to prevent SSRF via redirect-based attacks.
HTTP redirects bypass DNS pinning. An attacker-controlled webhook can respond with a
30x Locationheader pointing to an internal address like169.254.169.254. Reqwest follows redirects by default (including 307/308 responses), allowing this follow-up request to reach internal services unvalidated.Disable redirects for all webhook clients:
Fix
async fn apply_dns_pinning( mut builder: ClientBuilder, endpoint: &Url, ) -> Result<ClientBuilder, String> { + builder = builder.redirect(reqwest::redirect::Policy::none()); if let Some((host, addr)) = resolve_and_pin(endpoint).await? { builder = builder.resolve(&host, addr); } Ok(builder) }This affects all webhook implementations that call
apply_dns_pinning:SlackWebHook,OtherWebHook, andAlertManager.🤖 Prompt for AI Agents