ویژگی تصویر

تابع stream_socket_enable_crypto() در PHP

  /  PHP   /  تابع stream_socket_enable_crypto() در PHP
بنر تبلیغاتی الف
آموزش 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 می‌توان پیاده‌سازی‌های امن و پایداری برای هر دو سمت کلاینت و سرور ساخت.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: