ข้อมูลเกี่ยวกับคู่มือนี้
Chrome Verified Access API ช่วยให้บริการต่างๆ ของเครือข่าย เช่น VPN, หน้าอินทราเน็ต และอื่นๆ สามารถยืนยันแบบเข้ารหัสได้ว่าไคลเอ็นต์ของตนเป็นของแท้และเป็นไปตามนโยบายของบริษัท องค์กรขนาดใหญ่ส่วนใหญ่กำหนดให้อนุญาตเฉพาะอุปกรณ์ที่จัดการโดยองค์กรในเครือข่าย WPA2 EAP-TLS การเข้าถึง VPN ในระดับที่สูงขึ้น และหน้าอินทราเน็ตแบบ TLS ร่วมกัน โซลูชันจำนวนมากที่มีอยู่ต้องพึ่งพาการตรวจสอบระบบการเรียนรู้ในไคลเอ็นต์เดียวกันซึ่งอาจถูกบุกรุก กรณีนี้แสดงถึงความท้าทายที่ว่าอุปกรณ์อาจมีการปลอมแปลงสัญญาณที่ต้องใช้ในการรับรองสถานะที่ถูกต้องของอุปกรณ์ คู่มือนี้จะรับรองข้อมูลประจำตัวของอุปกรณ์โดยใช้ฮาร์ดแวร์ พร้อมการรับประกันสถานะอุปกรณ์โดยไม่มีการแก้ไขและเป็นไปตามนโยบายขณะเปิดเครื่อง ซึ่งเรียกว่า "การเข้าถึงที่ยืนยันแล้ว"
กลุ่มเป้าหมายหลัก | ผู้ดูแลระบบโดเมนด้านไอทีขององค์กร |
องค์ประกอบทางเทคนิค | ChromeOS, API การเข้าถึงที่ยืนยันของ Google |
ข้อกำหนดเบื้องต้นในการเข้าถึง Verified Access
โปรดตั้งค่าต่อไปนี้ให้เสร็จก่อนที่จะใช้ขั้นตอนการเข้าถึงที่ยืนยัน
เปิดใช้ 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 ในอุปกรณ์จะติดต่อกับ Verified Access 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 การดำเนินการนี้จะสร้างคำตอบสำหรับทดสอบที่ลงชื่อและเข้ารหัสไว้ ซึ่งส่วนขยายจะรวมไว้ในคำขอเข้าถึงที่ส่งไปยังบริการเครือข่าย
ในขั้นตอนนี้ คุณจะไม่ต้องกำหนดโปรโตคอลที่ส่วนขยายและบริการเครือข่ายใช้สื่อสาร ทั้งสองเอนทิตีนี้มีการใช้งานโดยนักพัฒนาซอฟต์แวร์ภายนอกและไม่ได้กำหนดไว้ว่าจะพูดคุยกันอย่างไร ตัวอย่างเช่น การส่งคำตอบทดสอบ (เข้ารหัส 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; }
ยืนยันการตอบกลับคำถาม - เมื่อได้รับการตอบกลับจากอุปกรณ์ (อาจเป็นส่วนขยายของโปรโตคอลการตรวจสอบสิทธิ์ที่มีอยู่) บริการเครือข่ายควรเรียกใช้ 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(); }
ให้สิทธิ์เข้าถึง - ขั้นตอนนี้ใช้ได้เฉพาะกับบริการเครือข่ายเท่านั้น ซึ่งเป็นการใช้งานที่แนะนำ (ไม่ได้กำหนดไว้) การดำเนินการที่เป็นไปได้มีดังนี้
- การสร้างคุกกี้เซสชัน
- การออกใบรับรองให้กับผู้ใช้หรืออุปกรณ์ ในกรณีที่ยืนยันผู้ใช้สำเร็จแล้ว และสมมติว่าบริการเครือข่ายได้รับสิทธิ์เข้าถึงข้อมูลผู้ใช้เพิ่มเติม (ผ่านนโยบายคอนโซลผู้ดูแลระบบของ Google) บริการเครือข่ายจะได้รับ CSR ที่ผู้ใช้ลงนาม ซึ่งสามารถใช้เพื่อขอรับใบรับรองจริงจากผู้ออกใบรับรอง เมื่อผสานรวมกับ MicrosoftⓇ 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 อย่างปลอดภัยในบทความนี้ หากเป็นไปตามการออกแบบที่อธิบายไว้ในย่อหน้านี้ ส่วนขยายการเข้าถึงที่ยืนยันแล้วและส่วนขยายการเริ่มต้นใช้งานใบรับรองไคลเอ็นต์รวมกันเป็นรายการเดียวได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีเขียนส่วนขยายการเริ่มต้นใช้งานใบรับรองไคลเอ็นต์