Skip to content

طبقة أمان TLS

مقدمة

توفر طبقة TLS في Xray-core تشفير TLS قياسي مع تخصيص واسع. تدعم كلاً من crypto/tls القياسي في Go و uTLS (refraction-networking/utls) لبصمة TLS، وتوليد الشهادات الديناميكي من CA، وتدبيس OCSP، وتثبيت الشهادات، واستئناف الجلسة، و ECH (مرحبا العميل المشفر)، ومجموعات التشفير المخصصة، وتفضيلات المنحنيات، وتسجيل المفاتيح. TLS ليس نقلاً بحد ذاته بل هو طبقة أمان تغلف أي نقل.

الملفات الرئيسية

  • transport/internet/tls/tls.go -- الأنواع الأساسية: Conn، UConn، سجل البصمات
  • transport/internet/tls/config.go -- Config.GetTLSConfig()، إدارة الشهادات، الخيارات
  • transport/internet/tls/pin.go -- دوال تجزئة الشهادات للتثبيت
  • transport/internet/tls/ech.go -- دعم ECH (مرحبا العميل المشفر)
  • transport/internet/tls/grpc.go -- مساعدات TLS الخاصة بـ gRPC
  • transport/internet/tls/unsafe.go -- وصول غير آمن لمجمع الشهادات

أنواع الاتصال

Conn (TLS القياسي)

tls.Conn (tls/tls.go:26-28) يغلف crypto/tls.Conn من Go:

go
type Conn struct {
    *tls.Conn
}

الميزات:

  • مهلة إغلاق رشيق: مهلة 250 مللي ثانية لإشعار إغلاق TLS، وبعدها يتم إغلاق الاتصال الأساسي قسريًا (tls.go:30-38)
  • كتابة MultiBuffer: تضغط وتكتب المخازن المتعددة بكفاءة (tls.go:40-45)
  • NegotiatedProtocol: تُبلغ عن نتيجة ALPN (tls.go:54-57)

UConn (بصمة uTLS)

tls.UConn (tls/tls.go:71-73) يغلف utls.UConn لبصمة TLS:

go
type UConn struct {
    *utls.UConn
}

طريقة WebsocketHandshakeContext (tls.go:94-117) تفرض ALPN http/1.1 لاتصالات WebSocket باستخدام uTLS:

go
func (c *UConn) WebsocketHandshakeContext(ctx context.Context) error {
    c.BuildHandshakeState()
    // Find ALPNExtension and override to ["http/1.1"]
    // Rebuild handshake state
    return c.HandshakeContext(ctx)
}

الواجهة

كلا النوعين ينفذان واجهة Interface (tls.go:15-21):

go
type Interface interface {
    net.Conn
    HandshakeContext(ctx context.Context) error
    VerifyHostname(host string) error
    HandshakeContextServerName(ctx context.Context) string
    NegotiatedProtocol() string
}

بصمة TLS

سجل البصمات

ثلاث مستويات من البصمات معرّفة (tls/tls.go:185-257):

PresetFingerprints (خيارات واجهة المستخدم الموصى بها):

الاسممعرف uTLS
chromeHelloChrome_Auto
firefoxHelloFirefox_Auto
safariHelloSafari_Auto
iosHelloIOS_Auto
androidHelloAndroid_11_OkHttp
edgeHelloEdge_Auto
360Hello360_Auto
qqHelloQQ_Auto
randomيُختار عشوائيًا عند بدء التشغيل
randomizedHelloRandomizedALPN مع أوزان مخصصة
randomizednoalpnHelloRandomizedNoALPN مع أوزان مخصصة

ModernFingerprints: إصدارات متصفحات محددة (Chrome 83-131، Firefox 99-120، إلخ.)

OtherFingerprints: إصدارات قديمة وبصمات خاصة.

اختيار البصمة العشوائية

عند بدء التشغيل (tls.go:145-167)، يُعيّن "random" إلى بصمة حديثة مختارة عشوائيًا:

go
func init() {
    bigInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(ModernFingerprints))))
    stopAt := int(bigInt.Int64())
    // Select the stopAt-th entry from ModernFingerprints
    PresetFingerprints["random"] = v
}

خيار "randomized" يستخدم التعشية المدمجة في uTLS مع أوزان مخصصة:

  • يفرض TLS 1.3 (TLSVersMax_Set_VersionTLS13 = 1)
  • يعطل P256 لمشاركة المفتاح الأولى (FirstKeyShare_Set_CurveP256 = 0)

GetFingerprint

GetFingerprint (tls.go:169-183) يبحث عن البصمات عبر الخرائط الثلاث:

go
func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) {
    if name == "" { return &utls.HelloChrome_Auto }
    if fingerprint = PresetFingerprints[name]; fingerprint != nil { return }
    if fingerprint = ModernFingerprints[name]; fingerprint != nil { return }
    if fingerprint = OtherFingerprints[name]; fingerprint != nil { return }
    return  // nil = standard Go TLS
}

