تابع stream_socket_enable_crypto() در PHP
تابع stream_socket_enable_crypto() یکی از ابزارهای اصلی PHP برای روشن یا خاموش کردن رمزنگاری (TLS/SSL) روی یک stream/socket است. این تابع مخصوصاً زمانی کاربرد دارد که بخواهید روی یک کانکشن موجود، به صورت صریح handshake رمزنگاری را آغاز یا خاتمه دهید — برای مثال در پیادهسازی STARTTLS یا هنگام مدیریت یک سرور TLS.
سینتکس و پارامترها
سینتکس کلی تابع به صورت زیر است:
bool|int stream_socket_enable_crypto ( resource $stream , bool $enable [, int $crypto_type = STREAM_CRYPTO_METHOD_TLS_CLIENT [, resource $session_stream ]] )پارامترهای مهم:
- $stream: منبع stream یا socket که میخواهید روی آن رمزنگاری را فعال/غیرفعال کنید.
- $enable: true برای فعال کردن، false برای غیر فعال کردن رمزنگاری.
- $crypto_type: ثابتهایی مانند STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT، STREAM_CRYPTO_METHOD_TLSv1_3_SERVER و… که روش TLS/SSL و نقش را تعیین میکنند.
- $session_stream (اختیاری): در برخی پیادهسازیها برای بازاستفاده از session TLS کاربرد دارد.
مقادیر بازگشتی
- true: عملیات موفقیتآمیز بود و handshake کامل شد.
- false: خطا در هنگام شروع یا انجام handshake رخ داده است.
- 0: در حالت non-blocking، عملیات هنوز کامل نشده و باید دوباره فراخوانی شود.
ثابتهای رایج crypto_type
- STREAM_CRYPTO_METHOD_TLS_CLIENT / SERVER — انتخاب کلی TLS برای کلاینت یا سرور.
- STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT — مشخص کردن TLS 1.2.
- STREAM_CRYPTO_METHOD_TLSv1_3_SERVER — مشخص کردن TLS 1.3 (در نسخههای جدید PHP/OpenSSL).
مثال: فعالسازی TLS به صورت ساده (implicit TLS)
$ctx = stream_context_create([
'ssl' => [
'verify_peer' => true,
'cafile' => '/path/to/ca.pem',
'peer_name' => 'example.com',
]
]);
$fp = stream_socket_client('tcp://example.com:443', $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $ctx);
stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
fwrite($fp, "GET / HTTP/1.1rnHost: example.comrnConnection: closernrn");
echo stream_get_contents($fp);
fclose($fp);در این کد، ابتدا یک کانتکست SSL ساخته میشود که شامل CA و نام همتا (SNI) است. سپس کانکشن TCP برقرار شده و با فراخوانی stream_socket_enable_crypto()، handshake TLS انجام میشود. بعد از موفقیت، میتوان دادهی رمزنگاری شده رد و بدل کرد.
مثال: پیادهسازی STARTTLS (ارتقا از plaintext به TLS)
$fp = stream_socket_client('tcp://smtp.example.com:587', $errno, $errstr, 30);
fwrite($fp, "EHLO myhostrn");
// خواندن پاسخ سرور ...
fwrite($fp, "STARTTLSrn");
// خواندن پاسخ سرور مبنی بر آمادهبودن برای TLS ...
stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
// حالا کانکشن TLS شده و میتوان ادامه SMTP را رمزنگاریشده انجام داد.در این مثال، ابتدا یک اتصال SMTP عادی برقرار میکنیم. پس از ارسال دستور STARTTLS و پذیرش سرور، با stream_socket_enable_crypto() handshake آغاز شده و کانکشن به TLS ارتقا مییابد.
مثال سرور TLS با استفاده از stream_socket_server
$ctx = stream_context_create([
'ssl' => [
'local_cert' => '/path/to/server.pem', // باید شامل cert و private key باشد
'allow_self_signed' => true,
'verify_peer' => false
]
]);
$server = stream_socket_server('tcp://0.0.0.0:8443', $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $ctx);
$client = stream_socket_accept($server);
stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLS_SERVER);
// سپس خواندن/نوشتن روی $client رمزنگاریشده است.
در محیط تولید، نباید allow_self_signed=true و verify_peer=false را استفاده کنید؛ این فقط برای توسعه/تست مناسب است. فایل local_cert باید شامل کلید خصوصی و گواهی به فرمت PEM باشد.
حالت non-blocking و حلقه handshake
اگر socket در حالت non-blocking باشد، فراخوانی stream_socket_enable_crypto ممکن است مقدار 0 بازگرداند، به این معنا که handshake هنوز کامل نشده است. در این حالت باید با استفاده از stream_select و فراخوانی مکرر تابع منتظر تکمیل handshake بمانید.
stream_set_blocking($fp, false);
while (true) {
$ret = stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
if ($ret === true) break;
if ($ret === false) { /* خطا */ break; }
// ret === 0 -> منتظر خواندن/نوشتن بمانید
$r = [$fp]; $w = [$fp]; $e = null;
stream_select($r, $w, $e, 5);
}این الگو باعث میشود در حالت غیرمسدود، handshake به شکل تدریجی و ایمن تکمیل شود.
تنظیمات کانتکست و تایید گواهی
برای تایید گواهیها و جلوگیری از خطاهای certificate verify failed، از گزینههای کانتکست استفاده کنید:
- verify_peer => true
- cafile یا capath => مسیر گواهیهای CA
- peer_name => نام میزبان برای بررسی SNI
- local_cert => گواهی و کلید برای سرور
بدون تنظیم صحیح این گزینهها، ممکن است هنگام فعالسازی crypto با خطاهای OpenSSL مواجه شوید.
خطاها و مشکلات رایج
- خطای certificate verify failed: معمولاً به دلیل عدم وجود CA مناسب یا نام همتا نادرست. راهحل: تنظیم cafile و peer_name.
- بازگشت 0 در حالت non-blocking: نیاز به حلقه و stream_select دارید.
- عدم امکان فعالسازی مجدد یا مخلوط کردن دادههای رمزنگاریشده و غیررمزنگاری: قبل از فعالسازی مطمئن شوید که لایه بالاتر (مثلاً پروتکل) آماده است.
- نسخههای مختلف PHP/OpenSSL: برخی ثابتها یا رفتارها بین نسخهها متفاوت است؛ همواره مستندات PHP و نسخه OpenSSL سرور را چک کنید.
نکات حرفهای و بهترین روشها
- همیشه verify_peer=true و ارائه CA معتبر در محیط تولید.
- استفاده از TLS 1.2 یا 1.3 و حذف پروتکلهای قدیمی با تعیین crypto_type مناسب.
- برای سرویسهای عمومی، SNI را با peer_name ست کنید تا گواهی صحیح انتخاب شود.
- اجتناب از allow_self_signed=true در تولید؛ برای تست از آن استفاده کنید اما بعداً با گواهی معتبر جایگزین کنید.
- در حالت non-blocking handshake را با stream_select و پردازش رویدادها مدیریت کنید تا کارایی و پایداری حفظ شود.
خلاصه
تابع stream_socket_enable_crypto() ابزاری قدرتمند برای مدیریت TLS/SSL روی streamها در PHP است. با تنظیم درست کانتکست، مدیریت حالتهای blocking/non-blocking و توجه به نسخههای PHP/OpenSSL میتوان پیادهسازیهای امن و پایداری برای هر دو سمت کلاینت و سرور ساخت.
آیا این مطلب برای شما مفید بود ؟



