המדריך למפתחים של 'גישה מאומתת של Chrome'

מידע על המדריך הזה

Chrome Verified Access API מאפשר להשתמש בשירותי רשת כמו רשתות VPN ואינטראנט וכן הלאה כדי לאמת באופן קריפטוגרפי שהלקוחות שלהם אמיתיים לציית למדיניות החברה. רוב הארגונים הגדולים צריכים לאפשר רק למכשירים שמנוהלים על ידי הארגון לרשתות WPA2 EAP-TLS שלהם, גישה ברמה גבוהה יותר ברשתות VPN ודפי אינטראנט הדדיים. הרבה לקוחות קיימים הפתרונות מסתמכים על בדיקות היוריסטיקה על אותו לקוח שייתכן בסיכון. זהו האתגר שעליו מסתמכים האותות זהו את הסטטוס הלגיטימי של המכשיר, אם הוא הוא מזויף. במדריך הזה ניתנות אחריות קריפטוגרפית בגיבוי חומרה זהות המכשיר ושהמצב שלו לא השתנה ועומד בדרישות המדיניות בזמן האתחול; שנקרא 'גישה מאומתת'.

קהל ראשי מנהלי דומיין ב-IT לארגונים
רכיבים טכניים ChromeOS, Google Verified Access API

דרישות מוקדמות לגישה מאומתת

צריך להשלים את ההגדרה הבאה לפני שמטמיעים את תהליך הגישה המאומתת.

הפעלת ה-API

מגדירים פרויקט במסוף Google API ומפעילים את ה-API:

  1. ליצור או להשתמש בפרויקט קיים מסוף Google API.
  2. נכנסים אל ממשקי API מופעלים שירותים.
  3. מפעילים את Chrome Verified Access API.
  4. יוצרים מפתח API לאפליקציה בעזרת מאמרי העזרה של Google Cloud API.

יצירה של חשבון שירות

כדי ששירות הרשת יוכל לגשת ל-Chrome Verified Access API כדי לאמת את אתגר-תגובה, יצירת חשבון שירות ומפתח לחשבון שירות (לא צריך ליצור פרויקט חדש ב-Cloud, אפשר להשתמש באותו הפרויקט).

אחרי שיוצרים את המפתח של חשבון השירות, צריך להיות לכם חשבון שירות קובץ המפתח הפרטי ירד. זה העותק היחיד של המפתח הפרטי, אז צריך ליצור יש לאחסן אותם באופן מאובטח.

רישום של מכשיר Chromebook מנוהל

צריך להגדיר מכשיר Chromebook שמנוהל כראוי באמצעות Chrome עבור 'גישה מאומתת'.

  1. מכשיר ה-Chromebook חייב להיות רשום לניהול ארגוני או חינוכי.
  2. המשתמש במכשיר חייב להיות משתמש רשום מאותו דומיין.
  3. חובה להתקין במכשיר את תוסף Chrome ל'גישה מאומתת'.
  4. כללי המדיניות מוגדרים להפעלה של 'גישה מאומתת', ולהוסיף את Chrome לרשימת ההיתרים ולתת גישה ל-API עבור חשבון השירות שמייצג בשירות הרשת (עיינו בקטע מסמכי עזרה של מסוף Google Admin).

אימות המשתמש והמכשיר

המפתחים יכולים להשתמש ב'גישה מאומתת' לאימות משתמשים או מכשירים, או להשתמש בשניהם כאמצעי אבטחה נוסף:

  • אימות המכשיר – אם הפעולה בוצעה ללא שגיאות, אימות המכשיר מספק להבטיח שמכשיר Chrome רשום בדומיין מנוהל, וש תואם למדיניות המכשיר במצב הפעלה מאומתת כפי שצוין בדומיין מנהל מערכת. אם לשירות הרשת ניתנה הרשאה לראות את המכשיר (ניתן לעיין במסמכי העזרה של מסוף Google Admin), ולאחר מכן היא מקבלת גם מזהה מכשיר שיכול לשמש לביקורת ולמעקב או לקריאה ל-Directory API.

  • אימות משתמש – אם הפעולה מסתיימת בהצלחה, אימות המשתמש מבטיח שמשתמש Chrome שמחובר לחשבון הוא משתמש מנוהל ומשתמש במכשיר רשום תואם למדיניות המשתמש בנושא מצב הפעלה מאומתת כפי שצוין בדומיין מנהל מערכת. אם לשירות הרשת ניתנה הרשאה לקבל נתוני משתמש נוספים, הוא גם ישיג בקשה לחתימה על אישור על ידי המשתמש (CSR בפורמט schema-public-key-and-challenge או SPKAC, נקרא גם פורמט מפתחות).

