Praktik terbaik TLS/SSL

TLS (Transport Layer Security) adalah protokol kriptografi yang dirancang untuk mengamankan komunikasi antara dua komputer melalui internet. Protokol TLS diekspos di .NET melalui SslStream kelas .

Artikel ini menyajikan praktik terbaik untuk menyiapkan komunikasi yang aman antara klien dan server dan mengasumsikan penggunaan .NET. Untuk praktik terbaik dengan .NET Framework, lihat Praktik terbaik Keamanan Lapisan Transportasi (TLS) dengan .NET Framework.

Pilih versi TLS

Meskipun dimungkinkan untuk menentukan versi protokol TLS yang akan digunakan melalui EnabledSslProtocols properti , disarankan untuk menunggak pengaturan sistem operasi dengan menggunakan None nilai (ini adalah default).

Menuangkan keputusan ke OS secara otomatis menggunakan versi terbaru TLS yang tersedia dan memungkinkan aplikasi mengambil perubahan setelah peningkatan OS. Sistem operasi juga dapat mencegah penggunaan versi TLS yang tidak lagi dianggap aman.

Pilih cipher suite

SslStream memungkinkan pengguna untuk menentukan cipher suite mana yang dapat dinegosiasikan oleh jabat tangan TLS melalui CipherSuitesPolicy kelas . Seperti halnya versi TLS, disarankan untuk membiarkan OS memutuskan cipher suite mana yang terbaik untuk dinegosiasikan dengan, dan, oleh karena itu, disarankan untuk menghindari penggunaan CipherSuitesPolicy.

Catatan

CipherSuitesPolicy tidak didukung pada Windows dan upaya untuk membuat instans itu akan menyebabkan NotSupportedException dilemparkan.

Tentukan sertifikat server

Saat mengautentikasi sebagai server, SslStream memerlukan instans X509Certificate2 . Disarankan untuk selalu menggunakan X509Certificate2 instans yang juga berisi kunci privat.

Ada beberapa cara agar sertifikat server dapat diteruskan ke SslStream:

Pendekatan yang disarankan adalah menggunakan SslServerAuthenticationOptions.ServerCertificateContext properti . Ketika sertifikat diperoleh oleh salah satu dari dua cara lainnya, SslStreamCertificateContext instans dibuat secara internal oleh SslStream implementasi. SslStreamCertificateContext Membuat melibatkan pembangunan yang X509Chain merupakan operasi intensif CPU. Lebih efisien untuk membuat SslStreamCertificateContext sekali dan menggunakannya kembali untuk beberapa SslStream instans.

Menggunakan kembali SslStreamCertificateContext instans juga memungkinkan fitur tambahan seperti kami dimulainya kembali sesi TLS di server Linux.

Validasi kustom X509Certificate

Ada skenario tertentu di mana prosedur validasi sertifikat default tidak memadai dan beberapa logika validasi kustom diperlukan. Bagian dari logika validasi dapat disesuaikan dengan menentukan SslClientAuthenticationOptions.CertificateChainPolicy atau SslServerAuthenticationOptions.CertificateChainPolicy. Atau, logika kustom sepenuhnya dapat disediakan melalui <properti System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback> . Untuk informasi selengkapnya, lihat Kepercayaan sertifikat kustom.

Kepercayaan sertifikat kustom

Saat menemukan sertifikat yang tidak dikeluarkan oleh salah satu otoritas sertifikat yang dipercaya oleh mesin (termasuk sertifikat yang ditandatangani sendiri), prosedur validasi sertifikat default akan gagal. Salah satu cara yang mungkin untuk mengatasinya adalah dengan menambahkan sertifikat penerbit yang diperlukan ke penyimpanan tepercaya komputer. Namun, itu dapat memengaruhi aplikasi lain pada sistem dan tidak selalu memungkinkan.

Solusi alternatifnya adalah menentukan sertifikat akar tepercaya kustom melalui X509ChainPolicy. Untuk menentukan daftar kepercayaan kustom yang akan digunakan alih-alih daftar kepercayaan sistem selama validasi, pertimbangkan contoh berikut:

SslClientAuthenticationOptions clientOptions = new();

clientOptions.CertificateChainPolicy = new X509ChainPolicy()
{
    TrustMode = X509ChainTrustMode.CustomRootTrust,
    CustomTrustStore =
    {
        customIssuerCert
    }
};