الاسم الفارغ يعود إلى Chrome Auto كقيمة افتراضية. إذا لم يتم العثور على تطابق، يُعاد nil، مما يشير إلى المستدعي باستخدام TLS القياسي في Go.

بناء العميل/الخادم

العميل

go
// tls.go:60-63 -- Standard Go TLS
func Client(c net.Conn, config *tls.Config) net.Conn {
    tlsConn := tls.Client(c, config)
    return &Conn{Conn: tlsConn}
}

// tls.go:124-127 -- uTLS fingerprinted
func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) net.Conn {
    utlsConn := utls.UClient(c, copyConfig(config), *fingerprint)
    return &UConn{UConn: utlsConn}
}

الخادم

go
// tls.go:66-69
func Server(c net.Conn, config *tls.Config) net.Conn {
    tlsConn := tls.Server(c, config)
    return &Conn{Conn: tlsConn}
}

نسخ الإعدادات لـ uTLS

copyConfig (tls.go:133-143) تترجم crypto/tls.Config إلى utls.Config:

go
func copyConfig(c *tls.Config) *utls.Config {
    return &utls.Config{
        Rand:                           c.Rand,
        RootCAs:                        c.RootCAs,
        ServerName:                     c.ServerName,
        InsecureSkipVerify:             c.InsecureSkipVerify,
        VerifyPeerCertificate:          c.VerifyPeerCertificate,
        KeyLogWriter:                   c.KeyLogWriter,
        EncryptedClientHelloConfigList: c.EncryptedClientHelloConfigList,
    }
}

إدارة الشهادات

GetTLSConfig

Config.GetTLSConfig (tls/config.go:364-481) يبني tls.Config:

go
func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
    root, _ := c.getCertPool()
    // Build RandCarrier with pinning/verification info
    config := &tls.Config{
        InsecureSkipVerify:     c.AllowInsecure,
        Rand:                   randCarrier,
        ClientSessionCache:     globalSessionCache,
        RootCAs:                root,
        NextProtos:             c.NextProtocol,
        SessionTicketsDisabled: !c.EnableSessionResumption,
        VerifyPeerCertificate:  randCarrier.verifyPeerCert,
    }
    // Apply options (WithDestination, WithNextProto, etc.)
    // Configure GetCertificate (dynamic or static)
    // Set ServerName, CurvePreferences, MinVersion, MaxVersion
    // Configure CipherSuites, KeyLogWriter, ECH
    return config
}

أنواع الشهادات

الشهادات لها وضعان للاستخدام:

  • ENCIPHERMENT (Certificate_ENCIPHERMENT): شهادة خادم/عميل عادية
  • AUTHORITY_ISSUE (Certificate_AUTHORITY_ISSUE): شهادة CA للإصدار الديناميكي

توليد الشهادات الديناميكي

عند وجود شهادات AUTHORITY_ISSUE، تولّد getGetCertificateFunc (config.go:172-244) شهادات عند الطلب:

go
func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
    return func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
        domain := hello.ServerName
        // Check existing certs, remove expired ones
        // Generate new cert from CA for domain
        newCert, _ := issueCertificate(rawCert, domain)
        c.Certificates = append(c.Certificates, *newCert)
        return issuedCertificate, nil
    }
}

هذا يمكّن سيناريوهات MitM ويزيل الحاجة لشهادات مولدة مسبقًا.

اختيار الشهادات الثابتة

getNewGetCertificateFunc (config.go:246-274) يطابق الشهادات حسب SNI:

go
func getNewGetCertificateFunc(certs []*tls.Certificate, rejectUnknownSNI bool) ... {
    // Match by CommonName or DNSNames
    // Support wildcard matching (*.example.com)
    // If rejectUnknownSNI, return error for unknown domains
    // Otherwise, return first certificate as fallback
}

إعادة التحميل السريع و OCSP

setupOcspTicker (config.go:96-131) يقوم دوريًا بـ:

  1. إعادة تحميل ملفات الشهادات من القرص (إذا تم تعيين CertificatePath/KeyPath)
  2. جلب بيانات تدبيس OCSP

الفاصل الزمني الافتراضي: 3600 ثانية (ساعة واحدة)، أو قيمة إعداد OcspStapling.

تثبيت الشهادات

PinnedPeerCertSha256

تجزئات SHA-256 للشهادات الموثوقة. التحقق في RandCarrier.verifyPeerCert (config.go:283-350):

go
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
    // Check if leaf cert matches any pinned hash -> success
    // Check if CA cert matches any pinned hash -> verify leaf against pinned CA
    // If VerifyPeerCertByName is set, verify against multiple domain names
    // Otherwise verify against ServerName
}

