ปกป้องบัญชีผู้ใช้ด้วยการป้องกันแบบครอบคลุมหลายบริการ

หากแอปของคุณอนุญาตให้ผู้ใช้ลงชื่อเข้าใช้บัญชีโดยใช้ Google คุณสามารถเพิ่มความปลอดภัยของบัญชีผู้ใช้ที่แชร์เหล่านี้ได้โดยการฟังและตอบสนองต่อการแจ้งเตือนเหตุการณ์ด้านความปลอดภัยที่ให้บริการโดยบริการการป้องกันแบบครอบคลุมหลายบริการ

การแจ้งเตือนเหล่านี้จะแจ้งให้คุณทราบถึงการเปลี่ยนแปลงครั้งใหญ่ในบัญชี Google ของผู้ใช้ ซึ่งมักจะส่งผลต่อความปลอดภัยของบัญชีของผู้ใช้ในแอปของคุณด้วย ตัวอย่างเช่น หากบัญชี Google ของผู้ใช้ถูกลักลอบใช้ ก็อาจทำให้บัญชีของผู้ใช้ในแอปของคุณถูกบุกรุกผ่านการกู้คืนบัญชีอีเมลหรือการใช้การลงชื่อเพียงครั้งเดียว

Google จะส่งออบเจ็กต์บริการที่เรียกว่าโทเค็นการดำเนินการด้านความปลอดภัยให้คุณเพื่อช่วยลดความเสี่ยงที่อาจเกิดขึ้นจากเหตุการณ์ดังกล่าว โทเค็นเหล่านี้จะแสดงข้อมูลเพียงเล็กน้อยเท่านั้น ซึ่งได้แก่ ประเภทของการดำเนินการด้านความปลอดภัยและเวลาที่เกิดเหตุการณ์ รวมถึงตัวระบุของผู้ใช้ที่ได้รับผลกระทบ แต่คุณสามารถใช้โทเค็นเหล่านี้เพื่อดำเนินการตอบสนองที่เหมาะสมได้ ตัวอย่างเช่น หากบัญชี Google ของผู้ใช้ถูกบุกรุก คุณสามารถปิดใช้ฟีเจอร์ลงชื่อเข้าใช้ด้วย Google สำหรับผู้ใช้รายนั้นชั่วคราว และป้องกันไม่ให้ระบบส่งอีเมลสำหรับการกู้คืนบัญชีไปยังอีเมล Gmail ของผู้ใช้

การป้องกันแบบครอบคลุมหลายบริการอิงตามมาตรฐาน RISC ซึ่งพัฒนาขึ้นที่มูลนิธิ OpenID

ภาพรวม

หากต้องการใช้การป้องกันแบบครอบคลุมหลายบริการกับแอปหรือบริการ คุณต้องดำเนินการต่อไปนี้ให้เสร็จสมบูรณ์

  1. ตั้งค่าโปรเจ็กต์ในคอนโซล API

  2. สร้างปลายทางของผู้รับเหตุการณ์ที่ Google จะส่งโทเค็นการดำเนินการด้านความปลอดภัยไปให้ ปลายทางนี้มีหน้าที่ตรวจสอบโทเค็นที่ได้รับ แล้วตอบสนองต่อการดำเนินการด้านความปลอดภัยในแบบที่คุณเลือก

  3. ลงทะเบียนปลายทางกับ Google เพื่อเริ่มรับโทเค็นการดำเนินการด้านความปลอดภัย

วิชาบังคับก่อน

คุณจะได้รับโทเค็นการดำเนินการด้านความปลอดภัยสำหรับผู้ใช้ Google ที่ให้สิทธิ์บริการของคุณเข้าถึงข้อมูลโปรไฟล์หรืออีเมลเท่านั้น คุณจะได้รับสิทธิ์นี้โดยการขอขอบเขต profile หรือ email SDK ลงชื่อเข้าใช้ด้วย Google เวอร์ชันใหม่กว่าหรือ Google Sign-In เวอร์ชันเดิมจะขอขอบเขตเหล่านี้โดยค่าเริ่มต้น แต่หากคุณไม่ได้ใช้การตั้งค่าเริ่มต้น หรือหากคุณเข้าถึงปลายทาง OpenID Connect ของ Google โดยตรง โปรดตรวจสอบว่าคุณขอขอบเขตเหล่านี้อย่างน้อย 1 ขอบเขต

ตั้งค่าโปรเจ็กต์ในคอนโซล API

ก่อนที่จะเริ่มรับโทเค็นการดำเนินการด้านความปลอดภัย คุณต้องสร้างบัญชีบริการและเปิดใช้ RISC API ในโปรเจ็กต์คอนโซล API คุณต้องใช้โปรเจ็กต์คอนโซล API เดียวกันกับที่ใช้เข้าถึงบริการของ Google เช่น Google Sign-In ในแอป

วิธีสร้างบัญชีบริการมีดังนี้

  1. เปิดหน้าข้อมูลเข้าสู่ระบบของคอนโซล API เมื่อได้รับข้อความแจ้ง ให้เลือกโปรเจ็กต์คอนโซล API ที่คุณใช้เพื่อเข้าถึงบริการของ Google ในแอป

  2. คลิกสร้างข้อมูลเข้าสู่ระบบ > บัญชีบริการ

  3. สร้างบัญชีบริการใหม่ที่มีบทบาทผู้ดูแลระบบการกำหนดค่า RISC (roles/riscconfigs.admin) โดยทำตามวิธีการเหล่านี้

  4. (ไม่บังคับ) หากองค์กร Google Cloud ของคุณอนุญาตให้สร้างคีย์บัญชีบริการและคุณวางแผนที่จะลงชื่อ JWT ในเครื่อง ให้สร้างคีย์สำหรับบัญชีบริการที่สร้างขึ้นใหม่ เลือกประเภทคีย์ JSON แล้วคลิกสร้าง เมื่อสร้างคีย์แล้ว คุณจะดาวน์โหลดไฟล์ JSON ที่มีข้อมูลเข้าสู่ระบบของบัญชีบริการ เก็บไฟล์นี้ไว้ในที่ปลอดภัย แต่ก็ต้องเข้าถึงได้จากปลายทางตัวรับเหตุการณ์ด้วย

