ข้อมูลเกี่ยวกับคู่มือนี้
Chrome Verified Access API อนุญาตให้ใช้บริการเครือข่าย เช่น VPN และอินทราเน็ต หน้าเว็บ และอื่นๆ ด้วยการเข้ารหัสเพื่อยืนยันว่าไคลเอ็นต์เป็นของแท้และ สอดคล้องกับนโยบายของบริษัท องค์กรขนาดใหญ่ส่วนใหญ่มีข้อกำหนด อนุญาตเฉพาะอุปกรณ์ที่จัดการโดยองค์กรเข้าสู่เครือข่าย WPA2 EAP-TLS การเข้าถึงระดับสูงขึ้นใน VPN และหน้าอินทราเน็ตแบบ TLS ร่วมกัน หลายรายการที่มีอยู่ โซลูชันจะอาศัยการตรวจสอบการเรียนรู้ด้วยไคลเอ็นต์เดียวกัน ถูกบุกรุก ซึ่งก่อให้เกิดความท้าทายที่ต้องอาศัยสัญญาณต่างๆ ยืนยันสถานะที่ถูกต้องของอุปกรณ์ ผ่านการปลอมแปลง คู่มือนี้ให้การรับประกันการเข้ารหัสลับโดยใช้ฮาร์ดแวร์ ตัวตนของอุปกรณ์และสถานะของอุปกรณ์ไม่มีการแก้ไขและเป็นไปตามนโยบาย ขณะเปิดเครื่อง ซึ่งเรียกว่า "การเข้าถึงที่ยืนยัน"
กลุ่มเป้าหมายหลัก | ผู้ดูแลระบบโดเมนไอทีขององค์กร |
องค์ประกอบทางเทคนิค | ChromeOS, Google Verified Access API |
ข้อกำหนดเบื้องต้นของการเข้าถึงที่ยืนยันแล้ว
ทำการตั้งค่าต่อไปนี้ให้เสร็จสิ้นก่อนใช้กระบวนการการเข้าถึงที่ยืนยันแล้ว
เปิดใช้ API
ตั้งค่าโครงการคอนโซล Google API และเปิดใช้ API โดยทำดังนี้
- สร้างหรือใช้โปรเจ็กต์ที่มีอยู่ใน คอนโซล Google API
- ไปที่หน้า API ที่เปิดใช้และ บริการ
- เปิดใช้ Chrome Verified Access API
- สร้างคีย์ API สำหรับแอปพลิเคชันโดยทำตามเอกสารประกอบของ Google Cloud API
สร้างบัญชีบริการ
สำหรับบริการเครือข่ายในการเข้าถึง Chrome Verified Access API เพื่อยืนยัน การตอบสนองต่อชาเลนจ์ สร้างบัญชีบริการและคีย์บัญชีบริการ (ไม่จำเป็นต้องสร้างโปรเจ็กต์ระบบคลาวด์ใหม่ ใช้โปรเจ็กต์เดิมก็ได้)
เมื่อสร้างคีย์บัญชีบริการแล้ว คุณควรมีบัญชีบริการ ดาวน์โหลดไฟล์คีย์ส่วนตัวแล้ว นี่เป็นเพียงสำเนาเดียวของคีย์ส่วนตัว ดังนั้นให้ โปรดเก็บไว้อย่างปลอดภัย
ลงทะเบียนอุปกรณ์ Chromebook ที่มีการจัดการ
คุณต้องตั้งค่าอุปกรณ์ Chromebook ที่มีการจัดการอย่างเหมาะสมด้วย Chrome ส่วนขยายสำหรับการเข้าถึงที่ยืนยัน
- อุปกรณ์ Chromebook ต้องลงทะเบียนสำหรับการจัดการขององค์กรหรือการศึกษา
- ผู้ใช้อุปกรณ์ต้องเป็นผู้ใช้ที่ลงทะเบียนจากโดเมนเดียวกัน
- ต้องติดตั้งส่วนขยาย Chrome สำหรับการเข้าถึงที่ยืนยันแล้วในอุปกรณ์
- นโยบายได้รับการกำหนดค่าเพื่อเปิดใช้การเข้าถึงที่ได้รับการยืนยัน เพิ่ม Chrome ลงในรายการที่อนุญาต ส่วนขยาย และให้สิทธิ์เข้าถึง API สำหรับบัญชีบริการที่เป็นตัวแทน บริการเครือข่าย (โปรดดู เอกสารความช่วยเหลือของคอนโซลผู้ดูแลระบบของ Google)
ยืนยันผู้ใช้และอุปกรณ์
นักพัฒนาแอปสามารถใช้การเข้าถึงที่ผ่านการยืนยันสำหรับการยืนยันผู้ใช้หรืออุปกรณ์ หรือใช้ทั้ง 2 อย่าง เพื่อเพิ่มความปลอดภัย:
การยืนยันอุปกรณ์ - หากการยืนยันอุปกรณ์สำเร็จ จะมี ให้รับประกันว่าอุปกรณ์ Chrome ได้รับการลงทะเบียนในโดเมนที่มีการจัดการ เป็นไปตามนโยบายอุปกรณ์สำหรับโหมดการเปิดเครื่องที่ได้รับการยืนยันตามที่โดเมนระบุ ผู้ดูแลระบบ หากบริการเครือข่ายได้รับสิทธิ์ในการดูอุปกรณ์ (ดูเอกสารความช่วยเหลือของคอนโซลผู้ดูแลระบบของ Google) และได้รับ รหัสอุปกรณ์ที่สามารถใช้ในการตรวจสอบ ติดตาม หรือเรียกใช้ Directory API
การยืนยันผู้ใช้ - หากการยืนยันผู้ใช้สำเร็จ การยืนยันผู้ใช้จะเป็นการรับประกัน ผู้ใช้ Chrome ที่ลงชื่อเข้าใช้คือผู้ใช้ที่มีการจัดการ ใช้อุปกรณ์ที่ลงทะเบียน และ เป็นไปตามนโยบายผู้ใช้โหมดการเปิดเครื่องที่ได้รับการยืนยันตามที่โดเมนระบุ ผู้ดูแลระบบ หากบริการเครือข่ายได้รับสิทธิ์ให้รับ ข้อมูลผู้ใช้เพิ่มเติม ก็จะได้รับคำขอเซ็นชื่อในใบรับรองที่ออกด้วย โดยผู้ใช้ (CSR ในรูปแบบของบริการจัดการคีย์สาธารณะที่ลงนาม หรือ SPKAC หรือที่เรียกว่ารูปแบบคีย์เจน)
วิธียืนยันผู้ใช้และอุปกรณ์
รับคำท้า - ส่วนขยาย Chrome ในอุปกรณ์จะติดต่อกับ "ยืนยันแล้ว" เข้าถึง API เพื่อรับความท้าทาย ปัญหาคือข้อมูลไม่ชัดเจน (BLOB ที่ 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); } };
สร้างคำท้า - ส่วนขยาย Chrome จะใช้คำท้า ได้รับในขั้นตอนที่ 1 เพื่อเรียกใช้ enterprise.platformKeys Chrome API ช่วงเวลานี้ จะสร้างการตอบกลับคำท้าที่มีการลงชื่อและเข้ารหัส ในคำขอสิทธิ์เข้าถึงที่ส่งไปยังบริการเครือข่าย
ในขั้นตอนนี้ คุณต้องไม่พยายามกำหนดโปรโตคอลที่ส่วนขยายและ การใช้บริการเครือข่ายในการสื่อสาร มีการติดตั้งใช้งานทั้ง 2 เอนทิตีนี้ โดยนักพัฒนาซอฟต์แวร์ภายนอกและไม่ได้กำหนดแนวทางการสื่อสารระหว่างกัน CANNOT TRANSLATE ตัวอย่างจะส่งการตอบกลับชาเลนจ์ (เข้ารหัส 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); }
ฟังก์ชัน Callback ที่ท้าทาย
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; }
ยืนยันการตอบกลับคำท้า - เมื่อได้รับการตอบรับคำท้าจาก (อาจเป็นส่วนขยายของโปรโตคอลการตรวจสอบสิทธิ์ที่มีอยู่) บริการเครือข่ายควรเรียกใช้ Verified Access API เพื่อยืนยันอุปกรณ์ ระดับตัวตนและนโยบาย (ดูโค้ดตัวอย่างด้านล่าง) เพื่อต่อสู้กับการปลอมแปลง เรา แนะนำให้บริการเครือข่ายระบุไคลเอ็นต์ที่กำลังคุยด้วยและ ระบุข้อมูลประจำตัวที่คาดไว้ของลูกค้าในคำขอ
- สำหรับการยืนยันอุปกรณ์ ควรระบุโดเมนอุปกรณ์ที่คาดไว้ ที่ใช้เวลาเพียง 2 นาที ซึ่งมักจะเป็นค่าที่มีการฮาร์ดโค้ดในหลายกรณี เนื่องจากเครือข่าย บริการปกป้องทรัพยากรสำหรับโดเมนหนึ่งๆ หากไม่ทราบ อาจสรุปได้จากข้อมูลระบุตัวตนของผู้ใช้
- สำหรับการยืนยันผู้ใช้ อีเมลของผู้ใช้ที่คาดไว้ควรเป็น ที่มีให้ เราคาดหวังว่าบริการเครือข่ายจะรู้จักผู้ใช้ของตน (โดยปกติ กำหนดให้ผู้ใช้ลงชื่อเข้าใช้)
เมื่อมีการเรียก 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(); }
ให้สิทธิ์เข้าถึง - ขั้นตอนนี้ใช้ได้เฉพาะกับบริการเครือข่ายโดยเฉพาะ นี่คือ การใช้งานที่แนะนำ (ไม่ได้กำหนด) การดำเนินการที่เป็นไปได้มีดังนี้
- การสร้างคุกกี้เซสชัน
- การออกใบรับรองให้ผู้ใช้หรืออุปกรณ์ ในกรณีที่ผู้ใช้ดำเนินการสำเร็จ ยืนยัน และสมมติว่าบริการเครือข่ายมีสิทธิ์เข้าถึง ข้อมูลผู้ใช้เพิ่มเติม (ผ่านทางนโยบายคอนโซลผู้ดูแลระบบของ Google) ได้รับ CSR ที่ผู้ใช้ลงนาม ซึ่งสามารถนำไปใช้เพื่อรับ ใบรับรองจากหน่วยงานที่รับรอง เมื่อผสานรวมกับ MicrosoftR CA บริการเครือข่ายอาจทำหน้าที่เป็น ตัวกลาง และใช้อินเทอร์เฟซ ICertRequest
การใช้ใบรับรองไคลเอ็นต์กับการเข้าถึงที่ยืนยันแล้ว
ในองค์กรขนาดใหญ่ อาจมีบริการเครือข่ายหลายอย่าง (เซิร์ฟเวอร์ VPN, จุดเข้าใช้งาน Wi-Fi, ไฟร์วอลล์ และไซต์อินทราเน็ตหลายแห่ง) ที่ จะได้รับประโยชน์จากการเข้าถึงที่ผ่านการยืนยัน แต่การสร้างตรรกะของขั้นตอน 2-4 (ในส่วนด้านบน) ในบริการเครือข่ายเหล่านี้แต่ละรายการอาจไม่ได้ ใช้ได้จริง บ่อยครั้งที่บริการเครือข่ายจำนวนมากเหล่านี้สามารถ ต้องมีใบรับรองไคลเอ็นต์เป็นส่วนหนึ่งของการตรวจสอบสิทธิ์ (เช่น EAP-TLS หรือหน้าอินทราเน็ต TLS ร่วม) ดังนั้นถ้าใบรับรอง Enterprise หน่วยงานที่ออกใบรับรองไคลเอ็นต์เหล่านี้สามารถใช้ขั้นตอนที่ 2-4 และ การกำหนดเงื่อนไขการออกใบรับรองไคลเอ็นต์ในการตอบ การมีใบรับรองก็สามารถเป็นหลักฐานว่า ลูกค้ามีความจริงใจและเป็นไปตามนโยบายขององค์กร หลังจากนั้น Wi-Fi แต่ละครั้ง จุดเข้าใช้งาน, เซิร์ฟเวอร์ VPN และอื่นๆ สามารถตรวจสอบใบรับรองไคลเอ็นต์นี้ได้ แทนที่จะทำตามขั้นตอนที่ 2-4
กล่าวคือ CA (ที่ออกใบรับรองไคลเอ็นต์ให้กับองค์กร อุปกรณ์) จะมีบทบาทเป็นบริการเครือข่ายในรูปที่ 1 โดยจะต้องเรียกใช้ Verified Access API และ เฉพาะเมื่อมีการยืนยันคำตอบของคำถาม ผ่าน ให้ส่งใบรับรองแก่ไคลเอ็นต์ ส่งใบรับรองให้กับ ไคลเอ็นต์นั้นเทียบเท่ากับขั้นตอนที่ 4 - การให้สิทธิ์ ในรูปที่ 1
เราจะอธิบายกระบวนการรับใบรับรองไคลเอ็นต์ไปยัง Chromebook อย่างปลอดภัย ในบทความนี้ หาก ตามด้วยการออกแบบที่อธิบายในย่อหน้านี้ ตามด้วยส่วนขยายการเข้าถึงที่ยืนยัน และส่วนขยายการเริ่มต้นใช้งานใบรับรองไคลเอ็นต์จะรวมกันเป็นรายการเดียวได้ ดูข้อมูลเพิ่มเติม เกี่ยวกับวิธีเขียนส่วนขยายการเริ่มต้นใช้งานใบรับรองไคลเอ็นต์