دليل مطوّري برامج إمكانية الدخول المعتمَد إلى Chrome

نبذة عن هذا الدليل

تسمح واجهة برمجة التطبيقات للدخول المتحقَّق منه في Chrome لخدمات الشبكة، مثل الشبكات الافتراضية الخاصة وصفحات الإنترانت وما إلى ذلك، بالتحقق بطريقة مشفّرة من أن عملائها صادقين ويتوافقون مع سياسة الشركة. تطلب معظم المؤسسات الكبيرة السماح للأجهزة المُدارة فقط في شبكات WPA2 EAP-TLS، والوصول من مستوى أعلى إلى الشبكات الافتراضية الخاصة، وصفحات الشبكة الداخلية المشتركة لطبقة النقل الآمنة. تعتمد العديد من الحلول الحالية على عمليات فحص استدلالية على نفس العميل قد يكون مُخترَقًا. يمثل هذا تحديًا بأن الإشارات التي يتم الاعتماد عليها للتحقق من الحالة المشروعة للجهاز هي نفسها قد تم تزويرها. يوفر هذا الدليل ضمانات تشفير مستندة إلى الأجهزة لهوية الجهاز وأن حالته لم يتم تعديلها ومتوافقة مع السياسة عند التشغيل، ويسمى ذلك "الدخول المتحقَّق منه".

الجمهور الأساسي مشرفو نطاقات تكنولوجيا المعلومات في المؤسسات
المكوّنات الفنية ChromeOS وواجهة برمجة التطبيقات للدخول المعتمَد من Google

المتطلبات الأساسية للدخول المتحقَّق منه

أكمل الإعداد التالي قبل تنفيذ عملية الدخول المتحقَّق منه.

تفعيل واجهة برمجة التطبيقات

إعداد مشروع وحدة تحكم واجهة برمجة تطبيقات Google وتفعيل واجهة برمجة التطبيقات:

  1. أنشئ مشروعًا حاليًا أو استخدِمه في وحدة تحكم واجهة برمجة تطبيقات Google.
  2. انتقِل إلى صفحة واجهات برمجة التطبيقات والخدمات التي تم تفعيلها.
  3. فعِّل واجهة برمجة التطبيقات للدخول المعتمَد في Chrome.
  4. يمكنك إنشاء مفتاح واجهة برمجة تطبيقات لتطبيقك من خلال اتّباع مستندات Google Cloud API.

إنشاء حساب خدمة

لكي تتمكّن خدمة الشبكة من الوصول إلى واجهة برمجة التطبيقات للوصول المتحقَّق منه في Chrome للتحقّق من استجابتك للتحدي، أنشِئ حساب خدمة ومفتاح حساب خدمة (لست بحاجة إلى إنشاء مشروع جديد على السحابة الإلكترونية، ويمكنك استخدام المشروع نفسه).

بعد إنشاء مفتاح حساب الخدمة، من المفترض أن يكون قد تم تنزيل ملف المفتاح الخاص لحساب الخدمة. هذه هي النسخة الوحيدة من المفتاح الخاص، لذا احرص على تخزينه بأمان.

تسجيل جهاز Chromebook مُدار

تحتاج إلى إعداد جهاز Chromebook مُدار بشكل صحيح مع إضافة Chrome للدخول المتحقَّق منه.

  1. يجب أن يكون جهاز Chromebook مسجّلاً في إدارة المؤسسات أو المؤسسات التعليمية.
  2. يجب أن يكون مستخدم الجهاز مستخدمًا مسجلاً من النطاق نفسه.
  3. يجب أن تكون إضافة Chrome لإمكانية الدخول المعتمد مثبّتة على الجهاز.
  4. يتم ضبط السياسات لتفعيل ميزة "الوصول المتحقَّق منه" وتفعيل إضافة Chrome في القائمة المسموح بها ومنح إذن الوصول إلى واجهة برمجة التطبيقات لحساب الخدمة الذي يمثّل خدمة الشبكة (يُرجى الاطّلاع على مستندات المساعدة الخاصة بـ "وحدة تحكّم المشرف في Google").

إثبات ملكية المستخدم والجهاز

