حل مشاكل SSL/TLS عند استخدام الوكيل: دليل شامل
أخطاء SSL/TLS هي واحدة من أكثر المشاكل شيوعاً عند العمل عبر خوادم الوكيل. يؤدي الإعداد غير الصحيح إلى فشل الاتصال وتسرب البيانات واستحالة تحليل الموارد المحمية. في هذا الدليل سنحلل أسباب الأخطاء وطرق حلها مع أمثلة محددة على الأكواد.
كيفية عمل SSL/TLS عبر الوكيل
قبل حل المشاكل، من المهم فهم آلية عمل الاتصالات المحمية عبر الوكيل. هناك نهجان مختلفان بشكل أساسي لتوكيل حركة HTTPS، وكل منهما له خصائصه ومزاياه ونقاط الفشل المحتملة.
النفق الشفاف (CONNECT)
عند استخدام طريقة CONNECT، يقوم خادم الوكيل بإنشاء نفق TCP بين العميل والخادم الهدف. الوكيل لا يرى محتوى حركة المرور - فهو يقوم ببساطة بنقل البايتات المشفرة في كلا الاتجاهين. هذه هي الطريقة الأكثر أماناً والأكثر شيوعاً للعمل مع HTTPS عبر الوكيل.
تبدو عملية إنشاء الاتصال كما يلي: يرسل العميل طلب CONNECT إلى الوكيل مع تحديد الخادم الهدف والمنفذ. يقوم الوكيل بإنشاء اتصال TCP مع الخادم الهدف ويعيد للعميل رد 200 Connection Established. بعد ذلك، يقوم العميل بإجراء مصافحة TLS مباشرة مع الخادم الهدف عبر النفق المُنشأ.
# مخطط نفق CONNECT
العميل → الوكيل: CONNECT example.com:443 HTTP/1.1
الوكيل → الخادم: [اتصال TCP على المنفذ 443]
الوكيل → العميل: HTTP/1.1 200 Connection Established
العميل ↔ الخادم: [مصافحة TLS عبر النفق]
العميل ↔ الخادم: [حركة مرور مشفرة]
توكيل MITM (الاعتراض)
يستخدم بعض الوكلاء (خاصة الوكلاء الموجودة في الشركات) تقنية Man-in-the-Middle: يقومون بإنهاء اتصال TLS على جانبهم وفك تشفير حركة المرور ثم إنشاء اتصال TLS جديد مع الخادم الهدف. لهذا الغرض، يقوم الوكيل بإنشاء شهادة خاصة به لكل نطاق، موقعة بشهادة جذر الوكيل.
يسمح هذا النهج بفحص وتصفية حركة HTTPS، لكنه يتطلب تثبيت شهادة جذر الوكيل في مخزن الشهادات الموثوقة للعميل. هنا بالذات تنشأ معظم مشاكل SSL/TLS عند العمل عبر الشبكات الموجودة في الشركات.
الأخطاء الشائعة وأسبابها
دعنا نفكر في الأخطاء الأكثر شيوعاً في SSL/TLS عند العمل عبر الوكيل. فهم سبب كل خطأ هو المفتاح للحل السريع للمشكلة.
SSL: CERTIFICATE_VERIFY_FAILED
يعني هذا الخطأ أن العميل لا يمكنه التحقق من صحة شهادة الخادم. قد تكون هناك عدة أسباب: يستخدم الوكيل فحص MITM مع عدم تثبيت شهادة جذر، أو انتهت صلاحية شهادة الخادم الهدف أو أنها موقعة ذاتياً، أو قد تكون هناك شهادة وسيطة مفقودة في السلسلة.
# Python - رسالة خطأ نموذجية
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate
# Node.js
Error: unable to verify the first certificate
# cURL
curl: (60) SSL certificate problem: unable to get local issuer certificate
SSL: WRONG_VERSION_NUMBER
يحدث الخطأ عندما يحاول العميل إنشاء اتصال TLS لكنه يتلقى رداً بصيغة غير متوقعة. في أغلب الأحيان يحدث هذا عندما لا يدعم الوكيل طريقة CONNECT ويحاول معالجة طلب HTTPS كـ HTTP عادي. قد يكون السبب أيضاً منفذاً غير صحيح أو بروتوكولاً.
TLSV1_ALERT_PROTOCOL_VERSION
يرفض الخادم الاتصال بسبب إصدار بروتوكول TLS غير متوافق. تعطل الخوادم الحديثة إصدارات TLS القديمة 1.0 و 1.1 لأسباب أمنية. إذا تم إعداد الوكيل أو العميل لاستخدام إصدارات قديمة، سيتم رفض الاتصال.
SSLV3_ALERT_HANDSHAKE_FAILURE
لم يتمكن العميل والخادم من الاتفاق على معاملات التشفير. قد يحدث هذا بسبب عدم وجود cipher suites مشتركة أو مشاكل في SNI (Server Name Indication) أو عدم توافق مكتبات التشفير.
| الخطأ | السبب المحتمل | الحل |
|---|---|---|
CERTIFICATE_VERIFY_FAILED |
وكيل MITM، شهادة منتهية الصلاحية | تثبيت شهادة جذر الوكيل |
WRONG_VERSION_NUMBER |
HTTP بدلاً من HTTPS، عدم دعم CONNECT | التحقق من إعدادات الوكيل والبروتوكول |
PROTOCOL_VERSION |
إصدار TLS قديم | تحديث الحد الأدنى لإصدار TLS |
HANDSHAKE_FAILURE |
cipher suites غير متوافقة | إعداد قائمة الأصفار |
مشاكل الشهادات
الشهادات هي المصدر الأكثر شيوعاً للمشاكل عند العمل عبر الوكيل. دعنا نفكر في السيناريوهات الرئيسية وطرق حلها.
تثبيت شهادة جذر الوكيل
إذا كان الوكيل يستخدم فحص MITM، فمن الضروري إضافة شهادة جذره إلى مخزن الشهادات الموثوقة. تعتمد العملية على نظام التشغيل ولغة البرمجة المستخدمة.
# Linux: إضافة الشهادة إلى مخزن النظام
sudo cp proxy-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
# macOS: إضافة إلى Keychain
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain proxy-ca.crt
# Windows PowerShell (من المسؤول)
Import-Certificate -FilePath "proxy-ca.crt" `
-CertStoreLocation Cert:\LocalMachine\Root
تحديد الشهادة في الكود
أحياناً يكون من الأنسب تحديد مسار الشهادة مباشرة في الكود، خاصة عند العمل في الحاويات أو على الخوادم بدون وصول جذر. يسمح هذا بتجنب تعديل إعدادات النظام.
# Python: تحديد ملف شهادة مخصص
import requests
# الطريقة 1: تحديد مسار ملف الشهادة
response = requests.get(
'https://example.com',
proxies={'https': 'http://proxy:8080'},
verify='/path/to/proxy-ca.crt'
)
# الطريقة 2: دمج مع شهادات النظام
import certifi
import ssl
# إنشاء ملف شهادات مدمج
with open('combined-ca.crt', 'w') as combined:
with open(certifi.where()) as system_certs:
combined.write(system_certs.read())
with open('proxy-ca.crt') as proxy_cert:
combined.write(proxy_cert.read())
response = requests.get(url, verify='combined-ca.crt')
تعطيل التحقق من الشهادات
في بعض الحالات (فقط للاختبار والتصحيح!) قد يكون من الضروري تعطيل التحقق من الشهادات. هذا غير آمن ولا يجب استخدامه في الإنتاج، لأنه يجعل الاتصال عرضة لهجمات MITM.
⚠️ تحذير: تعطيل التحقق من الشهادات يجعل الاتصال عرضة للاعتراض. استخدم فقط للتصحيح في بيئة معزولة، أبداً في الإنتاج!
# Python - فقط للتصحيح!
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
response = requests.get(
'https://example.com',
proxies={'https': 'http://proxy:8080'},
verify=False # غير آمن!
)
إعداد أنفاق CONNECT
الإعداد الصحيح لنفق CONNECT هو المفتاح للعمل المستقر مع HTTPS عبر الوكيل. دعنا نفكر في خصائص الإعداد لسيناريوهات الاستخدام المختلفة.
التحقق من دعم CONNECT
لا تدعم جميع الوكلاء طريقة CONNECT. قد يتم إعداد وكلاء HTTP فقط لتوكيل حركة HTTP. قبل الاستخدام، تأكد من أن الوكيل يدعم نفق HTTPS.
# التحقق من دعم CONNECT عبر netcat/telnet
echo -e "CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n" | \
nc proxy.example.com 8080
# الرد المتوقع عند النجاح:
# HTTP/1.1 200 Connection Established
# الردود المحتملة عند الخطأ:
# HTTP/1.1 403 Forbidden - CONNECT محظور
# HTTP/1.1 405 Method Not Allowed - الطريقة غير مدعومة
المصادقة في طلبات CONNECT
عند استخدام وكيل مع مصادقة، من المهم نقل بيانات الاعتماد بشكل صحيح. يجب أن يكون رأس Proxy-Authorization موجوداً في طلب CONNECT.
# Python: المصادقة عبر URL
import requests
proxy_url = 'http://username:password@proxy.example.com:8080'
response = requests.get(
'https://target.com',
proxies={'https': proxy_url}
)
# Python: المصادقة عبر الرؤوس (للحالات المعقدة)
import base64
credentials = base64.b64encode(b'username:password').decode('ascii')
session = requests.Session()
session.headers['Proxy-Authorization'] = f'Basic {credentials}'
session.proxies = {'https': 'http://proxy.example.com:8080'}
response = session.get('https://target.com')
العمل مع وكلاء SOCKS5
تعمل وكلاء SOCKS5 على مستوى أقل ولا تتطلب معالجة خاصة لـ HTTPS. فهي ببساطة تنفق اتصالات TCP، مما يجعلها مثالية للعمل مع أي بروتوكول.
# Python مع PySocks
import requests
proxies = {
'http': 'socks5h://user:pass@proxy:1080',
'https': 'socks5h://user:pass@proxy:1080'
}
# socks5h - حل DNS عبر الوكيل
# socks5 - حل DNS محلي
response = requests.get('https://example.com', proxies=proxies)
الحلول لمختلف لغات البرمجة
لكل لغة ومكتبة خصائصها الخاصة في العمل مع SSL/TLS عبر الوكيل. دعنا نفكر في الخيارات الأكثر شيوعاً مع أمثلة كود كاملة.
Python (requests + urllib3)
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context
import ssl
class TLSAdapter(HTTPAdapter):
"""محول مع معاملات TLS قابلة للتخصيص"""
def __init__(self, ssl_context=None, **kwargs):
self.ssl_context = ssl_context
super().__init__(**kwargs)
def init_poolmanager(self, *args, **kwargs):
if self.ssl_context:
kwargs['ssl_context'] = self.ssl_context
return super().init_poolmanager(*args, **kwargs)
# إنشاء سياق بإعدادات حديثة
ctx = create_urllib3_context()
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
ctx.set_ciphers('ECDHE+AESGCM:DHE+AESGCM:ECDHE+CHACHA20')
# تحميل شهادة إضافية
ctx.load_verify_locations('proxy-ca.crt')
session = requests.Session()
session.mount('https://', TLSAdapter(ssl_context=ctx))
session.proxies = {
'http': 'http://proxy:8080',
'https': 'http://proxy:8080'
}
response = session.get('https://example.com')
print(response.status_code)
Node.js (axios + https-proxy-agent)
const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');
const fs = require('fs');
const https = require('https');
// تحميل شهادة CA إضافية
const customCA = fs.readFileSync('proxy-ca.crt');
const agent = new HttpsProxyAgent({
host: 'proxy.example.com',
port: 8080,
auth: 'username:password',
ca: customCA,
rejectUnauthorized: true // لا تعطل في الإنتاج!
});
const client = axios.create({
httpsAgent: agent,
proxy: false // مهم: تعطيل الوكيل المدمج في axios
});
async function fetchData() {
try {
const response = await client.get('https://example.com');
console.log(response.data);
} catch (error) {
if (error.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') {
console.error('مشكلة في الشهادة:', error.message);
}
throw error;
}
}
fetchData();
Go (net/http)
package main
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"net/url"
"log"
)
func main() {
// تحميل شهادة إضافية
caCert, err := ioutil.ReadFile("proxy-ca.crt")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// إعداد TLS
tlsConfig := &tls.Config{
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
}
// إعداد الوكيل
proxyURL, _ := url.Parse("http://user:pass@proxy:8080")
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: tlsConfig,
}
client := &http.Client{Transport: transport}
resp, err := client.Get("https://example.com")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
log.Printf("الحالة: %s", resp.Status)
}
cURL (سطر الأوامر)
# طلب أساسي عبر الوكيل
curl -x http://proxy:8080 https://example.com
# مع المصادقة
curl -x http://proxy:8080 -U username:password https://example.com
# مع تحديد شهادة CA
curl -x http://proxy:8080 --cacert proxy-ca.crt https://example.com
# فرض استخدام TLS 1.2+
curl -x http://proxy:8080 --tlsv1.2 https://example.com
# تصحيح مصافحة TLS
curl -x http://proxy:8080 -v --trace-ascii - https://example.com 2>&1 | \
grep -E "(SSL|TLS|certificate)"
كشف وتجاوز فحص MITM
تستخدم بعض الشبكات (الشركات والشبكات العامة) فحص MITM شفاف لحركة HTTPS. قد يسبب هذا مشاكل مع certificate pinning وينتهك عمل التطبيقات التي تتوقع شهادات معينة.
كشف وكيل MITM
import ssl
import socket
def check_certificate_issuer(hostname, port=443):
"""يتحقق من جهة إصدار شهادة الموقع"""
context = ssl.create_default_context()
with socket.create_connection((hostname, port)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
issuer = dict(x[0] for x in cert['issuer'])
subject = dict(x[0] for x in cert['subject'])
print(f"الموضوع: {subject.get('commonName')}")
print(f"المُصدِر: {issuer.get('commonName')}")
print(f"المنظمة: {issuer.get('organizationName')}")
# وكلاء MITM الموجودة في الشركات المعروفة
mitm_indicators = [
'Zscaler', 'BlueCoat', 'Symantec', 'Fortinet',
'Palo Alto', 'McAfee', 'Cisco', 'Corporate'
]
issuer_str = str(issuer)
for indicator in mitm_indicators:
if indicator.lower() in issuer_str.lower():
print(f"⚠️ تم كشف وكيل MITM: {indicator}")
return True
return False
# التحقق
check_certificate_issuer('google.com')
العمل في ظروف MITM
إذا تم كشف وكيل MITM، هناك عدة استراتيجيات للعمل. يمكنك تثبيت شهادة جذر الوكيل (إذا كانت شبكة شركة موثوقة)، أو استخدام منافذ أو بروتوكولات بديلة، أو تطبيق VPN لتجاوز الفحص.
# الحصول على شهادة وكيل MITM للتثبيت
openssl s_client -connect example.com:443 -proxy proxy:8080 \
-showcerts 2>/dev/null | \
openssl x509 -outform PEM > mitm-cert.pem
# عرض معلومات الشهادة
openssl x509 -in mitm-cert.pem -text -noout | head -20
أدوات التشخيص
التشخيص الصحيح هو نصف الحل. دعنا نفكر في الأدوات والطرق لتصحيح اتصالات SSL/TLS عبر الوكيل.
OpenSSL للتشخيص
# التحقق من الاتصال عبر الوكيل
openssl s_client -connect example.com:443 \
-proxy proxy.example.com:8080 \
-servername example.com
# التحقق من سلسلة الشهادات
openssl s_client -connect example.com:443 \
-proxy proxy:8080 \
-showcerts 2>/dev/null | \
grep -E "(Certificate chain|s:|i:)"
# اختبار إصدار TLS محدد
openssl s_client -connect example.com:443 \
-proxy proxy:8080 \
-tls1_2
# التحقق من cipher suites المدعومة
openssl s_client -connect example.com:443 \
-proxy proxy:8080 \
-cipher 'ECDHE-RSA-AES256-GCM-SHA384'
سكريبت Python للتشخيص
import ssl
import socket
import requests
from urllib.parse import urlparse
def diagnose_ssl_proxy(target_url, proxy_url):
"""تشخيص شامل لـ SSL عبر الوكيل"""
print(f"🔍 التشخيص: {target_url}")
print(f"📡 الوكيل: {proxy_url}\n")
# 1. التحقق من توفر الوكيل
proxy = urlparse(proxy_url)
try:
sock = socket.create_connection(
(proxy.hostname, proxy.port),
timeout=10
)
sock.close()
print("✅ الوكيل متاح")
except Exception as e:
print(f"❌ الوكيل غير متاح: {e}")
return
# 2. التحقق من طريقة CONNECT
try:
response = requests.get(
target_url,
proxies={'https': proxy_url},
timeout=15
)
print(f"✅ اتصال HTTPS ناجح: {response.status_code}")
except requests.exceptions.SSLError as e:
print(f"❌ خطأ SSL: {e}")
# محاولة بدون التحقق من الشهادة للتشخيص
try:
response = requests.get(
target_url,
proxies={'https': proxy_url},
verify=False,
timeout=15
)
print("⚠️ الاتصال يعمل بدون التحقق من الشهادة")
print(" المشكلة على الأرجح في شهادة CA للوكيل")
except Exception as e2:
print(f"❌ لا يعمل حتى بدون التحقق: {e2}")
except Exception as e:
print(f"❌ خطأ الاتصال: {e}")
# 3. معلومات SSL
print(f"\n📋 إصدار OpenSSL: {ssl.OPENSSL_VERSION}")
print(f"📋 المسارات الموثوقة: {ssl.get_default_verify_paths()}")
# الاستخدام
diagnose_ssl_proxy(
'https://httpbin.org/ip',
'http://proxy:8080'
)
Wireshark للتحليل العميق
للحالات المعقدة، استخدم Wireshark مع فلتر على حركة TLS. يسمح هذا برؤية تفاصيل المصافحة وتحديد لحظة الفشل بدقة.
# مرشحات Wireshark لتحليل TLS
tls.handshake.type == 1 # Client Hello
tls.handshake.type == 2 # Server Hello
tls.handshake.type == 11 # Certificate
tls.alert_message # تنبيهات TLS (الأخطاء)
# التقاط حركة المرور عبر tcpdump
tcpdump -i eth0 -w ssl_debug.pcap \
'port 443 or port 8080' -c 1000
أفضل الممارسات الآمنة
سيساعدك اتباع أفضل الممارسات على تجنب معظم مشاكل SSL/TLS وضمان أمان بيانات عند العمل عبر الوكيل.
إعداد TLS
- استخدم الحد الأدنى TLS 1.2، ويفضل TLS 1.3
- قم بإعداد cipher suites حديثة (ECDHE و AES-GCM و ChaCha20)
- قم بتفعيل التحقق من الشهادات (لا تعطله أبداً في الإنتاج)
- قم بتحديث CA-bundle ومكتبات التشفير بانتظام
العمل مع الوكيل
- فضل وكلاء مع دعم CONNECT لحركة HTTPS
- استخدم SOCKS5 للنفق العام
- عند العمل مع وكلاء سكنية ضع في الاعتبار الخصائص المحتملة لشبكات مزودي الخدمة
- احفظ بيانات اعتماد الوكيل في متغيرات البيئة وليس في الكود
معالجة الأخطاء
import requests
from requests.exceptions import SSLError, ProxyError, ConnectionError
import time
def robust_request(url, proxy, max_retries=3):
"""طلب مقاوم للأخطاء عبر الوكيل"""
for attempt in range(max_retries):
try:
response = requests.get(
url,
proxies={'https': proxy},
timeout=30
)
return response
except SSLError as e:
print(f"خطأ SSL (محاولة {attempt + 1}): {e}")
# عادة لا تصحح أخطاء SSL بالإعادة
if 'CERTIFICATE_VERIFY_FAILED' in str(e):
raise # يتطلب إصلاح الإعدادات
except ProxyError as e:
print(f"خطأ الوكيل (محاولة {attempt + 1}): {e}")
time.sleep(2 ** attempt) # تأخير أسي
except ConnectionError as e:
print(f"خطأ الاتصال (محاولة {attempt + 1}): {e}")
time.sleep(2 ** attempt)
raise Exception(f"فشل تنفيذ الطلب بعد {max_retries} محاولات")
قائمة التحقق من حل المشاكل
عند حدوث أخطاء SSL/TLS، اتبع ترتيب التشخيص هذا:
- تحقق من توفر خادم الوكيل (ping و telnet)
- تأكد من دعم طريقة CONNECT
- تحقق من صحة بيانات المصادقة
- ادرس الشهادة عبر openssl s_client
- تحقق من وجود فحص MITM
- ثبت شهادات CA الضرورية
- حدّث إصدار TLS و cipher suites
💡 نصيحة: معظم مشاكل SSL/TLS عند العمل عبر الوكيل مرتبطة بالشهادات. ابدأ التشخيص بفحص سلسلة الشهادات وتأكد من وجود جميع الشهادات الوسيطة.
الخلاصة
مشاكل SSL/TLS عند العمل عبر الوكيل قابلة للحل من خلال نهج منظم للتشخيص. النقاط الرئيسية: فهم الفرق بين نفق CONNECT وتوكيل MITM والإعداد الصحيح للشهادات واستخدام إصدارات حديثة من بروتوكول TLS. معظم الأخطاء مرتبطة بغياب شهادات CA الضرورية أو عدم توافق معاملات التشفير.
لمهام التحليل والأتمتة التي تتطلب عمل HTTPS مستقر، يُنصح باستخدام وكلاء عالية الجودة مع دعم كامل لطريقة CONNECT - المزيد من الخيارات على proxycove.com.