use reqwest;
use tokio;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
// Vulnerable: Accept invalid certificates
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true) // DANGEROUS
.build()?;
let response = client
.get("https://api.example.com/data")
.send()
.await?;
println!("Response: {}", response.text().await?);
Ok(())
}
// Vulnerable: Global client with disabled verification
lazy_static! {
static ref HTTP_CLIENT: reqwest::Client = reqwest::Client::builder()
.danger_accept_invalid_certs(true) // All requests vulnerable
.build()
.unwrap();
}
pub async fn api_call(url: &str) -> Result {
// Uses insecure global client
let response = HTTP_CLIENT.get(url).send().await?;
response.text().await
}
pub async fn webhook_call(url: &str, data: &str) -> Result<(), reqwest::Error> {
// Vulnerable: Ad-hoc client with disabled verification
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()?;
client
.post(url)
.body(data.to_string())
.send()
.await?;
Ok(())
}
use reqwest;
use std::sync::Arc;
use tokio;
use rustls::{Certificate, ClientConfig, RootCertStore};
use webpki_roots;
#[tokio::main]
async fn main() -> Result<(), Box> {
// Secure: Use default certificate verification
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()?;
let response = client
.get("https://api.example.com/data")
.send()
.await?;
println!("Response: {}", response.text().await?);
Ok(())
}
// Secure: Global client with proper TLS configuration
fn create_secure_client() -> Result {
reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(30))
.user_agent("SecureApp/1.0")
// Certificate verification enabled by default
.build()
}
lazy_static! {
static ref HTTP_CLIENT: reqwest::Client = create_secure_client().unwrap();
}
pub async fn api_call(url: &str) -> Result> {
// Validate URL scheme
let parsed_url = url::Url::parse(url)?;
if parsed_url.scheme() != "https" {
return Err("Only HTTPS URLs are allowed".into());
}
// Use secure global client
let response = HTTP_CLIENT
.get(url)
.timeout(std::time::Duration::from_secs(10))
.send()
.await?;
if !response.status().is_success() {
return Err(format!("HTTP error: {}", response.status()).into());
}
Ok(response.text().await?)
}
pub async fn webhook_call(url: &str, data: &str) -> Result<(), Box> {
// Validate URL
let parsed_url = url::Url::parse(url)?;
if parsed_url.scheme() != "https" {
return Err("Only HTTPS URLs are allowed for webhooks".into());
}
// Allowlist of permitted webhook hosts
let allowed_hosts = vec![
"webhook.trusted.com",
"api.partner.com",
"notify.secure.com",
];
if let Some(host) = parsed_url.host_str() {
if !allowed_hosts.contains(&host) {
return Err("Webhook host not in allowlist".into());
}
} else {
return Err("Invalid webhook URL".into());
}
// Secure webhook call
let response = HTTP_CLIENT
.post(url)
.header("Content-Type", "application/json")
.body(data.to_string())
.timeout(std::time::Duration::from_secs(15))
.send()
.await?;
if !response.status().is_success() {
return Err(format!("Webhook failed: {}", response.status()).into());
}
Ok(())
}
// Advanced: Custom TLS configuration with certificate pinning
pub fn create_pinned_client(expected_cert_der: &[u8]) -> Result> {
let mut root_store = RootCertStore::empty();
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(
|ta| {
rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
ta.subject,
ta.spki,
ta.name_constraints,
)
},
));
// Add pinned certificate
let pinned_cert = Certificate(expected_cert_der.to_vec());
let config = ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store)
.with_no_client_auth();
let client = reqwest::Client::builder()
.use_preconfigured_tls(config)
.timeout(std::time::Duration::from_secs(30))
.build()?;
Ok(client)
}
// Secure configuration with custom CA
pub fn create_client_with_custom_ca(ca_cert_pem: &str) -> Result> {
let cert = reqwest::Certificate::from_pem(ca_cert_pem.as_bytes())?;
let client = reqwest::Client::builder()
.add_root_certificate(cert)
.timeout(std::time::Duration::from_secs(30))
.build()?;
Ok(client)
}
// Error handling for certificate issues
pub async fn secure_api_call_with_retry(url: &str) -> Result> {
let mut attempts = 0;
let max_attempts = 3;
while attempts < max_attempts {
match api_call(url).await {
Ok(response) => return Ok(response),
Err(e) => {
if e.to_string().contains("certificate") {
// Don't retry certificate errors
return Err(e);
}
attempts += 1;
if attempts >= max_attempts {
return Err(e);
}
// Exponential backoff
tokio::time::sleep(std::time::Duration::from_millis(100 * 2_u64.pow(attempts))).await;
}
}
}
Err("Max retries exceeded".into())
}
// Configuration struct for secure HTTP clients
#[derive(Debug, Clone)]
pub struct SecureHttpConfig {
pub timeout: std::time::Duration,
pub user_agent: String,
pub allowed_hosts: Vec,
pub require_https: bool,
}
impl Default for SecureHttpConfig {
fn default() -> Self {
Self {
timeout: std::time::Duration::from_secs(30),
user_agent: "SecureApp/1.0".to_string(),
allowed_hosts: vec![],
require_https: true,
}
}
}
pub struct SecureHttpClient {
client: reqwest::Client,
config: SecureHttpConfig,
}
impl SecureHttpClient {
pub fn new(config: SecureHttpConfig) -> Result {
let client = reqwest::Client::builder()
.timeout(config.timeout)
.user_agent(&config.user_agent)
.build()?;
Ok(Self { client, config })
}
pub async fn get(&self, url: &str) -> Result> {
self.validate_url(url)?;
let response = self.client.get(url).send().await?;
if !response.status().is_success() {
return Err(format!("HTTP error: {}", response.status()).into());
}
Ok(response.text().await?)
}
fn validate_url(&self, url: &str) -> Result<(), Box> {
let parsed_url = url::Url::parse(url)?;
if self.config.require_https && parsed_url.scheme() != "https" {
return Err("Only HTTPS URLs are allowed".into());
}
if !self.config.allowed_hosts.is_empty() {
if let Some(host) = parsed_url.host_str() {
if !self.config.allowed_hosts.contains(&host.to_string()) {
return Err("Host not in allowlist".into());
}
}
}
Ok(())
}
}