<div align="center">

# AntiSSRF-rs

[![Crates.io](https://img.shields.io/crates/v/antissrf)](https://crates.io/crates/antissrf)
[![Docs.rs](https://docs.rs/antissrf/badge.svg)](https://docs.rs/antissrf)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Rust](https://img.shields.io/badge/rust-2024%20edition-orange.svg)](https://blog.rust-lang.org/)

**Rust implementation of [Microsoft AntiSSRF](https://microsoft.github.io/AntiSSRF/) — a security library for preventing Server-Side Request Forgery (SSRF) attacks.**

</div>

---

## Overview

AntiSSRF-rs is a Rust version of Microsoft's AntiSSRF library, designed to prevent **Server-Side Request Forgery (SSRF)** attacks by validating every outbound network request against a configurable security policy. It integrates seamlessly with [`reqwest`](https://docs.rs/reqwest) and [`reqwest-middleware`](https://docs.rs/reqwest-middleware) to provide DNS-level IP blocking, header validation, protocol enforcement, and redirect-chain re-validation.

## Features

| Feature | Description |
|---------|-------------|
| **IP Blocking** | Block internal/sensitive IP addresses (localhost, private networks, cloud metadata services) using CIDR ranges |
| **IPv6 Normalization** | Automatically maps IPv4 to IPv6-mapped addresses (`::ffff:`) for consistent CIDR matching |
| **Header Enforcement** | Require specific headers (e.g., `X-Forwarded-For`) and deny dangerous headers |
| **Protocol Control** | Reject plaintext HTTP to untrusted endpoints |
| **Redirect Validation** | Re-validate every hop in a redirect chain against the same policy |
| **Domain Validation** | Check URLs against trusted domains (with subdomain support) and Azure service endpoints |
| **Immutable Policy** | Policy locks after first use to prevent runtime tampering |
| **Optional Networking** | Core library has zero network dependencies; `reqwest-integration` feature adds HTTP client support |

## Quick Start

#### 1. Add to `Cargo.toml`

```toml
[dependencies]
antissrf = "0.1.1"
```

Or with explicit feature control:

```toml
[dependencies]
# Core only (no HTTP client dependencies)
antissrf = { version = "0.1.1", default-features = false }

# Full reqwest integration (default)
antissrf = { version = "0.1.1", features = ["reqwest-integration"] }
```

#### 2. Basic Policy

```rust
use antissrf::{AntiSSRFPolicy, PolicyConfigOptions};

// Block all known dangerous IPs (IMDS, WireServer, private networks, etc.)
let policy = AntiSSRFPolicy::new(PolicyConfigOptions::ExternalOnlyLatest);
```

#### 3. With reqwest (DNS-level IP validation)

```rust
use antissrf::{AntiSSRFPolicy, PolicyConfigOptions};
use antissrf::network::reqwest_integration::AntiSSRFClientBuilder;

let policy = AntiSSRFPolicy::new(PolicyConfigOptions::ExternalOnlyLatest);
let client = AntiSSRFClientBuilder::new(policy)
    .timeout(std::time::Duration::from_secs(30))
    .build()
    .expect("Client should build");
```

#### 4. With reqwest_middleware (header + protocol validation)

```rust
use antissrf::{AntiSSRFPolicy, PolicyConfigOptions};
use antissrf::network::reqwest_integration::AntiSSRFMiddleware;
use reqwest_middleware::ClientBuilder;

let policy = AntiSSRFPolicy::new(PolicyConfigOptions::ExternalOnlyLatest);
let middleware = AntiSSRFMiddleware::new(policy);

let client = ClientBuilder::new(reqwest::Client::new())
    .with(middleware)
    .build();
```

#### 5. Fine-Grained Policy Configuration

```rust
use antissrf::{AntiSSRFPolicy, PolicyConfigOptions, AntiSSRFError};

let mut policy = AntiSSRFPolicy::new(PolicyConfigOptions::ExternalOnlyLatest);

// Allowlist takes precedence over denylist
policy.add_allowed_addresses(&["10.0.0.0/24"])?;

// Require specific headers
policy.add_required_headers(&["x-request-id"])?;

// Deny dangerous headers
policy.add_denied_headers(&["x-forwarded-host", "x-http-host-override"])?;

// Block plaintext HTTP
policy.set_allow_plaintext_http(false)?;

// Validate an outgoing request
let mut headers = vec![
    ("x-request-id".to_string(), "abc123".to_string()),
];
let allowed = policy.validate_request("https:", &mut headers)?;
assert!(allowed);
```

#### 6. Domain Validation

```rust
use antissrf::uri_validator;

// Check if URL is in a trusted domain (supports subdomains)
assert!(uri_validator::in_domain("https://api.example.com/path", &["example.com"]));

// Azure Key Vault domain validation
assert!(uri_validator::in_azure_key_vault_domain("https://myvault.vault.azure.net"));

// Rejects hostnames containing '--' (Azure naming restriction)
assert!(!uri_validator::in_azure_key_vault_domain("https://my--vault.vault.azure.net"));
```

## PolicyConfigOptions

| Option | Behavior |
|--------|----------|
| `None` | No restrictions — allow all requests |
| `InternalOnly` | Block external IPs (deny all unspecified) |
| `ExternalOnlyV1` | Block IMDS + WireServer + special ranges (v1 blocklist) |
| `ExternalOnlyLatest` | Same as V1 — alias for the latest recommended blocklist |

## Modules

| Module | Purpose |
|--------|---------|
| [`error`](https://docs.rs/antissrf/latest/antissrf/error/) | Error types (`AntiSSRFError`) |
| [`cidr`](https://docs.rs/antissrf/latest/antissrf/cidr/) | CIDR block parsing and IP containment |
| [`ip_address_ranges`](https://docs.rs/antissrf/latest/antissrf/ip_address_ranges/) | Predefined special IP ranges (IMDS, WireServer, loopback, etc.) |
| [`domains`](https://docs.rs/antissrf/latest/antissrf/domains/) | Azure service domain lists (Key Vault, Storage) |
| [`uri_validator`](https://docs.rs/antissrf/latest/antissrf/uri_validator/) | URL and domain validation utilities |
| [`policy`](https://docs.rs/antissrf/latest/antissrf/policy/) | Policy configuration and runtime enforcement |
| [`network`](https://docs.rs/antissrf/latest/antissrf/network/) | reqwest integration (resolver + middleware + client builder) |

## Running Tests

```bash
# Core tests (no network dependencies)
cargo test --lib

# With reqwest integration (default features)
cargo test --features reqwest-integration

# All tests with output
cargo test --features reqwest-integration -- --nocapture

# Documentation tests
cargo test --doc
```

## Security Design

- **Evaluation Order**: Allowlist → `deny_all_unspecified_ips` → Denylist. An allowlisted IP is always permitted, even if it also appears in a denylist.
- **IPv6 Normalization**: All IPv4 addresses are mapped to `::ffff:<ipv4>` before CIDR checks. IPv4 `/24` becomes IPv6-mapped `/120` (add 96 to prefix length).
- **Case-Insensitive Headers**: Header names are matched case-insensitively; header values are matched case-sensitively.
- **Edit Lock**: Once a policy is used for validation, it becomes immutable to prevent runtime tampering.
- **Redirect Re-Validation**: Every redirect hop is re-validated against the same policy, preventing open-redirect bypasses.

## License

MIT — See [LICENSE](LICENSE) for details.

## References

- [Microsoft AntiSSRF Documentation](https://microsoft.github.io/AntiSSRF/)
- [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
- [CWE-918: Server-Side Request Forgery](https://cwe.mitre.org/data/definitions/918.html)