ขณะอยู่ในหน้าข้อมูลเข้าสู่ระบบของโปรเจ็กต์ ให้จดบันทึกรหัสไคลเอ็นต์ที่คุณใช้สําหรับฟีเจอร์ลงชื่อเข้าใช้ด้วย Google หรือ Google Sign-In (เดิม) ด้วย โดยปกติแล้ว คุณจะมีรหัสไคลเอ็นต์สำหรับแต่ละแพลตฟอร์มที่รองรับ คุณจะต้องใช้รหัสไคลเอ็นต์เหล่านี้เพื่อตรวจสอบโทเค็นการดำเนินการด้านความปลอดภัยตามที่อธิบายไว้ในส่วนถัดไป

วิธีเปิดใช้ RISC API

  1. เปิดหน้า RISC API ในคอนโซล API ตรวจสอบว่ายังคงเลือกโปรเจ็กต์ที่คุณใช้เพื่อเข้าถึงบริการของ Google อยู่

  2. อ่านข้อกำหนดของ RISC และทำความเข้าใจข้อกำหนด

    หากคุณเปิดใช้ API สำหรับโปรเจ็กต์ที่องค์กรเป็นเจ้าของ โปรดตรวจสอบว่าคุณได้รับอนุญาตให้ผูกองค์กรกับข้อกำหนดของ RISC

  3. คลิกเปิดใช้เฉพาะในกรณีที่คุณยินยอมตามข้อกำหนดของ RISC

สร้างปลายทางของตัวรับเหตุการณ์

หากต้องการรับการแจ้งเตือนการดำเนินการด้านความปลอดภัยจาก Google คุณต้องสร้างปลายทาง HTTPS ที่จัดการคำขอ HTTPS POST หลังจากลงทะเบียนปลายทางนี้ (ดูด้านล่าง) Google จะเริ่มโพสต์สตริงที่ลงนามด้วยการเข้ารหัสลับซึ่งเรียกว่าโทเค็นการดำเนินการด้านความปลอดภัยไปยังปลายทาง โทเค็นการดำเนินการด้านความปลอดภัยคือ JWT ที่ลงชื่อแล้วซึ่งมีข้อมูลเกี่ยวกับการดำเนินการด้านความปลอดภัยรายการเดียว

สำหรับโทเค็นการดำเนินการด้านความปลอดภัยแต่ละรายการที่คุณได้รับที่ปลายทาง ให้ตรวจสอบและถอดรหัสโทเค็นก่อน จากนั้นจึงจัดการการดำเนินการด้านความปลอดภัยตามความเหมาะสมสำหรับบริการของคุณ คุณจำเป็นต้องตรวจสอบโทเค็นเหตุการณ์ก่อนถอดรหัสเพื่อป้องกันการโจมตีที่เป็นอันตรายจากผู้ไม่ประสงค์ดี ส่วนต่อไปนี้จะอธิบายงานเหล่านี้

1. ถอดรหัสและตรวจสอบโทเค็นการดำเนินการด้านความปลอดภัย

เนื่องจากโทเค็นการดำเนินการด้านความปลอดภัยเป็น JWT ประเภทหนึ่ง คุณจึงใช้ไลบรารี JWT ใดก็ได้ เช่น ไลบรารีที่แสดงใน jwt.io เพื่อถอดรหัสและตรวจสอบโทเค็น ไม่ว่าคุณจะใช้ไลบรารีใด โค้ดการตรวจสอบโทเค็นต้องทำสิ่งต่อไปนี้

  1. รับตัวระบุผู้ออกการป้องกันแบบครอบคลุมหลายบริการ (issuer) และ URI ใบรับรองคีย์การรับรอง (jwks_uri) จากเอกสารการกำหนดค่า RISC ของ Google ซึ่งดูได้ที่ https://accounts.google.com/.well-known/risc-configuration
  2. ใช้ไลบรารี JWT ที่คุณเลือกเพื่อรับรหัสคีย์การลงนามจากส่วนหัวของโทเค็นการดำเนินการด้านความปลอดภัย
  3. จากเอกสารใบรับรองคีย์ Signing ของ Google ให้รับคีย์สาธารณะที่มีรหัสคีย์ที่คุณได้รับในขั้นตอนก่อนหน้า หากเอกสารไม่มีคีย์ที่มีรหัสที่คุณต้องการ เป็นไปได้ว่าโทเค็นการดำเนินการด้านความปลอดภัยไม่ถูกต้อง และปลายทางควรแสดงข้อผิดพลาดของ HTTP 400
  4. ใช้ไลบรารี JWT ที่คุณเลือกเพื่อยืนยันสิ่งต่อไปนี้
    • โทเค็นการดำเนินการด้านความปลอดภัยจะได้รับการลงนามโดยใช้คีย์สาธารณะที่คุณได้รับในขั้นตอนก่อนหน้า
    • การอ้างสิทธิ์ aud ของโทเค็นเป็นรหัสไคลเอ็นต์ของแอปรายการใดรายการหนึ่งของคุณ
    • การอ้างสิทธิ์ iss ของโทเค็นตรงกับตัวระบุผู้ออกที่คุณได้รับจากเอกสารการค้นพบ RISC โปรดทราบว่าคุณไม่จำเป็นต้องยืนยันการหมดอายุของโทเค็น (exp) เนื่องจากโทเค็นการดำเนินการด้านความปลอดภัยแสดงถึงเหตุการณ์ที่ผ่านมาแล้ว จึงไม่มีวันหมดอายุ