Klien yang dikonfigurasi dengan kebijakan sebelumnya hanya akan menerima sertifikat yang dipercaya oleh customIssuerCert.

Abaikan kesalahan validasi tertentu

Pertimbangkan perangkat IoT tanpa jam persisten. Setelah menyala, jam perangkat akan dimulai bertahun-tahun di masa lalu dan, oleh karena itu, semua sertifikat akan dianggap "belum valid". Pertimbangkan kode berikut yang menunjukkan implementasi panggilan balik validasi yang mengabaikan pelanggaran periode validitas.

static bool CustomCertificateValidationCallback(
    object sender,
    X509Certificate? certificate,
    X509Chain? chain,
    SslPolicyErrors sslPolicyErrors)
{
    // Anything that would have been accepted by default is OK
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        return true;
    }
    
    // If there is something wrong other than a chain processing error, don't trust it.
    if (sslPolicyErrors != SslPolicyErrors.RemoteCertificateChainErrors)
    {
        return false;
    }
    
    Debug.Assert(chain is not null);

    // If the reason for RemoteCertificateChainError is that the chain built empty, don't trust it.
    if (chain.ChainStatus.Length == 0)
    {
        return false;
    }

    foreach (X509ChainStatus status in chain.ChainStatus)
    {
        // If an error other than `NotTimeValid` (or `NoError`) is present, don't trust it.
        if ((status.Status & ~X509ChainStatusFlags.NotTimeValid) != X509ChainStatusFlags.NoError)
        {
            return false;
        }
    }

    return true;
}

Penyematan sertifikat

Situasi lain di mana validasi sertifikat kustom diperlukan adalah ketika klien mengharapkan server untuk menggunakan sertifikat tertentu, atau sertifikat dari sekumpulan kecil sertifikat yang diketahui. Praktik ini dikenal sebagai penyematan sertifikat. Cuplikan kode berikut menunjukkan panggilan balik validasi yang memeriksa apakah server menyajikan sertifikat dengan kunci publik tertentu yang diketahui.

static bool CustomCertificateValidationCallback(
    object sender,
    X509Certificate? certificate,
    X509Chain? chain,
    SslPolicyErrors sslPolicyErrors)
{
    // If there is something wrong other than a chain processing error, don't trust it.
    if ((sslPolicyErrors & ~SslPolicyErrors.RemoteCertificateChainErrors) != 0)
    {
        return false;
    }
    
    Debug.Assert(certificate is not null);

    const string ExpectedPublicKey =
        "3082010A0282010100C204ECF88CEE04C2B3D850D57058CC9318EB5C" +
        "A86849B022B5F9959EB12B2C763E6CC04B604C4CEAB2B4C00F80B6B0" +
        "F972C98602F95C415D132B7F71C44BBCE9942E5037A6671C618CF641" +
        "42C546D31687279F74EB0A9D11522621736C844C7955E4D16BE8063D" +
        "481552ADB328DBAAFF6EFF60954A776B39F124D131B6DD4DC0C4FC53" +
        "B96D42ADB57CFEAEF515D23348E72271C7C2147A6C28EA374ADFEA6C" +
        "B572B47E5AA216DC69B15744DB0A12ABDEC30F47745C4122E19AF91B" +
        "93E6AD2206292EB1BA491C0C279EA3FB8BF7407200AC9208D98C5784" +
        "538105CBE6FE6B5498402785C710BB7370EF6918410745557CF9643F" +
        "3D2CC3A97CEB931A4C86D1CA850203010001";

    return certificate.GetPublicKeyString().Equals(ExpectedPublicKey);
}

Pertimbangan untuk validasi sertifikat klien

Aplikasi server harus berhati-hati saat memerlukan dan memvalidasi sertifikat klien. Sertifikat mungkin berisi ekstensi AIA (Akses Informasi Otoritas) yang menentukan di mana sertifikat penerbit dapat diunduh. Oleh karena itu, server dapat mencoba mengunduh sertifikat penerbit dari server eksternal saat membangun X509Chain untuk sertifikat klien. Demikian pula, server mungkin perlu menghubungi server eksternal untuk memastikan bahwa sertifikat klien belum dicabut.

Kebutuhan untuk menghubungi server eksternal saat membangun dan memvalidasi X509Chain dapat mengekspos aplikasi terhadap penolakan serangan layanan jika server eksternal lambat merespons. Oleh karena itu, aplikasi server harus mengonfigurasi X509Chain perilaku membangun menggunakan CertificateChainPolicy.