راهنمای برنامه‌نویس Chrome Verified Access

درباره این راهنما

Chrome Verified Access API به سرویس‌های شبکه مانند VPN، صفحات اینترانت و غیره اجازه می‌دهد تا به صورت رمزنگاری تأیید کنند که مشتریان خود واقعی هستند و با خط‌مشی شرکت مطابقت دارند. اکثر شرکت‌های بزرگ این نیاز را دارند که فقط به دستگاه‌های مدیریت‌شده سازمانی اجازه ورود به شبکه‌های WPA2 EAP-TLS، دسترسی سطح بالاتر در VPN و صفحات اینترانت متقابل TLS را بدهند. بسیاری از راه‌حل‌های موجود به بررسی‌های اکتشافی روی همان مشتری متکی هستند که ممکن است به خطر افتاده باشد. این چالشی را نشان می‌دهد که سیگنال‌هایی که برای تأیید وضعیت قانونی دستگاه به آنها تکیه می‌کنند ممکن است خودشان جعل شده باشند. این راهنما ضمانت‌های رمزنگاری مبتنی بر سخت‌افزار در مورد هویت دستگاه و اینکه وضعیت آن اصلاح نشده و مطابق با خط‌مشی در هنگام راه‌اندازی است، ارائه می‌کند. به نام دسترسی تایید شده

مخاطبان اولیه مدیران دامنه IT سازمانی
اجزای فنی ChromeOS، Google Verified Access API

پیش نیازهای دسترسی تایید شده

قبل از اجرای فرآیند دسترسی تأیید شده، تنظیمات زیر را تکمیل کنید.

API را فعال کنید

یک پروژه کنسول Google API را راه اندازی کنید و API را فعال کنید:

  1. یک پروژه موجود در کنسول Google API ایجاد یا استفاده کنید.
  2. به صفحه Enabled APIs & services بروید.
  3. Chrome Verified Access API را فعال کنید.
  4. با دنبال کردن اسناد Google Cloud API یک کلید API برای برنامه خود ایجاد کنید.

یک حساب کاربری ایجاد کنید

برای اینکه سرویس شبکه بتواند به Chrome Verified Access API دسترسی پیدا کند تا پاسخ چالش شما را تأیید کند، یک حساب سرویس و یک کلید حساب سرویس ایجاد کنید (نیازی به ایجاد پروژه Cloud جدید ندارید، می‌توانید از همان پروژه استفاده کنید).

پس از ایجاد کلید حساب سرویس، باید فایل کلید خصوصی حساب سرویس را دانلود کنید. این تنها کپی از کلید خصوصی است، بنابراین مطمئن شوید که آن را ایمن ذخیره کنید.

یک دستگاه Chromebook مدیریت شده ثبت نام کنید

برای دسترسی تأیید شده به یک راه‌اندازی دستگاه Chromebook با مدیریت صحیح با افزونه Chrome خود نیاز دارید.

  1. دستگاه Chromebook باید برای مدیریت سازمانی یا آموزشی ثبت نام کرده باشد.
  2. کاربر دستگاه باید یک کاربر ثبت شده از همان دامنه باشد.
  3. افزونه Chrome برای دسترسی تأیید شده باید در دستگاه نصب شود.
  4. خط‌مشی‌ها به گونه‌ای پیکربندی شده‌اند که «دسترسی تأییدشده» را فعال کند، افزونه Chrome را در فهرست مجاز قرار دهد، و به API برای حساب خدماتی که خدمات شبکه را نشان می‌دهد، دسترسی بدهد (به مستندات راهنمای کنسول مدیریت Google مراجعه کنید).

کاربر و دستگاه را تأیید کنید

توسعه دهندگان می توانند از دسترسی تأیید شده برای تأیید کاربر یا دستگاه استفاده کنند، یا از هر دو برای امنیت بیشتر استفاده کنند:

  • راستی‌آزمایی دستگاه —در صورت موفقیت‌آمیز، تأیید دستگاه تضمین می‌کند که دستگاه Chrome در یک دامنه مدیریت‌شده ثبت‌شده است و مطابق با خط‌مشی دستگاه حالت بوت تأییدشده که توسط سرپرست دامنه مشخص شده است، مطابقت دارد. اگر به سرویس شبکه اجازه مشاهده هویت دستگاه داده شود (به مستندات راهنمای کنسول Google Admin مراجعه کنید)، سپس یک شناسه دستگاه را نیز دریافت می‌کند که می‌تواند برای ممیزی، ردیابی یا تماس با Directory API استفاده شود.

  • راستی‌آزمایی کاربر — در صورت موفقیت‌آمیز بودن، تأیید کاربر تضمین می‌کند که کاربر واردشده به Chrome یک کاربر مدیریت‌شده است، از دستگاه ثبت‌شده استفاده می‌کند و مطابق با خط‌مشی کاربر حالت بوت تأییدشده که توسط سرپرست دامنه مشخص شده است، مطابقت دارد. اگر به سرویس شبکه اجازه دریافت داده‌های کاربر اضافی داده شود، درخواست امضای گواهی صادر شده توسط کاربر را نیز دریافت می‌کند (CSR به شکل امضا شده-کلید عمومی-و-چالش یا SPKAC، که به فرمت کیجن نیز معروف است. ).

