Skip to content

קריאת רמזי AIA בתעודה בלי לבלבל בין אחזור לאמון

קריאת רמזי AIA בתעודה בלי לבלבל בין אחזור לאמון

הקוד להמחשה ולא הורץ.

המקור האנגלי פורסם בתאריך 2023-10-29T11:43:00.008+02:00. המאמר המקורי באנגלית

סמל ספריית Bouncy Castle מהמאמר המקורי

Authority Information Access יכולה לתאר מיקומים של תעודות מנפיק ושירותי OCSP. אלו שיטות גישה שונות: caIssuers אינו נקודת קצה לתשובת OCSP. מיקום AIA הוא רמז לאיתור הכלול בתעודה, לא הרשאה לאחזר URL ולא ראיה לכך שהמנפיק שהוחזר מהימן. ההרחבה עשויה להיעדר, ומעקב אחריה אינו מבטיח שרשרת מלאה. [1]

חילוץ רמזים בלי לאחזר אותם

דוגמת Bouncy Castle הזו מטפלת בהרחבה חסרה, מגבילה את גודל הקלט ואת מספר הרשומות, בודקת את תג ה־URI של GeneralName ומחזירה רק מועמדי HTTPS תקינים מבחינה תחבירית. HTTPS בלבד הוא מדיניות דוגמה מגבילה במכוון, והיא עשויה להשמיט מיקומי AIA לגיטימיים המשתמשים ב־HTTP. הבדיקות אינן הופכות URL לבטוח לאחזור: לא מבוצעת פנייה ל־DNS או לרשת. [2]

using System;
using System.Collections.Generic;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.X509;

public static class AiaHintsExample
{
    // Parse only. Returned URIs are untrusted, not approved network destinations.
    public static IReadOnlyList<Uri> ReadHttpsIssuerHints(X509Certificate certificate)
    {
        if (certificate == null) throw new ArgumentNullException(nameof(certificate));
        var output = new List<Uri>();
        var value = certificate.GetExtensionValue(X509Extensions.AuthorityInfoAccess);
        if (value == null) return output;
        byte[] encoded = value.GetOctets();
        if (encoded.Length == 0 || encoded.Length > 65536)
            throw new ArgumentException("AIA exceeds this parser's size policy.");
        var aia = AuthorityInformationAccess.GetInstance(Asn1Object.FromByteArray(encoded));
        AccessDescription[] descriptions = aia.GetAccessDescriptions();
        if (descriptions.Length > 32) throw new ArgumentException("Too many AIA entries.");
        var seen = new HashSet<string>(StringComparer.Ordinal);
        foreach (AccessDescription description in descriptions)
        {
            if (!description.AccessMethod.Equals(X509ObjectIdentifiers.IdADCAIssuers)) continue;
            GeneralName location = description.AccessLocation;
            if (location.TagNo != GeneralName.UniformResourceIdentifier) continue;
            string text = DerIA5String.GetInstance(location.Name).GetString();
            if (text.Length > 4096 || !Uri.TryCreate(text, UriKind.Absolute, out Uri? uri)) continue;
            if (uri.Scheme != Uri.UriSchemeHttps || uri.Port != 443 ||
                string.IsNullOrEmpty(uri.Host) || uri.UserInfo.Length != 0 || uri.Fragment.Length != 0)
                continue;
            if (seen.Add(uri.AbsoluteUri)) output.Add(uri);
        }
        return output;
    }
}

אם מוסיפים אחזור בהמשך

מאחזר נפרד שאושר צריך רשימת היתר מדויקת של מארחים ויציאות ואכיפה של תעבורת רשת יוצאת. יש לדחות כתובות loopback, כתובות פרטיות, link-local, multicast וכל כתובת יעד שלא אושרה, לרבות כתובות IPv4 הממופות ל־IPv6. יש לאמת כל תשובת DNS ולקבע את יעד החיבור שאושר, או להשתמש בשרת מתווך מבוקר כדי למנוע DNS rebinding. יש להשבית הפניות מחדש או להחיל מחדש את כל המדיניות בכל קפיצה. אין להעביר אישורים, עוגיות או אימות משתמע, ואין לאפשר סכמות URL כמו file או LDAP או סכמות שרירותיות אחרות. [3]