เช่น

Java

การใช้ java-jwt และ jwks-rsa-java:

public DecodedJWT validateSecurityEventToken(String token) {
    DecodedJWT jwt = null;
    try {
        // In a real implementation, get these values from
        // https://accounts.google.com/.well-known/risc-configuration
        String issuer = "accounts.google.com";
        String jwksUri = "https://www.googleapis.com/oauth2/v3/certs";

        // Get the ID of the key used to sign the token.
        DecodedJWT unverifiedJwt = JWT.decode(token);
        String keyId = unverifiedJwt.getKeyId();

        // Get the public key from Google.
        JwkProvider googleCerts = new UrlJwkProvider(new URL(jwksUri), null, null);
        PublicKey publicKey = googleCerts.get(keyId).getPublicKey();

        // Verify and decode the token.
        Algorithm rsa = Algorithm.RSA256((RSAPublicKey) publicKey, null);
        JWTVerifier verifier = JWT.require(rsa)
                .withIssuer(issuer)
                // Get your apps' client IDs from the API console:
                // https://console.developers.google.com/apis/credentials?project=_
                .withAudience("123456789-abcedfgh.apps.googleusercontent.com",
                              "123456789-ijklmnop.apps.googleusercontent.com",
                              "123456789-qrstuvwx.apps.googleusercontent.com")
                .acceptLeeway(Long.MAX_VALUE)  // Don't check for expiration.
                .build();
        jwt = verifier.verify(token);
    } catch (JwkException e) {
        // Key not found. Return HTTP 400.
    } catch (InvalidClaimException e) {

    } catch (JWTDecodeException exception) {
        // Malformed token. Return HTTP 400.
    } catch (MalformedURLException e) {
        // Invalid JWKS URI.
    }
    return jwt;
}

Python

import json
import jwt       # pip install pyjwt
import requests  # pip install requests

def validate_security_token(token, client_ids):
    # Get Google's RISC configuration.
    risc_config_uri = 'https://accounts.google.com/.well-known/risc-configuration'
    risc_config = requests.get(risc_config_uri).json()

    # Get the public key used to sign the token.
    google_certs = requests.get(risc_config['jwks_uri']).json()
    jwt_header = jwt.get_unverified_header(token)
    key_id = jwt_header['kid']
    public_key = None
    for key in google_certs['keys']:
        if key['kid'] == key_id:
            public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(key))
    if not public_key:
        raise Exception('Public key certificate not found.')
        # In this situation, return HTTP 400

    # Decode the token, validating its signature, audience, and issuer.
    try:
        token_data = jwt.decode(token, public_key, algorithms='RS256',
                                options={'verify_exp': False},
                                audience=client_ids, issuer=risc_config['issuer'])
    except:
        raise
        # Validation failed. Return HTTP 400.
    return token_data

# Get your apps' client IDs from the API console:
# https://console.developers.google.com/apis/credentials?project=_
client_ids = ['123456789-abcedfgh.apps.googleusercontent.com',
              '123456789-ijklmnop.apps.googleusercontent.com',
              '123456789-qrstuvwx.apps.googleusercontent.com']
token_data = validate_security_token(token, client_ids)

หากโทเค็นถูกต้องและถอดรหัสสำเร็จ ให้แสดงสถานะ HTTP 202 จากนั้นให้จัดการการดำเนินการด้านความปลอดภัยที่ระบุโดยโทเค็น

2. จัดการการดำเนินการด้านความปลอดภัย

เมื่อถอดรหัสแล้ว โทเค็นการดำเนินการด้านความปลอดภัยจะมีลักษณะดังตัวอย่างต่อไปนี้

{
  "iss": "https://accounts.google.com/",
  "aud": "123456789-abcedfgh.apps.googleusercontent.com",
  "iat": 1508184845,
  "jti": "756E69717565206964656E746966696572",
  "events": {
    "https://schemas.openid.net/secevent/risc/event-type/account-disabled": {
      "subject": {
        "subject_type": "iss-sub",
        "iss": "https://accounts.google.com/",
        "sub": "7375626A656374"
      },
      "reason": "hijacking"
    }
  }
}

การอ้างสิทธิ์ iss และ aud จะระบุผู้ออกโทเค็น (Google) และผู้รับที่ต้องการของโทเค็น (บริการของคุณ) คุณได้ยืนยันการอ้างสิทธิ์เหล่านี้ในขั้นตอนก่อนหน้าแล้ว

การอ้างสิทธิ์ jti เป็นสตริงที่ระบุการดำเนินการด้านความปลอดภัยรายการเดียว และไม่ซ้ำกันในสตรีม คุณสามารถใช้ตัวระบุนี้เพื่อติดตามการดำเนินการด้านความปลอดภัยที่ได้รับ