يمكن للمطوّرين استخدام ميزة "الوصول المتحقَّق منه" للتحقّق من المستخدم أو الجهاز، أو استخدام كليهما معًا لمزيد من الأمان:

  • التحقق من الجهاز—في حالة نجاح التحقق من الجهاز، تقدم عملية التحقق من الجهاز ضمانًا بأن جهاز Chrome مسجل في نطاق مُدار وأنه يتوافق مع سياسة الجهاز في وضع التشغيل المتحقق منه كما هو محدد بواسطة مشرف النطاق. إذا تم منح خدمة الشبكة إذنًا للاطّلاع على هوية الجهاز (راجِع مستندات مساعدة "وحدة تحكُّم المشرف في Google")، ستتلقّى أيضًا هذه الخدمة رقم تعريف جهاز يمكن استخدامه للتدقيق في واجهة برمجة تطبيقات الدليل أو تتبُّعها أو الاتصال بها.

  • التحقق من المستخدم: في حال نجاح هذا الإجراء، تضمن عملية التحقّق من المستخدم ضمانًا بأنّ مستخدم Chrome الذي سجّل الدخول هو مستخدم مُدار، وأنّه يستخدم جهازًا مسجَّلاً، ويتوافق مع سياسة المستخدم في وضع التشغيل المتحقَّق منه على النحو الذي يحدّده مشرف النطاق. وإذا تم منح خدمة الشبكة إذنًا بتلقي بيانات مستخدم إضافية، فستحصل أيضًا على طلب توقيع شهادة صادر عن المستخدم (CSR في شكل Signed-public-key-and-challenge أو SPKAC، المعروف أيضًا باسم تنسيق Keygen).

