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

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

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

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

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

ภาพรวม

หากต้องการใช้การป้องกันแบบครอบคลุมหลายบริการกับแอปหรือบริการ คุณต้องทําตาม งานต่อไปนี้

  1. สร้างโปรเจ็กต์ใน API Console

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. เปิดหน้า RISC API ใน API Consoleตรวจสอบว่ายังได้เลือกโปรเจ็กต์ที่คุณใช้ เพื่อเข้าถึงบริการของ 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. จากเอกสารใบรับรองคีย์การลงนามของ 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 โทเค็น
prefix อักขระ 16 ตัวแรกของโทเค็น
hash_base64_sha512_sha512 แฮช 2 ชั้นของโทเค็นโดยใช้ 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 สำหรับผู้ใช้และปิดใช้การกู้คืนบัญชีโดยใช้อีเมลที่เชื่อมโยงกับบัญชี Google ของผู้ใช้ (โดยปกติจะเป็นบัญชี Gmail แต่ไม่จำเป็นเสมอไป) เสนอวิธีการลงชื่อเข้าใช้สำรองให้ผู้ใช้

https://schemas.openid.net/secevent/risc/event-type/account-enabled แนะนำ: เปิดใช้การลงชื่อเข้าใช้ด้วย Google สำหรับผู้ใช้อีกครั้ง และเปิดใช้ การกู้คืนบัญชีอีกครั้งด้วยอีเมลบัญชี 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 ให้สร้าง 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. เรียกใช้ RISC Stream Configuration API

เมื่อมีโทเค็นการให้สิทธิ์แล้ว คุณจะใช้ 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 ตามที่อธิบายไว้ข้างต้น

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

หากต้องการหยุดสตรีมเหตุการณ์จาก 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()))

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

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

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

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

ปลายทาง ขอบเขต
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