نحوه تایید کاربر و دستگاه

  1. دریافت یک چالش — افزونه Chrome در دستگاه برای دریافت چالش با API دسترسی تأیید شده تماس می گیرد. چالش یک ساختار داده غیرشفاف (یک حباب امضا شده توسط Google) است که برای 1 دقیقه مناسب است، به این معنی که در صورت استفاده از چالش قدیمی، تأیید پاسخ چالش (مرحله 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
      }
    };
    

    کد کمکی برای رمزگذاری چالش — اگر از v1 API استفاده می‌کنید، چالش باید کدگذاری شود.

    // 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 دریافت کرده بود برای فراخوانی Enterprise.platformKeys Chrome API استفاده می کند. این یک پاسخ چالش امضا شده و رمزگذاری شده ایجاد می کند، که برنامه افزودنی آن را در درخواست دسترسی که به سرویس شبکه ارسال می کند، شامل می شود.

    در این مرحله، هیچ تلاشی برای تعریف پروتکلی وجود ندارد که برنامه افزودنی و سرویس شبکه برای برقراری ارتباط از آن استفاده کنند. هر دوی این موجودیت‌ها توسط توسعه‌دهندگان خارجی پیاده‌سازی می‌شوند و نحوه صحبت آنها با یکدیگر تجویز نمی‌شود. یک مثال می تواند ارسال یک پاسخ چالشی (با کد 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. تأیید پاسخ چالش — پس از دریافت پاسخ چالش از یک دستگاه (شاید به عنوان یک برنامه افزودنی برای یک پروتکل احراز هویت موجود)، سرویس شبکه باید با Verified Access API تماس بگیرد تا هویت دستگاه و وضعیت خط‌مشی را تأیید کند (نمونه کد زیر را ببینید). برای مبارزه با جعل، توصیه می کنیم سرویس شبکه مشتری را که با آن صحبت می کند شناسایی کند و هویت مورد انتظار مشتری را در درخواست خود درج کند:

    • برای تأیید دستگاه ، دامنه مورد انتظار دستگاه باید ارائه شود. این احتمالاً در بسیاری از موارد یک مقدار سخت کد شده است، زیرا سرویس شبکه از منابع یک دامنه خاص محافظت می کند. اگر این موضوع از قبل شناخته نشده باشد، می توان آن را از هویت کاربر استنباط کرد.
    • برای تأیید کاربر ، آدرس ایمیل کاربر مورد انتظار باید ارائه شود. ما انتظار داریم که سرویس شبکه کاربران خود را بشناسد (معمولاً لازم است کاربران وارد سیستم شوند).

    هنگامی که Google API فراخوانی می شود، تعدادی بررسی را انجام می دهد، مانند:

    • بررسی کنید که پاسخ چالش توسط ChromeOS تولید شده است و در حین حمل و نقل اصلاح نشده است
    • بررسی کنید که دستگاه یا کاربر توسط سازمانی مدیریت می شود.
    • بررسی کنید که هویت دستگاه/کاربر با هویت مورد انتظار مطابقت داشته باشد (اگر مورد دوم ارائه شده باشد).
    • بررسی کنید که چالشی که به آن پاسخ داده می‌شود تازه است (بیش از 1 دقیقه از عمر آن نمی‌گذرد).
    • بررسی کنید که دستگاه یا کاربر با خط مشی مشخص شده توسط سرپرست دامنه مطابقت دارد.
    • بررسی کنید که تماس گیرنده (سرویس شبکه) مجوز تماس با API را دارد.
    • اگر به تماس‌گیرنده اجازه دریافت داده‌های اضافی دستگاه یا کاربر داده شد، شناسه دستگاه یا درخواست امضای گواهی کاربر (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 Admin)، یک CSR امضا شده توسط کاربر دریافت می‌کند که سپس می‌تواند برای دریافت گواهی واقعی از گواهی استفاده شود. قدرت. هنگام ادغام با Microsoft CA، سرویس شبکه می تواند به عنوان یک واسطه عمل کند و از رابط ICertRequest استفاده کند.

استفاده از گواهی های مشتری با دسترسی تأیید شده

استفاده از گواهی های مشتری با دسترسی تأیید شده

در یک سازمان بزرگ، ممکن است چندین سرویس شبکه (سرورهای VPN، نقاط دسترسی Wi-Fi، فایروال ها و چندین سایت اینترانت) وجود داشته باشد که از دسترسی تأیید شده بهره مند شوند. با این حال، ایجاد منطق مراحل 2-4 (در بخش بالا) در هر یک از این خدمات شبکه ممکن است عملی نباشد. اغلب، بسیاری از این سرویس‌های شبکه از قبل این قابلیت را دارند که به گواهی‌های مشتری به عنوان بخشی از احراز هویت خود نیاز داشته باشند (به عنوان مثال، EAP-TLS یا صفحات اینترانت متقابل TLS). بنابراین اگر مرجع صدور گواهینامه سازمانی که این گواهی‌های مشتری را صادر می‌کند، بتواند مراحل 2 تا 4 را اجرا کند و صدور گواهی مشتری را مشروط به تأیید چالش-پاسخ کند، در این صورت داشتن گواهی می‌تواند دلیلی بر واقعی بودن مشتری و مطابقت آن باشد. سیاست شرکت پس از آن، هر نقطه دسترسی Wi-Fi، سرور VPN، و غیره می تواند به جای نیاز به دنبال کردن مراحل 2-4، این گواهی مشتری را بررسی کند.

به عبارت دیگر، در اینجا CA (که گواهی مشتری را برای دستگاه‌های سازمانی صادر می‌کند) نقش سرویس شبکه در شکل 1 را بر عهده می‌گیرد. باید API دسترسی تأیید شده را فراخوانی کند و تنها پس از تأیید تأیید پاسخ چالش، گواهی را به آن ارائه دهد. مشتری. ارائه گواهی به مشتری معادل مرحله 4 - Grant Access در شکل 1 است.

روند دریافت گواهینامه های مشتری به طور ایمن به Chromebook در این مقاله توضیح داده شده است. اگر از طرحی که در این پاراگراف توضیح داده شده پیروی شود، پسوند دسترسی تأیید شده و پسوند ورود گواهی کلاینت را می‌توان در یکی ترکیب کرد. درباره نحوه نوشتن افزونه ورود گواهی مشتری بیشتر بدانید.