كيفية التحقّق من المستخدم والجهاز

  1. الحصول على تحدٍ: تتصل إضافة Chrome على الجهاز بواجهة برمجة التطبيقات للدخول المُتحقَّق منه للحصول على تحد. التحدي هو بنية بيانات مبهمة (كائن فقاعة موقعة من Google) وصالح لمدة دقيقة واحدة، مما يعني أن التحقق من الاستجابة للتحدي (الخطوة 3) يفشل في حالة استخدام تحدي قديم.

    في أبسط حالات الاستخدام، يبدأ المستخدم هذا التدفق من خلال النقر على زر تنشئه الإضافة (هذا هو أيضًا ما تفعله نموذج الإضافة التي توفّرها Google).

    var apiKey = 'YOUR_API_KEY_HERE';
    var challengeUrlString =
      'https://verifiedaccess.googleapis.com/v2/challenge:generate?key=' + apiKey;
    
    // Request challenge from URL
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open('POST', challengeUrlString, true);
    xmlhttp.send();
    xmlhttp.onreadystatechange = function() {
      if (xmlhttp.readyState == 4) {
        var challenge = xmlhttp.responseText;
        console.log('challenge: ' + challenge);
        // v2 of the API returns an encoded challenge so no further challenge processing is needed
      }
    };
    

    رمز مساعد لترميز التحدي: إذا كنت تستخدم الإصدار 1 من واجهة برمجة التطبيقات، يجب ترميز التحدي.

    // This can be replaced by using a third-party library such as
    // [https://github.com/dcodeIO/ProtoBuf.js/wiki](https://github.com/dcodeIO/ProtoBuf.js/wiki)
    /**
     * encodeChallenge convert JSON challenge into base64 encoded byte array
     * @param {string} challenge JSON encoded challenge protobuf
     * @return {string} base64 encoded challenge protobuf
     */
    var encodeChallenge = function(challenge) {
      var jsonChallenge = JSON.parse(challenge);
      var challengeData = jsonChallenge.challenge.data;
      var challengeSignature = jsonChallenge.challenge.signature;
    
      var protobufBinary = protoEncodeChallenge(
          window.atob(challengeData), window.atob(challengeSignature));
    
      return window.btoa(protobufBinary);
    };
    
    /**
     * protoEncodeChallenge produce binary encoding of the challenge protobuf
     * @param {string} dataBinary binary data field
     * @param {string} signatureBinary binary signature field
     * @return {string} binary encoded challenge protobuf
     */
    var protoEncodeChallenge = function(dataBinary, signatureBinary) {
      var protoEncoded = '';
    
      // See https://developers.google.com/protocol-buffers/docs/encoding
      // for encoding details.
    
      // 0x0A (00001 010, field number 1, wire type 2 [length-delimited])
      protoEncoded += '\u000A';
    
      // encode length of the data
      protoEncoded += varintEncode(dataBinary.length);
      // add data
      protoEncoded += dataBinary;
    
      // 0x12 (00010 010, field number 2, wire type 2 [length-delimited])
      protoEncoded += '\u0012';
      // encode length of the signature
      protoEncoded += varintEncode(signatureBinary.length);
      // add signature
      protoEncoded += signatureBinary;
    
      return protoEncoded;
    };
    
    /**
     * varintEncode produce binary encoding of the integer number
     * @param {number} number integer number
     * @return {string} binary varint-encoded number
     */
    var varintEncode = function(number) {
      // This only works correctly for numbers 0 through 16383 (0x3FFF)
      if (number <= 127) {
        return String.fromCharCode(number);
      } else {
        return String.fromCharCode(128 + (number & 0x7f), number >>> 7);
      }
    };
    
  2. إنشاء استجابة للتحدي: تستخدم إضافة Chrome التحدي الذي تلقته في الخطوة 1 لطلب واجهة برمجة تطبيقات Chrome enterprise.platformKeys. يؤدّي ذلك إلى إنشاء استجابة موقّعة ومشفّرة للتحدي، والتي تدرجها الإضافة في طلب الوصول الذي ترسله إلى خدمة الشبكة.

    في هذه الخطوة، لا توجد محاولة لتحديد بروتوكول تستخدمه الإضافة وخدمة الشبكة للاتصال. يتم تنفيذ هذين الكيانين من قبل مطورين خارجيين ولا يتم تحديدهما لكيفية التحدث مع بعضهما البعض. من الأمثلة على ذلك إرسال استجابة تحدي (بترميز عنوان URL) كمعلمة سلسلة طلب البحث، أو باستخدام HTTP POST، أو استخدام عنوان HTTP خاص.

    إليك نموذج رمز لإنشاء رد على التحدي:

    إنشاء ردّ للتحدي

      // Generate challenge response
      var encodedChallenge; // obtained by generate challenge API call
      try {
        if (isDeviceVerification) { // isDeviceVerification set by external logic
          chrome.enterprise.platformKeys.challengeKey(
              {
                scope: 'MACHINE',
                challenge: decodestr2ab(encodedChallenge),
              },
              ChallengeCallback);
        } else {
          chrome.enterprise.platformKeys.challengeKey(
              {
                scope: 'USER',
                challenge: decodestr2ab(encodedChallenge),
                registerKey: { 'RSA' }, // can also specify 'ECDSA'
              },
              ChallengeCallback);
        }
      } catch (error) {
        console.log('ERROR: ' + error);
      }
    

    وظيفة رد الاتصال الخاصة بالتحدي

      var ChallengeCallback = function(response) {
        if (chrome.runtime.lastError) {
          console.log(chrome.runtime.lastError.message);
        } else {
          var responseAsString = ab2base64str(response);
          console.log('resp: ' + responseAsString);
        ... // send on to network service
       };
      }
    

    رمز مساعد لتحويل ArrayBuffer

      /**
       * ab2base64str convert an ArrayBuffer to base64 string
       * @param {ArrayBuffer} buf ArrayBuffer instance
       * @return {string} base64 encoded string representation
       * of the ArrayBuffer
       */
      var ab2base64str = function(buf) {
        var binary = '';
        var bytes = new Uint8Array(buf);
        var len = bytes.byteLength;
        for (var i = 0; i < len; i++) {
          binary += String.fromCharCode(bytes[i]);
        }
        return window.btoa(binary);
      }
    
      /**
       * decodestr2ab convert a base64 encoded string to ArrayBuffer
       * @param {string} str string instance
       * @return {ArrayBuffer} ArrayBuffer representation of the string
       */
      var decodestr2ab = function(str) {
        var binary_string =  window.atob(str);
        var len = binary_string.length;
        var bytes = new Uint8Array(len);
        for (var i = 0; i < len; i++)        {
            bytes[i] = binary_string.charCodeAt(i);
        }
        return bytes.buffer;
      }
    
  3. التحقق من الاستجابة للتحدي: عند تلقي استجابة للتحدي من أحد الأجهزة (ربما كإضافة لبروتوكول مصادقة حالي)، يجب أن تتصل خدمة الشبكة بواجهة برمجة التطبيقات لإمكانية الدخول المعتمَد للتحقق من هوية الجهاز ووضع السياسة (انظر مثال الرمز أدناه). لمكافحة الانتحال، ننصح بأن تحدد خدمة الشبكة العميل الذي تتحدث إليه وأن تتضمن الهوية المتوقعة للعميل في طلبها:

    • لإثبات ملكية الجهاز، يجب تقديم نطاق الجهاز المتوقّع . ومن المحتمل أن تكون هذه القيمة غير قابلة للتغيير في البرنامج في كثير من الحالات، لأنّ خدمة الشبكة تحمي الموارد الخاصة بنطاق معيّن. إذا لم يكن هذا معروفًا في وقت مبكر، يمكن استنتاجه من هوية المستخدم.
    • لإثبات هوية المستخدم، يجب تقديم عنوان البريد الإلكتروني الخاص بالمستخدم المتوقّع. نتوقّع أن تتعرّف خدمة الشبكة على مستخدميها (عادةً ما يُطلب من المستخدمين تسجيل الدخول).

    عند استدعاء واجهة برمجة تطبيقات Google، فإنّها تُجري عددًا من عمليات التحقّق، مثل:

    • تأكَّد من أنّ نظام التشغيل ChromeOS ينتج عنه استجابة التحدي ولا يتم تعديله أثناء نقلها.
    • تأكَّد من أنّ الجهاز أو المستخدم مُدارما من قِبل المؤسسة.
    • تأكَّد من أنّ هوية الجهاز/المستخدم تتطابق مع الهوية المتوقّعة (إذا تم توفير الاسم الثاني).
    • تأكَّد من أنّ التحدي الذي تمّ الرد عليه حديث (لا يزيد عن دقيقة واحدة).
    • تحقَّق من توافُق الجهاز أو المستخدم مع السياسة التي حدّدها مشرف النطاق.
    • تأكَّد من منح المتصل (خدمة الشبكة) إذنًا لطلب بيانات من واجهة برمجة التطبيقات.
    • إذا تم منح المتصل إذنًا بالحصول على بيانات إضافية عن الجهاز أو المستخدم، يمكنك تضمين رقم تعريف الجهاز أو طلب توقيع شهادة المستخدم (CSR) في الرد.

    يستخدم هذا المثال مكتبة gRPC.

    import com.google.auth.oauth2.GoogleCredentials;
    import com.google.auth.oauth2.ServiceAccountCredentials;
    import com.google.chrome.verifiedaccess.v2.VerifiedAccessGrpc;
    import com.google.chrome.verifiedaccess.v2.VerifyChallengeResponseRequest;
    import com.google.chrome.verifiedaccess.v2.VerifyChallengeResponseResult;
    import com.google.protobuf.ByteString;
    
    import io.grpc.ClientInterceptor;
    import io.grpc.ClientInterceptors;
    import io.grpc.ManagedChannel;
    import io.grpc.auth.ClientAuthInterceptor;
    import io.grpc.netty.GrpcSslContexts;
    import io.grpc.netty.NettyChannelBuilder;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.util.Arrays;
    import java.util.concurrent.Executors;
    
    // https://cloud.google.com/storage/docs/authentication#generating-a-private-key
    private final String clientSecretFile = "PATH_TO_GENERATED_JSON_SECRET_FILE";
    
    private ManagedChannel channel;
    private VerifiedAccessGrpc.VerifiedAccessBlockingStub client;
    
    void setup() {
    
       channel = NettyChannelBuilder.forAddress("verifiedaccess.googleapis.com", 443)
          .sslContext(GrpcSslContexts.forClient().ciphers(null).build())
          .build();
    
       List<ClientInterceptor> interceptors = Lists.newArrayList();
       // Attach a credential for my service account and scope it for the API.
       GoogleCredentials credentials =
           ServiceAccountCredentials.class.cast(
               GoogleCredentials.fromStream(
                   new FileInputStream(new File(clientSecretFile))));
      credentials = credentials.createScoped(
          Arrays.<String>asList("https://www.googleapis.com/auth/verifiedaccess"));
      interceptors.add(
           new ClientAuthInterceptor(credentials, Executors.newSingleThreadExecutor()));
    
      // Create a stub bound to the channel with the interceptors applied
      client = VerifiedAccessGrpc.newBlockingStub(
          ClientInterceptors.intercept(channel, interceptors));
    }
    
    /**
     * Invokes the synchronous RPC call that verifies the device response.
     * Returns the result protobuf as a string.
     *
     * @param signedData base64 encoded signedData blob (this is a response from device)
     * @param expectedIdentity expected identity (domain name or user email)
     * @return the verification result protobuf as string
     */
    public String verifyChallengeResponse(String signedData, String expectedIdentity)
      throws IOException, io.grpc.StatusRuntimeException {
      VerifyChallengeResponseResult result =
        client.verifyChallengeResponse(newVerificationRequest(signedData,
            expectedIdentity)); // will throw StatusRuntimeException on error.
    
      return result.toString();
    }
    
    private VerifyChallengeResponseRequest newVerificationRequest(
      String signedData, String expectedIdentity) throws IOException {
      return VerifyChallengeResponseRequest.newBuilder()
        .setChallengeResponse(
            ByteString.copyFrom(BaseEncoding.base64().decode(signedData)))
        .setExpectedIdentity(expectedIdentity == null ? "" : expectedIdentity)
        .build();
    }
    
  4. منح إذن الوصول: ترتبط هذه الخطوة أيضًا بخدمات الشبكة. هذا تطبيق مقترح (غير موصوف). قد تكون الإجراءات المحتملة:

    • إنشاء ملف تعريف ارتباط جلسة
    • إصدار شهادة للمستخدم أو الجهاز وفي حال نجاح التحقُّق من المستخدم، وافتراض منح خدمة الشبكة إذن الوصول إلى بيانات المستخدمين الإضافية (من خلال سياسة وحدة تحكُّم المشرف في Google)، تحصل الخدمة على طلب CSR موقَّع من قِبل المستخدم، والذي يمكن استخدامه بعد ذلك للحصول على الشهادة الفعلية من هيئة إصدار الشهادات. عند الدمج مع مرجع التصديق Microsoft، يمكن لخدمة الشبكة أن تعمل كوسيط وتستفيد من واجهة ICertRequest.

