Hướng dẫn dành cho nhà phát triển về quyền truy cập được xác minh của Chrome

Giới thiệu về hướng dẫn này

API Quyền truy cập được xác minh của Chrome cho phép các dịch vụ mạng như VPN, mạng nội bộ v.v. để xác minh bằng mã hoá rằng khách hàng của họ là người thật và tuân theo chính sách của công ty. Hầu hết các doanh nghiệp lớn đều yêu cầu chỉ cho phép các thiết bị do doanh nghiệp quản lý sử dụng mạng WPA2 EAP-TLS của họ, quyền truy cập cấp cao hơn trong VPN và các trang mạng nội bộ dùng giao thức TLS (Bảo mật tầng truyền tải) lẫn nhau. Nhiều quốc gia hiện có dựa vào các phép kiểm tra suy nghiệm trên cùng một ứng dụng khách bị xâm phạm. Điều này đặt ra một thách thức là các tín hiệu đang được dựa vào chứng thực tình trạng hợp pháp của thiết bị có thể đã giả. Hướng dẫn này đưa ra sự đảm bảo về mặt mã hoá dựa trên phần cứng của thông tin nhận dạng của thiết bị và trạng thái của thiết bị đó là chưa sửa đổi và tuân thủ chính sách lúc khởi động; có tên là Quyền truy cập đã xác minh.

Đối tượng chính Quản trị viên miền CNTT dành cho doanh nghiệp
Thành phần kỹ thuật ChromeOS, API Quyền truy cập đã xác minh của Google

Điều kiện tiên quyết để có quyền truy cập đã xác minh

Hãy hoàn tất thông tin thiết lập sau đây trước khi triển khai quy trình Quyền truy cập đã xác minh.

Bật API

Thiết lập dự án bảng điều khiển API của Google và bật API:

  1. Tạo hoặc sử dụng dự án hiện có trong Bảng điều khiển API của Google.
  2. Chuyển đến API đã bật và Dịch vụ của chúng tôi.
  3. Bật API Quyền truy cập đã xác minh của Chrome.
  4. Tạo khoá API cho ứng dụng của bạn bằng cách làm theo tài liệu về Google Cloud API.

Tạo một tài khoản dịch vụ

Để dịch vụ mạng truy cập vào API Quyền truy cập được xác minh của Chrome nhằm xác minh thử thách-phản hồi, tạo tài khoản dịch vụ và khoá tài khoản dịch vụ (bạn không cần tạo dự án Cloud mới, bạn có thể sử dụng cùng một dự án).

Sau khi tạo khoá tài khoản dịch vụ, bạn sẽ có một tài khoản dịch vụ đã tải xuống tệp khóa riêng tư. Đây là bản sao duy nhất của khoá riêng tư, vì vậy hãy bạn nên lưu trữ mã đó một cách an toàn.

Đăng ký thiết bị Chromebook được quản lý

Bạn cần thiết lập thiết bị Chromebook được quản lý đúng cách với Chrome của mình tiện ích mở rộng cho Quyền truy cập đã xác minh.

  1. Thiết bị Chromebook phải được đăng ký để sử dụng tính năng quản lý doanh nghiệp hoặc tổ chức giáo dục.
  2. Người dùng thiết bị phải là người dùng đã đăng ký từ cùng một miền.
  3. Bạn phải cài đặt tiện ích của Chrome cho Quyền truy cập đã xác minh trên thiết bị.
  4. Các chính sách được định cấu hình để bật Quyền truy cập đã xác minh, cho phép Chrome tiện ích và cấp quyền truy cập vào API cho tài khoản dịch vụ đại diện cho dịch vụ mạng (xem Tài liệu trợ giúp về Bảng điều khiển dành cho quản trị viên của Google).

Xác minh người dùng và thiết bị

