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

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

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

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

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

ภาพรวม

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

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

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

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

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

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

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

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

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

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

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

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

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

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

วิธีเปิดใช้ 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 Sign-in รุ่นเดิม หรือ 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 แฮชโทเค็น 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 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 ให้สร้าง 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 เพื่อรับการกำหนดค่าสตรีมปัจจุบัน แก้ไขเนื้อหาคำตอบ แล้วส่ง POST การกำหนดค่าที่แก้ไขแล้วกลับไปยัง 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 เมื่อลงทะเบียนผู้รับ

หากต้องการขอโทเค็นการยืนยัน ให้ส่งคําขอ POST ผ่าน HTTPS ที่มีสิทธิ์ไปยัง 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 ปลายทางการนำส่งต้องเป็น 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 Sign In เท่านั้น การเชื่อมต่อนี้ต้องใช้ไคลเอ็นต์ 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