استخدام شهادات العميل مع إمكانية الدخول المتحقَّق منه

استخدام شهادات العميل مع إمكانية الدخول المعتمد:

في المؤسسات الكبيرة، قد يكون هناك العديد من خدمات الشبكة (خوادم VPN ونقاط الوصول إلى Wi-Fi والجدران النارية ومواقع الشبكة الداخلية المتعددة) التي يمكن أن تستفيد من ميزة "الوصول المتحقَّق منه". ومع ذلك، قد لا يكون بناء منطق الخطوات من 2 إلى 4 (في القسم أعلاه) في كل خدمة من خدمات الشبكة هذه عمليًا. غالبًا ما تمتلك العديد من خدمات الشبكات هذه حاليًا القدرة على طلب شهادات العميل كجزء من مصادقاتها (على سبيل المثال، EAP-TLS أو صفحات الشبكة الداخلية المشتركة لطبقة النقل الآمنة). لذا، إذا كان بإمكان هيئة إصدار الشهادات التي تُصدر شهادات العميل تنفيذ الخطوات من 2 إلى 4 وشرط إصدار شهادة العميل بشأن التحقق من الاستجابة للتحدي، يمكن أن يكون حيازة الشهادة دليلاً على أنّ العميل حقيقي ويلتزم بسياسة الشركة. وبعد ذلك، يمكن لكل نقطة وصول Wi-Fi وخادم VPN وما إلى ذلك التحقق من شهادة العميل هذه بدلاً من اتباع الخطوات من 2 إلى 4.

وبعبارة أخرى، يتولى مرجع التصديق (الذي يُصدر شهادة العميل لأجهزة المؤسسات) هنا دور خدمة الشبكة في الشكل 1. ويجب استدعاء واجهة برمجة التطبيقات للوصول المتحقَّق منه، وفقط عند اجتياز التحقق من الاستجابة للتحدي، يجب تقديم الشهادة للعميل. يعادل تقديم الشهادة للعميل الخطوة 4: منح حق الوصول في الشكل 1.

يمكنك الاطّلاع على توضيح لعملية الحصول على شهادات العميل على أجهزة Chromebook بشكل آمن في هذه المقالة. في حال اتّباع التصميم الموضّح في هذه الفقرة، يمكن دمج إضافة إمكانية الوصول المُتحقَّق منه وإضافة إعداد شهادة العميل في إضافة واحدة. اطّلِع على المزيد من المعلومات حول كيفية كتابة إضافة إعداد شهادة عميل.