بنية RandCarrier (config.go:352-357) تستغل tls.Config.Rand لحمل حالة التحقق (لأن VerifyPeerCertificate ليس لديها آلية لتمرير بيانات مخصصة):

go
type RandCarrier struct {
    Config               *tls.Config
    RootCAs              *x509.CertPool
    VerifyPeerCertByName []string
    PinnedPeerCertSha256 [][]byte
}

توليد التجزئة

GenerateCertHash (tls/pin.go:10-19) تحسب SHA-256 لشهادة:

go
func GenerateCertHash[T *x509.Certificate | []byte](cert T) []byte {
    var out [32]byte
    switch v := any(cert).(type) {
    case *x509.Certificate:
        out = sha256.Sum256(v.Raw)
    case []byte:
        out = sha256.Sum256(v)
    }
    return out[:]
}

نظام الخيارات

دوال Option (config.go:484) تعدّل tls.Config بعد البناء الأولي:

go
type Option func(*tls.Config)

الخيارات المتاحة:

  • WithDestination: تعيّن ServerName من عنوان الوجهة (فقط إذا لم يكن معيّنًا مسبقًا) (config.go:490-496)
  • WithOverrideName: تفرض ServerName محددًا (config.go:498-502)
  • WithNextProto: تعيّن بروتوكولات ALPN (فقط إذا لم تكن معيّنة مسبقًا) (config.go:505-511)

أولوية حل SNI

نظرًا لبنية GetTLSConfig (config.go:415-417)، ترتيب حل ServerName هو:

  1. ServerName من الإعداد (إذا كان غير فارغ وليس "frommitm")
  2. خيار WithDestination (عنوان الوجهة)
  3. فارغ (لإعدادات جانب الخادم)

إدارة الجلسات

ذاكرة تخزين مؤقت عامة LRU للجلسات (config.go:24):

go
var globalSessionCache = tls.NewLRUClientSessionCache(128)

تذاكر الجلسة معطلة افتراضيًا (SessionTicketsDisabled: !c.EnableSessionResumption).

إعدادات الإصدار والتشفير

إصدار TLS

إصدارات حد أدنى/أقصى قابلة للتكوين (config.go:427-447):

  • "1.0" حتى "1.3"

مجموعات التشفير

قائمة مجموعات تشفير مخصصة من أسماء مفصولة بنقطتين (config.go:449-459):

go
if len(c.CipherSuites) > 0 {
    for _, n := range strings.Split(c.CipherSuites, ":") {
        config.CipherSuites = append(config.CipherSuites, id[n])
    }
}

تفضيلات المنحنيات

ParseCurveName (config.go:525-545) يربط أسماء النصوص بـ tls.CurveID:

الاسمالمنحنى
curvep256P-256
curvep384P-384
curvep521P-521
x25519X25519
x25519mlkem768X25519MLKEM768 (ما بعد الكم)
secp256r1mlkem768SecP256r1MLKEM768
secp384r1mlkem1024SecP384r1MLKEM1024

ConfigFromStreamSettings

دالة الاستخراج (config.go:514-523) تُستدعى بواسطة كل نقل للتحقق من إعدادات TLS:

go
func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config {
    if settings == nil { return nil }
    config, ok := settings.SecuritySettings.(*Config)
    if !ok { return nil }
    return config
}

تُعيد nil عندما لا يكون TLS مُعدًا، مما يشير إلى النقل بتخطي تغليف TLS.

ملاحظات التنفيذ

  • ALPN الافتراضي: إذا لم يتم تكوين NextProtocol، تعود إلى ["h2", "http/1.1"] (config.go:423-425).
  • مهلة الإغلاق: إغلاق TLS لديه مهلة صارمة 250 مللي ثانية لمنع التعليق عند الإيقاف الرشيق (tls.go:30-38).
  • دعم MitM: دالة IsFromMitm (config.go:547-549) تتحقق من علامة "frommitm" الخاصة في ServerName، المستخدمة لميزات بروكسي MitM.
  • VerifyPeerCertByName: عند التعيين، يُفرض InsecureSkipVerify على true (config.go:393-397)، ويتم التحقق يدويًا في verifyPeerCert مقابل أسماء نطاقات متعددة.
  • تسجيل المفاتيح: يُفعّل عبر إعداد MasterKeyLog، مع الكتابة إلى ملف لتصحيح الأخطاء باستخدام Wireshark (config.go:461-468).
  • ECH: يُطبق مرحبا العميل المشفر عبر ApplyECH عند تكوين EchConfigList أو EchServerKeys (config.go:469-478).
  • انتهاء صلاحية الشهادة: يُفحص مع فترة سماح مدتها دقيقتان (config.go:133-142).
  • بناء السلسلة: عندما يكون BuildChain مفعلاً على شهادة CA، تُلحق شهادة CA بالشهادات الصادرة حديثًا (config.go:155).

تحليل تقني لأغراض إعادة التنفيذ.