יש להגביל זמני חיבור, קריאה ומשך כולל, את מספר בתי התשובה לאחר פריסה, את מספר התעודות, ניסיונות החזרה, עומק הרקורסיה ומספר הבקשות הכולל. יש לעקוב אחרי טביעות אצבע של תעודות וכתובות שכבר נבקרו כדי לעצור מחזורים. יש לפענח רק תבניות תעודה או מכל שנתמכות במפורש; caIssuers יכול להחזיר תעודה או מכל CMS המכיל תעודות בלבד, ולכן ההנחה שתמיד מתקבלת תעודת DER יחידה אינה כללית. יש לשמר כשלים לצורך אבחון ולנסות חלופות מותרות במסגרת התקציב; אין לבלוע כל חריגה ולכנות את התוצאה שרשרת מלאה. [1][3]

אימות לפי מדיניות אמון שנבחרה בנפרד

התאמה בין שמו המובחן של המנפיק לשם הנושא אינה בדיקת חתימה. גם תעודה בעלת חתימה עצמית תקינה אינה עוגן אמון אוטומטי. יש להשתמש במאמת שרשרת לבדיקת חתימות, מגבלות CA ו־basic constraints, שימושי מפתח, תוקף, מגבלות נתיב ושם ומטרת היישום המיועדת. עוגני האמון חייבים להיבחר בנפרד מתעודות שהורדו דרך AIA; תעודת ביניים שייכת למאגר נוסף, לא למאגר שורשים מהימנים. [1]

דוגמת .NET מודרני הבאה מקבלת כקלט תעודת קצה, תעודות ביניים מועמדות ושורשים שאושרו, משביתה הורדות AIA ומשתמשת בנתוני ביטול שבמטמון. מנועי השרשרת של הפלטפורמה עשויים גם לעיין במטמון מקומי של תעודות ביניים; זה אינו מאמת מבודד לחלוטין. נתוני ביטול חסרים או מיושנים הם כשל אימות, ולא סיבה להשבית בדיקת ביטול. תוצאה תקינה עדיין מחייבת בדיקות זהות או שם מארח והרשאה התלויות ביישום. הקוד לא הורץ, ושום שרשרת או מאגר אמון לא נבדקו ולא שונו. [4][5][6]

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

public static class OfflineChainExample
{
    public static bool Validate(X509Certificate2 leaf,
        IReadOnlyCollection<X509Certificate2> candidateIntermediates,
        IReadOnlyCollection<X509Certificate2> independentlyApprovedRoots,
        string requiredApplicationOid, DateTime verificationTimeUtc)
    {
        if (leaf == null || candidateIntermediates == null || independentlyApprovedRoots == null)
            throw new ArgumentNullException("Certificate inputs");
        if (candidateIntermediates.Count > 32 || independentlyApprovedRoots.Count < 1 ||
            independentlyApprovedRoots.Count > 32 || string.IsNullOrWhiteSpace(requiredApplicationOid) ||
            verificationTimeUtc.Kind != DateTimeKind.Utc)
            throw new ArgumentException("Explicit bounded trust, purpose and UTC time are required.");
        using var chain = new X509Chain();
        chain.ChainPolicy.DisableCertificateDownloads = true;
        chain.ChainPolicy.RevocationMode = X509RevocationMode.Offline;
        chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
        chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
        chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
        chain.ChainPolicy.VerificationTime = verificationTimeUtc;
        chain.ChainPolicy.ApplicationPolicy.Add(new Oid(requiredApplicationOid));
        foreach (var root in independentlyApprovedRoots) chain.ChainPolicy.CustomTrustStore.Add(root);
        foreach (var issuer in candidateIntermediates) chain.ChainPolicy.ExtraStore.Add(issuer);
        bool valid = chain.Build(leaf);
        // Retain ChainStatus for controlled diagnostics in a real application.
        return valid && chain.ChainStatus.Length == 0 && chain.ChainElements.Count <= 10;
    }
}

מקורות ראשוניים

  1. RFC 5280 sections 4.2.2.1 and 6
  2. Bouncy Castle AuthorityInformationAccess source
  3. OWASP SSRF Prevention Cheat Sheet
  4. Microsoft DisableCertificateDownloads
  5. Microsoft X509RevocationMode
  6. Microsoft X509ChainPolicy.TrustMode

Post a Comment

Previous Post Next Post