---
title: TLS Clients Must Validate Server Certificates
impact: CRITICAL
impactDescription: prevents Man-in-the-Middle (MITM) attacks by ensuring the server identity is authentic
tags: tls, certificates, validation, mitm, security, php
---

## TLS Clients Must Validate Server Certificates

Disabling certificate validation (often done to "skip" errors in development) completely negates the security of TLS. It allows an attacker to intercept, read, and modify your traffic by simply presenting any certificate, including a self-signed one.

**Incorrect (disabled validation):**

```php
// 1. cURL - DANGEROUS
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.example.com");
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // VULNERABLE
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);     // VULNERABLE

// 2. Stream Context - DANGEROUS
$context = stream_context_create([
    'ssl' => [
        'verify_peer' => false, // VULNERABLE
        'verify_peer_name' => false, // VULNERABLE
    ]
]);
file_get_contents("https://api.example.com", false, $context);

// 3. Guzzle - DANGEROUS
$client->get('https://api.example.com', ['verify' => false]);
```

**Correct (enforced validation):**

```php
// 1. cURL - Enabled by default (Ensure CA bundle is present)
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

// 2. Using an Internal CA for private services
curl_setopt($ch, CURLOPT_CAINFO, "/path/to/internal-ca.pem");

// 3. Guzzle (Recommended)
$client->get('https://api.example.com', [
    'verify' => '/path/to/ca-bundle.crt' // Or true to use system default
]);
```

**Common Pitfalls:**
- "Fixing" local development errors by setting `verify => false`. Instead, download the latest [cacert.pem](https://curl.se/docs/caextract.html) and update your `php.ini` (`curl.cainfo` and `openssl.cafile`).
- Using outdated OS images that have expired Root CAs.

**Tools:** `curl_error()`, `openssl_get_cert_locations()`, `SSLyze`, `OWASP ZAP`