איך מאמתים משתמש ומכשיר

  1. אתגר – התוסף ל-Chrome במכשיר יוצר קשר עם צוות 'אומת' גישה ל-API כדי לקבל אתגר. האתגר הוא נתונים עמומים (blob בחתימה של 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 של ה-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 ושהיא לא שונה בזמן ההעברה
    • מוודאים שהמכשיר או המשתמש מנוהלים על ידי הארגון.
    • צריך לוודא שהזהות של המכשיר או המשתמש תואמת לאימות המצופה זהות (אם סיפקת את הפרטים השנייה).
    • מוודאים שהאתגר שמקבלים אליו תשובות רעננות (לפני יותר מדקה).
    • אימות שהמכשיר או המשתמש עומדים בדרישות המדיניות שצוינו ב- מנהל הדומיין.
    • מוודאים שהמתקשר (שירות רשת) קיבל הרשאה להתקשר ממשק ה-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. הענקת גישה – השלב הזה ספציפי גם לשירות הרשת. זהו יישום מוצע (לא מומלץ). הפעולות האפשריות יכולות להיות:

    • יצירת קובץ cookie של סשן
    • הנפקת אישור עבור המשתמש או המכשיר. אם המשתמש הצליח ובהנחה ששירות הרשת קיבל גישה לנתוני משתמשים נוספים (באמצעות המדיניות של מסוף Google Admin), מקבל CSR חתום על ידי משתמש, שניתן להשתמש בו כדי לקבל מרשות האישורים. בשילוב עם MicrosoftR CA, שירות הרשת יכול לפעול בתור תיווך ולהשתמש בממשק ICertRequest.

שימוש באישורי לקוח עם 'גישה מאומתת'

שימוש באישורי לקוח עם 'גישה מאומתת'.

בארגון גדול יכולים להיות כמה שירותי רשתות (שרתי VPN, נקודות גישה ל-Wi-Fi, חומות אש ואתרי אינטראנט מרובים) יפיקו תועלת מ'גישה מאומתת'. אבל יצירת הלוגיקה של השלבים 2-4 (בקטע שלמעלה) בכל אחד משירותי הרשת האלה לא יכולים להיות פרקטי. לעיתים קרובות, רבים משירותי הרשת האלה כבר יכולים לדרוש אישורי לקוח כחלק מהאימות שלהם (לדוגמה, EAP-TLS או דפי אינטראנט TLS הדדיים). אז אם אישור הארגון הרשות שמנפיקה את אישורי הלקוח האלה יכולה ליישם את שלבים 2 עד 4. תנאי להנפקת אישור הלקוח בתגובה לאתגר אימות, החזקה של האישור יכולה להיות הוכחה הלקוח אמיתי ומציית למדיניות החברה. לאחר מכן כל חיבור Wi-Fi נקודת גישה, שרת VPN וכן הלאה יכולים לבדוק אם יש אישור לקוח זה במקום לבצע את שלבים 2 עד 4.

במילים אחרות, כאן ה-CA (שמנפיק את אישור הלקוח לארגון מכשירים) בתפקיד של שירות הרשת, כפי שמתואר באיור 1. הוא צריך להפעיל את Verified Access API, ורק אחרי שמאמתים את התשובה לאתגר עליך לספק את האישור ללקוח. מתן האישור ל- הלקוח הוא המקביל לשלב 4 – הענקת גישה באיור 1.

מתואר התהליך של קבלת אישורי לקוח באופן מאובטח ב-Chromebook. במאמר הזה. אם העיצוב שמתואר בפסקה הזו מופיע ואז תוסף הגישה המאומתת אפשר לשלב תוסף אחד של קליטת אישור לקוח. מידע נוסף איך לכתוב תוסף לתחילת השימוש באישור לקוח