การอ้างสิทธิ์ events มีข้อมูลเกี่ยวกับการดำเนินการด้านความปลอดภัยที่โทเค็นแสดงถึง การอ้างสิทธิ์นี้เป็นการแมปจากตัวระบุประเภทเหตุการณ์ไปยังการอ้างสิทธิ์ subject ซึ่งระบุผู้ใช้ที่เกี่ยวข้องกับเหตุการณ์นี้ และรายละเอียดเพิ่มเติมเกี่ยวกับเหตุการณ์ที่อาจมีอยู่

การอ้างสิทธิ์ subject จะระบุผู้ใช้ที่เฉพาะเจาะจงด้วยรหัสบัญชี Google ที่ไม่ซ้ำกันของผู้ใช้ (sub) รหัสบัญชี Google นี้เป็นตัวระบุเดียวกัน (sub) ที่มีอยู่ในโทเค็นรหัส JWT ที่ออกโดยไลบรารีลงชื่อเข้าใช้ด้วย Google (Javascript, HTML) เวอร์ชันใหม่กว่า, ไลบรารีลงชื่อเข้าใช้ด้วย Google เดิม หรือ OpenID Connect เมื่อ subject_type ของการอ้างสิทธิ์เป็น id_token_claims ก็อาจมีช่อง email ที่มีอีเมลของผู้ใช้ด้วย

ใช้ข้อมูลในการอ้างสิทธิ์ events เพื่อดำเนินการที่เหมาะสมสำหรับประเภทเหตุการณ์ในบัญชีของผู้ใช้ที่ระบุ

ตัวระบุโทเค็น OAuth

สำหรับเหตุการณ์ OAuth เกี่ยวกับโทเค็นแต่ละรายการ ประเภทตัวระบุเรื่องของโทเค็นจะมีฟิลด์ต่อไปนี้

  • token_type: รองรับ refresh_token เท่านั้น

  • token_identifier_alg: ดูค่าที่เป็นไปได้ในตารางด้านล่าง

  • token: ดูตารางด้านล่าง

token_identifier_alg token
prefix อักขระ 16 ตัวแรกของโทเค็น
hash_base64_sha512_sha512 แฮชคู่ของโทเค็นโดยใช้ SHA-512

หากคุณผสานรวมกับเหตุการณ์เหล่านี้ เราขอแนะนำให้จัดทำดัชนีโทเค็นตามค่าที่เป็นไปได้เหล่านี้เพื่อให้จับคู่ได้อย่างรวดเร็วเมื่อได้รับเหตุการณ์

ประเภทเหตุการณ์ที่รองรับ

การป้องกันแบบครอบคลุมหลายบริการรองรับการดำเนินการด้านความปลอดภัยประเภทต่อไปนี้

ประเภทเหตุการณ์ แอตทริบิวต์ วิธีตอบกลับ
https://schemas.openid.net/secevent/risc/event-type/sessions-revoked จำเป็น: รักษาความปลอดภัยบัญชีของผู้ใช้อีกครั้งโดยสิ้นสุดเซสชันที่เปิดอยู่ของผู้ใช้
https://schemas.openid.net/secevent/oauth/event-type/tokens-revoked

จำเป็น: หากโทเค็นมีไว้สำหรับการลงชื่อเข้าใช้ด้วย Google ให้สิ้นสุดเซสชันที่เปิดอยู่ นอกจากนี้ คุณอาจต้องแนะนำให้ผู้ใช้ตั้งค่าวิธีการลงชื่อเข้าใช้สำรอง

แนะนำ: หากโทเค็นมีไว้เพื่อเข้าถึง Google API อื่นๆ ให้ลบโทเค็น OAuth ของผู้ใช้ที่คุณจัดเก็บไว้

https://schemas.openid.net/secevent/oauth/event-type/token-revoked ดูตัวระบุโทเค็นได้ที่ส่วนตัวระบุโทเค็น OAuth

จำเป็น: หากคุณจัดเก็บโทเค็นการรีเฟรชที่เกี่ยวข้อง ให้ลบโทเค็นดังกล่าวและขอให้ผู้ใช้ให้ความยินยอมอีกครั้งในครั้งถัดไปที่ต้องใช้โทเค็นเพื่อการเข้าถึง

https://schemas.openid.net/secevent/risc/event-type/account-disabled reason=hijacking,
reason=bulk-account

จำเป็น: หากสาเหตุที่ปิดใช้บัญชีคือ hijacking ให้รักษาความปลอดภัยบัญชีของผู้ใช้อีกครั้งโดยสิ้นสุดเซสชันที่เปิดอยู่ของผู้ใช้

แนะนำ: หากสาเหตุที่ปิดใช้บัญชีคือ bulk-account ให้วิเคราะห์กิจกรรมของผู้ใช้ในบริการของคุณและพิจารณาการดำเนินการติดตามผลที่เหมาะสม

แนะนำ: หากไม่มีการระบุเหตุผล ให้ปิดใช้ Google Sign-In สำหรับผู้ใช้และปิดใช้การกู้คืนบัญชีโดยใช้อีเมลที่เชื่อมโยงกับบัญชี Google ของผู้ใช้ (โดยปกติแล้วจะเป็นบัญชี Gmail แต่ไม่จำเป็นเสมอไป) เสนอวิธีการลงชื่อเข้าใช้แบบอื่นให้แก่ผู้ใช้

https://schemas.openid.net/secevent/risc/event-type/account-enabled แนะนำ: เปิดใช้ Google Sign-In อีกครั้งสำหรับผู้ใช้ และเปิดใช้การกู้คืนบัญชีอีกครั้งด้วยอีเมลของบัญชี Google ของผู้ใช้
https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required แนะนำ: คอยตรวจสอบกิจกรรมที่น่าสงสัยในบริการของคุณและดำเนินการตามความเหมาะสม
https://schemas.openid.net/secevent/risc/event-type/verification state=state แนะนำ: บันทึกว่าได้รับโทเค็นทดสอบแล้ว