Nhà phát triển có thể dùng Quyền truy cập đã xác minh để xác minh người dùng hay thiết bị, hoặc dùng cả hai để tăng cường bảo mật:

  • Xác minh thiết bị—Nếu thành công, quy trình xác minh thiết bị sẽ cung cấp đảm bảo rằng thiết bị Chrome được đăng ký trong miền được quản lý và miền đó tuân thủ chính sách thiết bị của chế độ khởi động được xác minh do miền chỉ định Google Cloud. Nếu dịch vụ mạng được cấp quyền xem thiết bị (xem Tài liệu trợ giúp về Bảng điều khiển dành cho quản trị viên của Google), thì sau đó ứng dụng cũng sẽ nhận được mã thiết bị có thể dùng để kiểm tra, theo dõi hoặc gọi API Thư mục.

  • Xác minh người dùng—Nếu thành công, quy trình xác minh người dùng sẽ đảm bảo kết quả người dùng Chrome đã đăng nhập là người dùng được quản lý, đang sử dụng một thiết bị đã đăng ký và tuân thủ chính sách người dùng của chế độ khởi động được xác minh do miền chỉ định Google Cloud. Nếu dịch vụ mạng được cấp quyền nhận dữ liệu người dùng bổ sung thì cũng sẽ nhận được yêu cầu ký chứng chỉ được phát hành bởi người dùng (CSR dưới dạng khoá-công-cộng-và-thử-nghiệm có chữ ký hoặc SPKAC, còn được gọi là định dạng chương trình tạo khoá).

Cách xác minh người dùng và thiết bị

  1. Nhận thử thách – Tiện ích của Chrome trên thiết bị sẽ kết nối với Truy cập API để tải thử thách. Thách thức là một dữ liệu không rõ ràng (một blob có chữ ký của Google) hoạt động trong 1 phút, nghĩa là xác minh phản hồi thử thách (bước 3) sẽ không thành công nếu sử dụng thử thách cũ.

    Trong trường hợp sử dụng đơn giản nhất, người dùng bắt đầu quy trình này bằng cách nhấp vào nút tiện ích tạo ra (đây cũng là những gì tiện ích mẫu do Google cung cấp làm).

    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
      }
    };
    

    Mã trợ giúp để mã hoá thử thách—Nếu bạn đang sử dụng phiên bản 1 của API, thử thách sẽ cần được mã hoá.

    // 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. Tạo câu trả lời thử thách – Tiện ích của Chrome sử dụng thử thách mà tiện ích này nhận được ở bước 1 để gọi API Chrome enterprise.platformKeys. Chiến dịch này tạo một phản hồi thử thách đã ký và mã hoá mà tiện ích này có trong yêu cầu truy cập gửi đến dịch vụ mạng.

    Ở bước này, không có cố gắng xác định giao thức mà tiện ích và sử dụng dịch vụ mạng để giao tiếp. Cả hai thực thể này đều được triển khai do các nhà phát triển bên ngoài quy định và không quy định cách giao tiếp với nhau. Một ví dụ: gửi phản hồi thử thách (được mã hoá URL) dưới dạng chuỗi truy vấn của chúng tôi, bằng cách sử dụng HTTP POST hoặc sử dụng tiêu đề HTTP đặc biệt.

    Dưới đây là mã mẫu để tạo phản hồi thử thách:

    Tạo câu trả lời cho thử thách

      // 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);
      }
    

    Hàm gọi lại thách thức

      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
       };
      }
    

    Mã trợ giúp cho lượt chuyển đổi 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. Xác minh câu trả lời cho thử thách – Sau khi nhận được câu trả lời thử thách từ một thiết bị (có thể là tiện ích cho một giao thức xác thực hiện có), dịch vụ mạng sẽ gọi API truy cập đã xác minh để xác minh thiết bị danh tính và khả năng tuân thủ chính sách (xem mã ví dụ bên dưới). Để chống giả mạo, chúng tôi nên đề xuất dịch vụ mạng xác định ứng dụng khách đang trò chuyện và đưa thông tin nhận dạng dự kiến của khách hàng vào yêu cầu:

    • Để xác minh thiết bị, bạn phải cung cấp miền của thiết bị dự kiến của Google. Đây có thể là giá trị được cố định giá trị trong mã trong nhiều trường hợp vì mạng dịch vụ bảo vệ tài nguyên cho một miền cụ thể. Nếu bạn không biết thông tin này trước, có thể suy ra được từ danh tính người dùng.
    • Để xác minh người dùng, địa chỉ email của người dùng dự kiến phải là đã cung cấp. Chúng tôi kỳ vọng dịch vụ mạng hiểu rõ người dùng của mình (thông thường sẽ yêu cầu người dùng đăng nhập).

    Khi được gọi, API của Google sẽ thực hiện một số bước kiểm tra, chẳng hạn như:

    • Xác minh rằng câu trả lời cho thử thách là do ChromeOS tạo và không phải là được sửa đổi trong khi chuyển
    • Xác minh rằng thiết bị hoặc người dùng do doanh nghiệp quản lý.
    • Xác minh rằng danh tính của thiết bị/người dùng khớp với thông tin dự kiến nhận dạng (nếu có).
    • Xác minh rằng thử thách được phản hồi là mới (cách đây không quá 1 phút).
    • Xác minh rằng thiết bị hoặc người dùng tuân thủ chính sách do quản trị viên miền.
    • Xác minh rằng phương thức gọi (dịch vụ mạng) đã được cấp quyền gọi điện API.
    • Nếu phương thức gọi được cấp quyền lấy thiết bị bổ sung hoặc dữ liệu người dùng, bao gồm cả mã thiết bị hoặc chữ ký chứng chỉ của người dùng yêu cầu (CSR) trong phản hồi.

    Ví dụ này sử dụng thư viện 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. Cấp quyền truy cập – Bước này cũng dành riêng cho từng dịch vụ mạng. Đây là một cách triển khai được đề xuất (không theo quy định). Các hành động có thể thực hiện có thể là:

    • Tạo cookie phiên
    • Cấp chứng chỉ cho người dùng hoặc thiết bị. Trong trường hợp người dùng thành công xác minh và giả định rằng dịch vụ mạng đã được cấp quyền truy cập vào dữ liệu người dùng bổ sung (thông qua chính sách Bảng điều khiển dành cho quản trị viên của Google), nhận được CSR do người dùng ký, sau đó có thể được dùng để lấy dữ liệu chứng chỉ từ tổ chức phát hành chứng nhận. Khi tích hợp với MicrosoftR CA, thì dịch vụ mạng có thể đóng vai trò là một bên trung gian và sử dụng giao diện ICertRequest.

