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

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

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

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

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

ภาพรวม

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

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

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

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

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

คุณจะได้รับโทเค็นการดำเนินการด้านความปลอดภัยสำหรับผู้ใช้ Google ที่ให้สิทธิ์บริการของคุณในการเข้าถึงข้อมูลโปรไฟล์หรืออีเมลเท่านั้น คุณได้รับสิทธิ์นี้จากการส่งคำขอขอบเขต profile หรือ email ลงชื่อเข้าใช้ด้วย Google เวอร์ชันใหม่หรือ SDK ของ 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 แล้วคลิก Create เมื่อสร้างคีย์แล้ว คุณจะดาวน์โหลดไฟล์ 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 ที่คุณเลือกเพื่อรับรหัสคีย์ Signing จากส่วนหัวของโทเค็นการดำเนินการด้านความปลอดภัย
  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 เป็นสตริงที่ระบุการดำเนินการด้านความปลอดภัย 1 รายการและเป็นค่าเฉพาะสำหรับสตรีมนั้นๆ คุณสามารถใช้ตัวระบุนี้เพื่อติดตามการดำเนินการด้านความปลอดภัยที่คุณได้รับ

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

แนะนำ: หากโทเค็นมีไว้สําหรับเข้าถึง 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 ที่อาจช่วยคุณดำเนินการโฟลว์ข้อมูลที่ซ้ำกันได้

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

ลงทะเบียนผู้รับ

หากต้องการเริ่มรับการดำเนินการด้านความปลอดภัย ให้ลงทะเบียนอุปกรณ์ปลายทางของตัวรับโดยใช้ 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 จะไม่ส่งเหตุการณ์ไปยังปลายทางของคุณและจะไม่บัฟเฟอร์การดำเนินการด้านความปลอดภัยเมื่อเกิดขึ้น หากต้องการเปิดใช้สตรีมเหตุการณ์อีกครั้ง ให้โพสต์ { "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 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