เหตุการณ์ที่ซ้ำกันและเหตุการณ์ที่พลาดไป

การป้องกันแบบครอบคลุมหลายบริการจะพยายามส่งเหตุการณ์ที่เชื่อว่ายังไม่ได้ส่งอีกครั้ง ดังนั้น บางครั้งคุณอาจได้รับเหตุการณ์เดียวกันหลายครั้ง หากการดำเนินการนี้อาจทำให้เกิดการดำเนินการซ้ำๆ ที่สร้างความไม่สะดวกให้แก่ผู้ใช้ ให้พิจารณาใช้การอ้างสิทธิ์ jti (ซึ่งเป็นตัวระบุที่ไม่ซ้ำกันสำหรับเหตุการณ์) เพื่อกรองเหตุการณ์ที่ซ้ำกันออก มีเครื่องมือภายนอก เช่น Google Cloud Dataflow ที่อาจช่วยคุณดำเนินการ Dataflow ที่กรองข้อมูลที่ซ้ำกันออกได้

โปรดทราบว่าระบบจะส่งเหตุการณ์โดยมีการลองใหม่แบบจำกัด ดังนั้นหากตัวรับสัญญาณหยุดทำงานเป็นเวลานาน คุณอาจพลาดบางเหตุการณ์อย่างถาวร

ลงทะเบียนเครื่องรับ

หากต้องการเริ่มรับการดำเนินการด้านความปลอดภัย ให้ลงทะเบียนปลายทางของผู้รับโดยใช้ RISC API การเรียกใช้ RISC API ต้องมาพร้อมกับโทเค็นการให้สิทธิ์

คุณจะได้รับการดำเนินการด้านความปลอดภัยสำหรับผู้ใช้แอปเท่านั้น ดังนั้นคุณจึงต้องกำหนดค่าหน้าจอขอความยินยอม OAuth ในโปรเจ็กต์ GCP เป็นข้อกำหนดเบื้องต้นสำหรับขั้นตอนที่อธิบายไว้ด้านล่าง

1. สร้างโทเค็นการให้สิทธิ์

คุณสามารถสร้างโทเค็นการให้สิทธิ์สำหรับ RISC API ได้โดยใช้ Google Cloud CLI (gcloud) ที่มีการแอบอ้างเป็นบัญชีบริการ (แนะนำเมื่อมีการบังคับใช้ iam.disableServiceAccountKeyCreation หรือสำหรับการตั้งค่า CLI แบบครั้งเดียว) หรือโดยการลงนาม JWT ในเครื่องด้วยคีย์ส่วนตัวของบัญชีบริการ

ตัวเลือก ก: สร้างโทเค็นเพื่อการเข้าถึงที่มีอายุสั้นโดยใช้ gcloud (ไม่ต้องใช้คีย์ JSON)

หากบัญชีผู้ใช้มีบทบาทผู้สร้างโทเค็นบัญชีบริการ (roles/iam.serviceAccountTokenCreator) ในบัญชีบริการ คุณสามารถสร้างโทเค็นเพื่อการเข้าถึง OAuth 2.0 ที่มีอายุสั้นซึ่งมีขอบเขตการกำหนดค่า RISC ที่จำเป็นได้โดยตรงจากเทอร์มินัล ดังนี้