Sử dụng chứng chỉ ứng dụng khách với Quyền truy cập đã xác minh

Sử dụng chứng chỉ ứng dụng khách với Quyền truy cập đã xác minh.

Trong một tổ chức lớn, có thể có nhiều dịch vụ mạng (Máy chủ VPN, điểm truy cập Wi-Fi, tường lửa và nhiều trang web mạng nội bộ) sẽ hưởng lợi từ Quyền truy cập đã xác minh. Tuy nhiên, việc xây dựng logic của các bước 2–4 (trong phần trên) trong mỗi dịch vụ mạng này có thể không được chấp nhận thiết thực. Thông thường, nhiều dịch vụ trong số các dịch vụ mạng này đã có khả năng yêu cầu chứng chỉ máy khách trong quá trình xác thực (ví dụ: EAP-TLS hoặc các trang mạng nội bộ TLS tương hỗ). Nếu Chứng chỉ doanh nghiệp Cơ quan cấp phát các chứng chỉ ứng dụng này có thể triển khai các bước 2–4 và điều kiện việc cấp chứng chỉ máy khách trên phản hồi thử thách xác minh thì việc sở hữu chứng chỉ có thể là bằng chứng cho thấy khách hàng là thật và tuân thủ chính sách của công ty. Sau đó, mỗi mạng Wi-Fi điểm truy cập, máy chủ VPN, v.v. có thể kiểm tra chứng chỉ máy khách này thay vì cần làm theo các bước từ 2 đến 4.

Nói cách khác, đây là CA (cấp chứng chỉ máy khách cho các doanh nghiệp thiết bị) đóng vai trò là Dịch vụ mạng trong Hình 1. Hàm này cần gọi API Quyền truy cập đã xác minh, và chỉ khi xác minh phản hồi thử thách truyền, cung cấp chứng chỉ cho ứng dụng khách. Cung cấp chứng chỉ cho ứng dụng tương đương với Bước 4 – Cấp quyền truy cập trong Hình 1.

Mô tả quy trình gửi chứng chỉ máy khách một cách an toàn cho Chromebook trong bài viết này. Nếu tuân theo thiết kế được mô tả trong đoạn này, sau đó là Tiện ích truy cập đã xác minh và Tiện ích làm quen với chứng chỉ ứng dụng khách có thể được kết hợp thành một. Tìm hiểu thêm về cách viết tiện ích giới thiệu chứng chỉ ứng dụng khách.