이 가이드에 대한 정보
Chrome Verified Access API를 사용하면 VPN, 인트라넷과 같은 네트워크 서비스를 사용할 수 있습니다. 해당 클라이언트의 클라이언트가 신뢰할 수 있고 회사 정책을 준수해야 합니다. 대부분의 대기업은 엔터프라이즈 관리 기기만 WPA2 EAP-TLS 네트워크에 허용 상호 TLS 인트라넷 페이지의 상위 계층 액세스 많은 기존 동일한 클라이언트에 대한 휴리스틱 검사에 의존하는데 침해당했습니다. 이것은 신호가 사용되는 신호가 그 자체로 장치의 적법한 상태를 증명하는 것이 있습니다. 이 가이드에서는 수정되지 않았고 정책을 준수하는 기기 상태임을 확인 부팅 시 인증 액세스라고 합니다
기본 잠재고객 | 기업 IT 도메인 관리자 |
기술 구성요소 | ChromeOS, Google Verified Access API |
인증 액세스 기본 요건
인증 액세스 절차를 구현하기 전에 다음 설정을 완료하세요.
API 사용 설정
Google API 콘솔 프로젝트를 설정하고 API를 사용 설정합니다.
- 다음에서 프로젝트를 만들거나 기존 프로젝트 사용: Google API 콘솔.
- 다음으로 이동: 사용 설정된 API 및 서비스 페이지를 참조하세요.
- Chrome Verified Access API를 사용 설정합니다.
- Google Cloud API 문서에 따라 애플리케이션의 API 키를 만듭니다.
서비스 계정 만들기
네트워크 서비스에서 Chrome Verified Access API에 액세스하여 챌린지-응답, 서비스 계정 및 서비스 계정 키 만들기 (새 Cloud 프로젝트를 만들 필요가 없으며, 같은 프로젝트를 사용해도 됩니다.)
서비스 계정 키를 만들면 서비스 계정을 갖게 됩니다. 비공개 키 파일이 다운로드되었습니다. 이것은 비공개 키의 유일한 사본이므로 안전하게 저장해야 합니다
관리 Chromebook 기기 등록하기
Chrome에 제대로 관리되는 Chromebook 기기 설정이 필요합니다. 확장 프로그램을 사용할 수 있습니다.
- Chromebook 기기가 기업 또는 교육 관리에 등록되어 있어야 합니다.
- 기기 사용자는 동일한 도메인에 등록된 사용자여야 합니다.
- 인증 액세스를 위한 Chrome 확장 프로그램이 기기에 설치되어 있어야 합니다.
- 인증 액세스를 사용 설정하고 Chrome을 허용 목록에 추가하도록 정책이 구성됩니다. 확장 프로그램을 만들고, 사용자를 나타내는 서비스 계정에 네트워크 서비스( Google 관리 콘솔 도움말 문서).
사용자 및 기기 확인
개발자는 사용자 인증 또는 기기 인증에 인증 액세스를 사용하거나 두 가지를 모두 사용할 수 있습니다. 다음과 같이 보안을 강화합니다.
기기 확인: 성공하면 기기 확인에서 Chrome 기기가 관리 도메인에 등록되고 도메인에서 지정한 자체 검사 부팅 모드 기기 정책을 준수해야 합니다. 관리자에게 문의하세요 네트워크 서비스에 기기를 볼 수 있는 권한이 부여된 경우 Google 관리 콘솔 도움말 문서 참조)를 받으면 Directory API를 감사, 추적, 호출하는 데 사용할 수 있는 기기 ID입니다.
사용자 확인: 성공하면 사용자 인증이 보장됩니다. 로그인한 Chrome 사용자가 관리 사용자이고 등록된 기기를 사용 중인지 확인 도메인에서 지정한 자체 검사 부팅 모드 사용자 정책을 준수해야 합니다. 관리자에게 문의하세요 네트워크 서비스에 추가 사용자 데이터를 확보하면 발급된 인증서 서명 요청도 사용자(서명된 공개 키와 챌린지(SPKAC) 형식의 CSR) 키 생성 형식이라고도 함).
사용자 및 기기 확인 방법
도전과제 받기: 기기의 Chrome 확장 프로그램에서 챌린지를 가져오려면 API에 액세스합니다. 문제는 불명확한 데이터가 (Google이 서명한 blob)의 경우 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 } };
챌린지를 인코딩하는 도우미 코드: API v1을 사용하는 경우 인코딩해야 합니다.
// 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 확장 프로그램에서 본인 확인 요청을 사용합니다. enterprise.platformKeys Chrome API를 호출하기 위해 필요합니다. 이 서명되고 암호화된 챌린지 응답을 생성하는데, 이는 확장 프로그램이 네트워크 서비스에 보내는 액세스 요청에 포함됩니다.
이 단계에서는 확장 프로그램과 네트워크 서비스 사용을 제한해야 합니다 이러한 두 항목 모두 상호 작용하는 방식이 규정되지 않았습니다. (URL로 인코딩된) 챌린지 응답을 쿼리 문자열로 전송하는 것을 예로 들 수 있습니다 HTTP POST 또는 특수 HTTP 헤더를 사용하여 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를 호출하여 기기를 확인해야 합니다. ID 및 정책 상태 (아래 예시 코드 참고) Google은 스푸핑을 방지하기 위해 네트워크 서비스가 대화 중인 클라이언트를 식별하도록 권장하고 요청에 클라이언트의 예상 ID를 포함합니다.
- 기기 인증의 경우 예상 기기 도메인을 제공해야 합니다. 에서 자세한 내용을 확인하실 수 있습니다. 이 값은 하드 코딩된 값일 가능성이 높습니다. 특정 도메인의 리소스를 보호하는 서비스입니다 이를 알 수 없는 경우 사용자 ID로부터 추론할 수 있습니다.
- 사용자 인증의 경우 예상 사용자의 이메일 주소는 다음과 같습니다. 확인할 수 있습니다 네트워크 서비스가 사용자 (일반적으로 사용자는 로그인해야 함).
Google API가 호출되면 다음과 같은 여러 검사를 수행합니다.
- 본인 확인 요청이 ChromeOS에서 생성된 것이 아니라 전송 중 수정됨
- 기기 또는 사용자가 기업에서 관리되는지 확인합니다.
- 기기/사용자의 ID가 예상과 일치하는지 확인합니다. ID를 입력합니다 (후자가 제공된 경우).
- 응답 중인 챌린지가 최신 상태 (1분 이내)여야 합니다.
- 기기 또는 사용자가 도메인 관리자에게 문의하세요.
- 호출자 (네트워크 서비스)에 호출 권한이 부여되었는지 확인 생성합니다.
- 호출자가 추가 기기를 가져올 수 있는 권한을 부여받았거나 사용자 데이터(기기 ID 또는 사용자 인증서 서명 포함) 요청 (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 인터페이스를 활용합니다.
인증 액세스에 클라이언트 인증서 사용
<ph type="x-smartling-placeholder">대규모 조직에는 여러 네트워크 서비스가 있을 수 있습니다. (VPN 서버, Wi-Fi 액세스 포인트, 방화벽 및 다중 인트라넷 사이트)이 연결되지 않습니다. 인증 액세스가 유용할 것입니다. 하지만 단계 논리를 빌드하면 각 네트워크 서비스에서 2~4번 (위 섹션)은 실용적이라고 합시다. 종종 이러한 네트워크 서비스 중 다수는 이미 네트워크에 있는 인증 과정에서 클라이언트 인증서를 요구합니다 (예: EAP-TLS 또는 상호 TLS 인트라넷 페이지). 따라서 기업 인증서가 이러한 클라이언트 인증서를 발급하는 기관은 2~4단계를 구현할 수 있고 챌린지 응답에서 클라이언트 인증서 발급 조건 증거가 될 수 있는 경우, 인증서를 소유한다는 것이 정직하고 기업 정책을 준수합니다. 이후 각 Wi-Fi에 액세스 포인트, VPN 서버 등은 이 클라이언트 인증서가 2~4단계를 따를 필요가 없습니다.
즉, 여기서 CA (클라이언트 인증서를 기업에 발급하는 그림 1에서 네트워크 서비스의 역할을 맡습니다. Kubernetes는 Verified Access API, 챌린지 응답 확인 시에만 클라이언트에 인증서를 제공합니다 인증서 제공 클라이언트는 그림 1의 4단계 - 액세스 권한 부여와 동일합니다.
클라이언트 인증서를 Chromebook으로 안전하게 가져오는 프로세스 설명 이 도움말을 참조하세요. 만약 이 단락에 설명된 설계를 따른 다음 인증 액세스 확장 프로그램을 클라이언트 인증서 온보딩 확장을 하나로 결합할 수 있습니다. 자세히 알아보기 클라이언트 인증서 온보딩 확장 프로그램 작성 방법 자세히 알아보기