prevent using HTTP_PROXY if detected inside CGI (#684)
This commit is contained in:
		
							
								
								
									
										128
									
								
								src/proxy.rs
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								src/proxy.rs
									
									
									
									
									
								
							| @@ -373,6 +373,17 @@ impl ProxyScheme { | |||||||
|  |  | ||||||
|         Ok(scheme) |         Ok(scheme) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[cfg(test)] | ||||||
|  |     fn http_uri(&self) -> &http::Uri { | ||||||
|  |         match self { | ||||||
|  |             ProxyScheme::Http { ref uri, .. } => { | ||||||
|  |                 uri | ||||||
|  |             }, | ||||||
|  |             #[cfg(feature = "socks")] | ||||||
|  |             _ => panic!("socks not expected"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| type SystemProxyMap = HashMap<String, ProxyScheme>; | type SystemProxyMap = HashMap<String, ProxyScheme>; | ||||||
| @@ -521,28 +532,51 @@ fn get_sys_proxies() -> SystemProxyMap { | |||||||
|     proxies |     proxies | ||||||
| } | } | ||||||
|  |  | ||||||
| fn insert_proxy(proxies: &mut SystemProxyMap, schema: String, addr: String) { | fn insert_proxy(proxies: &mut SystemProxyMap, scheme: impl Into<String>, addr: String) -> bool { | ||||||
|     if let Ok(valid_addr) = addr.into_proxy_scheme() { |     if let Ok(valid_addr) = addr.into_proxy_scheme() { | ||||||
|         proxies.insert(schema, valid_addr); |         proxies.insert(scheme.into(), valid_addr); | ||||||
|  |         true | ||||||
|  |     } else { | ||||||
|  |         false | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn get_from_environment() -> SystemProxyMap { | fn get_from_environment() -> SystemProxyMap { | ||||||
|     let mut proxies = HashMap::new(); |     let mut proxies = HashMap::new(); | ||||||
|  |  | ||||||
|     const PROXY_KEY_ENDS: &str = "_proxy"; |     if is_cgi() { | ||||||
|  |         if log::log_enabled!(log::Level::Warn) { | ||||||
|  |             if env::var_os("HTTP_PROXY").is_some() { | ||||||
|  |                 log::warn!("HTTP_PROXY environment variable ignored in CGI"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else if !insert_from_env(&mut proxies, "http", "HTTP_PROXY") { | ||||||
|  |         insert_from_env(&mut proxies, "http", "http_proxy"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     for (key, value) in env::vars() { |     if !insert_from_env(&mut proxies, "https", "HTTPS_PROXY") { | ||||||
|         let key: String = key.to_lowercase(); |         insert_from_env(&mut proxies, "https", "https_proxy"); | ||||||
|         if key.ends_with(PROXY_KEY_ENDS) { |  | ||||||
|             let end_indx = key.len() - PROXY_KEY_ENDS.len(); |  | ||||||
|             let schema = &key[..end_indx]; |  | ||||||
|             insert_proxy(&mut proxies, String::from(schema), value); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     proxies |     proxies | ||||||
| } | } | ||||||
|  |  | ||||||
|  | fn insert_from_env(proxies: &mut SystemProxyMap, scheme: &str, var: &str) -> bool { | ||||||
|  |     if let Ok(val) = env::var(var) { | ||||||
|  |         insert_proxy(proxies, scheme, val) | ||||||
|  |     } else { | ||||||
|  |         false | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Check if we are being executed in a CGI context. | ||||||
|  | /// | ||||||
|  | /// If so, a malicious client can send the `Proxy:` header, and it will | ||||||
|  | /// be in the `HTTP_PROXY` env var. So we don't use it :) | ||||||
|  | fn is_cgi() -> bool { | ||||||
|  |     env::var_os("REQUEST_METHOD").is_some() | ||||||
|  | } | ||||||
|  |  | ||||||
| #[cfg(target_os = "windows")] | #[cfg(target_os = "windows")] | ||||||
| fn get_from_registry_impl() -> Result<SystemProxyMap, Box<dyn Error>> { | fn get_from_registry_impl() -> Result<SystemProxyMap, Box<dyn Error>> { | ||||||
|     let hkcu = RegKey::predef(HKEY_CURRENT_USER); |     let hkcu = RegKey::predef(HKEY_CURRENT_USER); | ||||||
| @@ -565,7 +599,7 @@ fn get_from_registry_impl() -> Result<SystemProxyMap, Box<dyn Error>> { | |||||||
|                 [protocol, address] => { |                 [protocol, address] => { | ||||||
|                     insert_proxy( |                     insert_proxy( | ||||||
|                         &mut proxies, |                         &mut proxies, | ||||||
|                         String::from(*protocol), |                         *protocol, | ||||||
|                         String::from(*address), |                         String::from(*address), | ||||||
|                     ); |                     ); | ||||||
|                 } |                 } | ||||||
| @@ -580,21 +614,16 @@ fn get_from_registry_impl() -> Result<SystemProxyMap, Box<dyn Error>> { | |||||||
|     } else { |     } else { | ||||||
|         // Use one setting for all protocols. |         // Use one setting for all protocols. | ||||||
|         if proxy_server.starts_with("http:") { |         if proxy_server.starts_with("http:") { | ||||||
|             insert_proxy(&mut proxies, String::from("http"), proxy_server); |             insert_proxy(&mut proxies, "http", proxy_server); | ||||||
|         } else { |         } else { | ||||||
|             insert_proxy( |             insert_proxy( | ||||||
|                 &mut proxies, |                 &mut proxies, | ||||||
|                 String::from("http"), |                 "http", | ||||||
|                 format!("http://{}", proxy_server), |                 format!("http://{}", proxy_server), | ||||||
|             ); |             ); | ||||||
|             insert_proxy( |             insert_proxy( | ||||||
|                 &mut proxies, |                 &mut proxies, | ||||||
|                 String::from("https"), |                 "https", | ||||||
|                 format!("https://{}", proxy_server), |  | ||||||
|             ); |  | ||||||
|             insert_proxy( |  | ||||||
|                 &mut proxies, |  | ||||||
|                 String::from("ftp"), |  | ||||||
|                 format!("https://{}", proxy_server), |                 format!("https://{}", proxy_server), | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
| @@ -699,12 +728,12 @@ mod tests { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_get_sys_proxies() { |     fn test_get_sys_proxies_parsing() { | ||||||
|         // save system setting first. |         // save system setting first. | ||||||
|         let system_proxy = env::var("http_proxy"); |         let _g1 = env_guard("HTTP_PROXY"); | ||||||
|  |         let _g2 = env_guard("http_proxy"); | ||||||
|  |  | ||||||
|         // remove proxy. |         // empty | ||||||
|         env::remove_var("http_proxy"); |  | ||||||
|         assert_eq!(get_sys_proxies().contains_key("http"), false); |         assert_eq!(get_sys_proxies().contains_key("http"), false); | ||||||
|  |  | ||||||
|         // the system proxy setting url is invalid. |         // the system proxy setting url is invalid. | ||||||
| @@ -715,18 +744,51 @@ mod tests { | |||||||
|         env::set_var("http_proxy", "http://127.0.0.1/"); |         env::set_var("http_proxy", "http://127.0.0.1/"); | ||||||
|         let proxies = get_sys_proxies(); |         let proxies = get_sys_proxies(); | ||||||
|  |  | ||||||
|         match &proxies["http"] { |         assert_eq!(proxies["http"].http_uri(), "http://127.0.0.1/"); | ||||||
|             ProxyScheme::Http { uri, .. } => { |  | ||||||
|                 assert_eq!(uri, "http://127.0.0.1/"); |         // reset user setting when guards drop | ||||||
|             }, |  | ||||||
|             #[cfg(feature = "socks")] |  | ||||||
|             _ => panic!("socks not expected"), |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|         // reset user setting. |     #[test] | ||||||
|         match system_proxy { |     fn test_get_sys_proxies_in_cgi() { | ||||||
|             Err(_) => env::remove_var("http_proxy"), |         // save system setting first. | ||||||
|             Ok(proxy) => env::set_var("http_proxy", proxy), |         let _g1 = env_guard("REQUEST_METHOD"); | ||||||
|  |         let _g2 = env_guard("HTTP_PROXY"); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         env::set_var("HTTP_PROXY", "http://evil/"); | ||||||
|  |  | ||||||
|  |         // not in CGI yet | ||||||
|  |         assert_eq!(get_sys_proxies()["http"].http_uri(), "http://evil/"); | ||||||
|  |  | ||||||
|  |         // set like we're in CGI | ||||||
|  |         env::set_var("REQUEST_METHOD", "GET"); | ||||||
|  |         assert!(!get_sys_proxies().contains_key("http")); | ||||||
|  |  | ||||||
|  |         // reset user setting when guards drop | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Guard an environment variable, resetting it to the original value | ||||||
|  |     /// when dropped. | ||||||
|  |     fn env_guard(name: impl Into<String>) -> EnvGuard { | ||||||
|  |         let name = name.into(); | ||||||
|  |         let orig_val = env::var(&name).ok(); | ||||||
|  |         env::remove_var(&name); | ||||||
|  |         EnvGuard { name, orig_val } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     struct EnvGuard { | ||||||
|  |         name: String, | ||||||
|  |         orig_val: Option<String>, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     impl Drop for EnvGuard { | ||||||
|  |         fn drop(&mut self) { | ||||||
|  |             if let Some(val) = self.orig_val.take() { | ||||||
|  |                 env::set_var(&self.name, val); | ||||||
|  |             } else { | ||||||
|  |                 env::remove_var(&self.name); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user