AUTH_TOKEN=$(gcloud auth print-access-token \
  --impersonate-service-account=SERVICE_ACCOUNT_EMAIL \
  --scopes=https://www.googleapis.com/auth/risc.configuration.readwrite,https://www.googleapis.com/auth/risc.verify,https://www.googleapis.com/auth/risc.status.readwrite)

ดูรายละเอียดเพิ่มเติมเกี่ยวกับขอบเขต OAuth 2.0 ที่ใช้ได้สำหรับ RISC API ได้ที่ขอบเขตของโทเค็นเพื่อการเข้าถึง

ตัวเลือก ข: สร้าง JWT ที่รับรองด้วยตนเองโดยใช้คีย์บัญชีบริการ

หรือหากต้องการสร้างโทเค็นการให้สิทธิ์ในเครื่องโดยไม่มีการส่งข้อมูลไปกลับในเครือข่าย ให้สร้าง JWT ที่มีการอ้างสิทธิ์ต่อไปนี้

{
  "iss": SERVICE_ACCOUNT_EMAIL,
  "sub": SERVICE_ACCOUNT_EMAIL,
  "aud": "https://risc.googleapis.com/google.identity.risc.v1beta.RiscManagementService",
  "iat": CURRENT_TIME,
  "exp": CURRENT_TIME + 3600
}

ลงนาม JWT โดยใช้คีย์ส่วนตัวของบัญชีบริการ ซึ่งดูได้ในไฟล์ JSON ที่คุณดาวน์โหลดเมื่อสร้างคีย์บัญชีบริการ

เช่น

Java

การใช้ java-jwt และไลบรารีการตรวจสอบสิทธิ์ของ Google

public static String makeBearerToken() {
    String token = null;
    try {
        // Get signing key and client email address.
        FileInputStream is = new FileInputStream("your-service-account-credentials.json");
        ServiceAccountCredentials credentials =
               (ServiceAccountCredentials) GoogleCredentials.fromStream(is);
        PrivateKey privateKey = credentials.getPrivateKey();
        String keyId = credentials.getPrivateKeyId();
        String clientEmail = credentials.getClientEmail();

        // Token must expire in exactly one hour.
        Date issuedAt = new Date();
        Date expiresAt = new Date(issuedAt.getTime() + 3600000);

        // Create signed token.
        Algorithm rsaKey = Algorithm.RSA256(null, (RSAPrivateKey) privateKey);
        token = JWT.create()
                .withIssuer(clientEmail)
                .withSubject(clientEmail)
                .withAudience("https://risc.googleapis.com/google.identity.risc.v1beta.RiscManagementService")
                .withIssuedAt(issuedAt)
                .withExpiresAt(expiresAt)
                .withKeyId(keyId)
                .sign(rsaKey);
    } catch (ClassCastException e) {
        // Credentials file doesn't contain a service account key.
    } catch (IOException e) {
        // Credentials file couldn't be loaded.
    }
    return token;
}

Python

import json
import time

import jwt  # pip install pyjwt

def make_bearer_token(credentials_file):
    with open(credentials_file) as service_json:
        service_account = json.load(service_json)
        issuer = service_account['client_email']
        subject = service_account['client_email']
        private_key_id = service_account['private_key_id']
        private_key = service_account['private_key']
    issued_at = int(time.time())
    expires_at = issued_at + 3600
    payload = {'iss': issuer,
               'sub': subject,
               'aud': 'https://risc.googleapis.com/google.identity.risc.v1beta.RiscManagementService',
               'iat': issued_at,
               'exp': expires_at}
    encoded = jwt.encode(payload, private_key, algorithm='RS256',
                         headers={'kid': private_key_id})
    return encoded

auth_token = make_bearer_token('your-service-account-credentials.json')

โทเค็นการให้สิทธิ์นี้สามารถใช้เพื่อเรียก RISC API ได้เป็นเวลา 1 ชั่วโมง เมื่อโทเค็นหมดอายุ ให้สร้างโทเค็นใหม่เพื่อเรียก RISC API ต่อไป

2. เรียก API การกำหนดค่าสตรีม RISC

เมื่อมีโทเค็นการให้สิทธิ์แล้ว คุณจะใช้ RISC API เพื่อกำหนดค่าสตรีมการดำเนินการด้านความปลอดภัยของโปรเจ็กต์ได้ ซึ่งรวมถึงการลงทะเบียนปลายทางของผู้รับ

โดยส่งคำขอ HTTPS POST ไปยัง https://risc.googleapis.com/v1beta/stream:update โดยระบุปลายทางของผู้รับและประเภทของเหตุการณ์ด้านความปลอดภัยที่คุณสนใจ ดังนี้

POST /v1beta/stream:update HTTP/1.1
Host: risc.googleapis.com
Authorization: Bearer AUTH_TOKEN

{
  "delivery": {
    "delivery_method":
      "https://schemas.openid.net/secevent/risc/delivery-method/push",
    "url": RECEIVER_ENDPOINT
  },
  "events_requested": [
    SECURITY_EVENT_TYPES
  ]
}

เช่น

Java

public static void configureEventStream(final String receiverEndpoint,
                                        final List<String> eventsRequested,
                                        String authToken) throws IOException {
    ObjectMapper jsonMapper = new ObjectMapper();
    String streamConfig = jsonMapper.writeValueAsString(new Object() {
        public Object delivery = new Object() {
            public String delivery_method =
                    "https://schemas.openid.net/secevent/risc/delivery-method/push";
            public String url = receiverEndpoint;
        };
        public List<String> events_requested = eventsRequested;
    });

    HttpPost updateRequest = new HttpPost("https://risc.googleapis.com/v1beta/stream:update");
    updateRequest.addHeader("Content-Type", "application/json");
    updateRequest.addHeader("Authorization", "Bearer " + authToken);
    updateRequest.setEntity(new StringEntity(streamConfig));

    HttpResponse updateResponse = new DefaultHttpClient().execute(updateRequest);
    Header[] responseContentTypeHeaders = updateResponse.getHeaders("Content-Type");
    StatusLine responseStatus = updateResponse.getStatusLine();
    int statusCode = responseStatus.getStatusCode();
    HttpEntity entity = updateResponse.getEntity();
    // Now handle response
}

// ...

configureEventStream(
        "https://your-service.example.com/security-event-receiver",
        Arrays.asList(
                "https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required",
                "https://schemas.openid.net/secevent/risc/event-type/account-disabled"),
        authToken);

Python

import requests

def configure_event_stream(auth_token, receiver_endpoint, events_requested):
    stream_update_endpoint = 'https://risc.googleapis.com/v1beta/stream:update'
    headers = {'Authorization': 'Bearer {}'.format(auth_token)}
    stream_cfg = {'delivery': {'delivery_method': 'https://schemas.openid.net/secevent/risc/delivery-method/push',
                               'url': receiver_endpoint},
                  'events_requested': events_requested}
    response = requests.post(stream_update_endpoint, json=stream_cfg, headers=headers)
    response.raise_for_status()  # Raise exception for unsuccessful requests

configure_event_stream(auth_token, 'https://your-service.example.com/security-event-receiver',
                       ['https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required',
                        'https://schemas.openid.net/secevent/risc/event-type/account-disabled'])

หากคำขอแสดงผลเป็น HTTP 200 แสดงว่ากำหนดค่าสตรีมเหตุการณ์สำเร็จแล้ว และปลายทางของผู้รับควรเริ่มรับโทเค็นการดำเนินการด้านความปลอดภัย ส่วนถัดไปจะอธิบายวิธีทดสอบการกำหนดค่าสตรีมและปลายทางเพื่อยืนยันว่าทุกอย่างทำงานร่วมกันได้อย่างถูกต้อง

รับและอัปเดตการกำหนดค่าสตรีมปัจจุบัน

หากต้องการแก้ไขการกำหนดค่าสตรีมในอนาคต คุณสามารถทำ ได้โดยส่งคำขอ GET ที่ได้รับอนุญาตไปยัง https://risc.googleapis.com/v1beta/stream เพื่อรับการกำหนดค่าสตรีม ปัจจุบัน แก้ไขเนื้อหาการตอบกลับ แล้วส่งการกำหนดค่าที่ แก้ไขกลับไปยัง https://risc.googleapis.com/v1beta/stream:update โดยใช้ POST ตามที่อธิบายไว้ข้างต้น

หยุดและกลับมาสตรีมเหตุการณ์ต่อ

หากต้องการหยุดสตรีมเหตุการณ์จาก Google ให้ส่งคำขอ POST ที่ได้รับอนุญาตไปยัง https://risc.googleapis.com/v1beta/stream/status:update โดยมี { "status": "disabled" } ในเนื้อหาคำขอ ขณะที่สตรีมปิดใช้งานอยู่ Google จะไม่ส่งเหตุการณ์ไปยังปลายทางของคุณและจะไม่บัฟเฟอร์การดำเนินการด้านความปลอดภัยเมื่อเกิดขึ้น หากต้องการเปิดใช้สตรีมเหตุการณ์อีกครั้ง ให้ส่ง POST { "status": "enabled" } ไปยังปลายทางเดียวกัน

3. ไม่บังคับ: ทดสอบการกำหนดค่าสตรีม

คุณสามารถยืนยันได้ว่าการกำหนดค่าสตรีมและปลายทางของผู้รับทำงานร่วมกันอย่างถูกต้องโดยการส่งโทเค็นการยืนยันผ่านสตรีมเหตุการณ์ โทเค็นนี้อาจมีสตริงที่ไม่ซ้ำกันซึ่งคุณใช้เพื่อยืนยันว่าได้รับโทเค็นที่ปลายทางแล้วได้ หากต้องการใช้ขั้นตอนนี้ โปรดสมัครรับข้อมูลประเภทเหตุการณ์ https://schemas.openid.net/secevent/risc/event-type/verification เมื่อลงทะเบียนตัวรับ

หากต้องการขอโทเค็นการยืนยัน ให้ส่งคำขอ HTTPS POST ที่ได้รับอนุญาตไปยัง https://risc.googleapis.com/v1beta/stream:verify ในเนื้อหาของคำขอ ให้ระบุสตริงที่ใช้ระบุบางอย่าง ดังนี้

{
  "state": "ANYTHING"
}

เช่น

Java

public static void testEventStream(final String stateString,
                                   String authToken) throws IOException {
    ObjectMapper jsonMapper = new ObjectMapper();
    String json = jsonMapper.writeValueAsString(new Object() {
        public String state = stateString;
    });

    HttpPost updateRequest = new HttpPost("https://risc.googleapis.com/v1beta/stream:verify");
    updateRequest.addHeader("Content-Type", "application/json");
    updateRequest.addHeader("Authorization", "Bearer " + authToken);
    updateRequest.setEntity(new StringEntity(json));

    HttpResponse updateResponse = new DefaultHttpClient().execute(updateRequest);
    Header[] responseContentTypeHeaders = updateResponse.getHeaders("Content-Type");
    StatusLine responseStatus = updateResponse.getStatusLine();
    int statusCode = responseStatus.getStatusCode();
    HttpEntity entity = updateResponse.getEntity();
    // Now handle response
}

// ...

testEventStream("Test token requested at " + new Date().toString(), authToken);

Python

import requests
import time

def test_event_stream(auth_token, nonce):
    stream_verify_endpoint = 'https://risc.googleapis.com/v1beta/stream:verify'
    headers = {'Authorization': 'Bearer {}'.format(auth_token)}
    state = {'state': nonce}
    response = requests.post(stream_verify_endpoint, json=state, headers=headers)
    response.raise_for_status()  # Raise exception for unsuccessful requests

test_event_stream(auth_token, 'Test token requested at {}'.format(time.ctime()))

หากคำขอสำเร็จ ระบบจะส่งโทเค็นการยืนยันไปยังปลายทางที่คุณลงทะเบียนไว้ จากนั้น ตัวอย่างเช่น หากปลายทางของคุณจัดการโทเค็นการยืนยันโดยการบันทึกไว้ คุณสามารถตรวจสอบบันทึกเพื่อยืนยันว่าได้รับโทเค็นแล้ว

ข้อมูลอ้างอิงรหัสข้อผิดพลาด

RISC API อาจแสดงข้อผิดพลาดต่อไปนี้

รหัสข้อผิดพลาด ข้อความแสดงข้อผิดพลาด การดำเนินการที่แนะนำ
400 การกำหนดค่าสตรีมต้องมีช่อง $fieldname คำขอของคุณไปยังปลายทาง https://risc.googleapis.com/v1beta/stream:update ไม่ถูกต้องหรือแยกวิเคราะห์ไม่ได้ โปรดระบุ $fieldname ในคำขอ
401 ไม่ได้รับอนุญาต การให้สิทธิ์ล้มเหลว ตรวจสอบว่าคุณได้แนบ โทเค็นการให้สิทธิ์มากับคำขอ และโทเค็นนั้นถูกต้องและยังไม่หมดอายุ
403 ปลายทางการนำส่งต้องเป็น HTTPS URL ปลายทางการนำส่ง (เช่น ปลายทางที่คุณคาดว่าจะมีการนำส่งเหตุการณ์ RISC) ต้องเป็น HTTPS เราจะไม่ส่งเหตุการณ์ RISC ไปยัง HTTP URL
403 การกำหนดค่าสตรีมที่มีอยู่ไม่มีวิธีการนำส่งที่สอดคล้องกับข้อกำหนดสำหรับ RISC โปรเจ็กต์ที่อยู่ในระบบคลาวด์ของ Google Cloud ของคุณต้องมีการกำหนดค่า RISC อยู่แล้ว หากคุณใช้ Firebase และเปิดใช้ Google Sign-In ไว้ Firebase จะจัดการ RISC สำหรับโปรเจ็กต์ของคุณ คุณจะสร้างการกำหนดค่าที่กำหนดเองไม่ได้ หากคุณไม่ได้ใช้ Google Sign-In สำหรับโปรเจ็กต์ Firebase โปรดปิดใช้ แล้วลองอัปเดตอีกครั้งหลังจากผ่านไป 1 ชั่วโมง
403 ไม่พบโปรเจ็กต์ ตรวจสอบว่าคุณใช้บัญชีบริการที่ถูกต้องสำหรับโปรเจ็กต์ที่ถูกต้อง คุณอาจใช้บัญชีบริการที่เชื่อมโยงกับโปรเจ็กต์ที่ถูกลบ ดู วิธีดูบัญชีบริการทั้งหมดที่เชื่อมโยงกับโปรเจ็กต์
403 บัญชีบริการต้องได้รับสิทธิ์เข้าถึงการกำหนดค่า RISC ของคุณ ไปที่คอนโซล API ของโปรเจ็กต์และมอบหมายบทบาท "ผู้ดูแลระบบการกำหนดค่า RISC" (roles/riscconfigs.admin) ให้กับบัญชีบริการที่ทำการเรียกไปยังโปรเจ็กต์โดยทำตามวิธีการเหล่านี้
403 ควรเรียกใช้ Management API ของสตรีมโดยบัญชีบริการเท่านั้น ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีเรียกใช้ Google APIs ด้วยบัญชีบริการ
403 ปลายทางการนำส่งไม่ได้อยู่ในโดเมนใดๆ ของโปรเจ็กต์ ทุกโปรเจ็กต์มีชุดโดเมนที่ได้รับอนุญาต หากปลายทางการนำส่ง (เช่น ปลายทางที่คุณคาดหวังให้นำส่งเหตุการณ์ RISC) ไม่ได้โฮสต์อยู่ในโดเมนใดโดเมนหนึ่ง คุณจะต้องเพิ่มโดเมนของปลายทางลงในชุดโดเมนดังกล่าว
403 หากต้องการใช้ API นี้ โปรเจ็กต์ของคุณต้องมีการกำหนดค่าไคลเอ็นต์ OAuth อย่างน้อย 1 รายการ RISC จะทำงานก็ต่อเมื่อคุณสร้างแอปที่รองรับฟีเจอร์ลงชื่อเข้าใช้ด้วย Google การเชื่อมต่อนี้ต้องใช้ไคลเอ็นต์ OAuth หากโปรเจ็กต์ไม่มีไคลเอ็นต์ OAuth ก็เป็นไปได้ว่า RISC จะไม่มีประโยชน์สำหรับคุณ ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ OAuth ของ Google สำหรับ API ของเรา
403

สถานะที่ไม่รองรับ

สถานะไม่ถูกต้อง

ขณะนี้เรารองรับเฉพาะสถานะสตรีม "enabled" และ "disabled" เท่านั้น
404

โปรเจ็กต์ไม่มีการกำหนดค่า RISC

โปรเจ็กต์ไม่มีการกำหนดค่า RISC อยู่ จึงอัปเดตสถานะไม่ได้

เรียกใช้ปลายทาง https://risc.googleapis.com/v1beta/stream:update เพื่อสร้างการกำหนดค่าสตรีมใหม่
4XX/5XX อัปเดตสถานะไม่ได้ โปรดดูข้อมูลเพิ่มเติมในข้อความแสดงข้อผิดพลาดโดยละเอียด

ขอบเขตโทเค็นเพื่อการเข้าถึง

หากคุณตัดสินใจใช้โทเค็นเพื่อการเข้าถึงเพื่อตรวจสอบสิทธิ์กับ RISC API ขอบเขตที่แอปพลิเคชันของคุณต้องขอมีดังนี้

Endpoint ขอบเขต
https://risc.googleapis.com/v1beta/stream/status https://www.googleapis.com/auth/risc.status.readonly หรือ https://www.googleapis.com/auth/risc.status.readwrite
https://risc.googleapis.com/v1beta/stream/status:update https://www.googleapis.com/auth/risc.status.readwrite
https://risc.googleapis.com/v1beta/stream https://www.googleapis.com/auth/risc.configuration.readonly หรือ https://www.googleapis.com/auth/risc.configuration.readwrite
https://risc.googleapis.com/v1beta/stream:update https://www.googleapis.com/auth/risc.configuration.readwrite
https://risc.googleapis.com/v1beta/stream:verify https://www.googleapis.com/auth/risc.verify

หากต้องการความช่วยเหลือ

ก่อนอื่น ให้ดูส่วนข้อมูลอ้างอิงรหัสข้อผิดพลาด หากยังมีข้อสงสัย โปรดโพสต์คำถามใน Stack Overflow พร้อมติดแท็ก #SecEvents