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

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

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

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

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

ภาพรวม

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

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

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

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

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

คุณจะได้รับโทเค็นกิจกรรมด้านความปลอดภัยสําหรับผู้ใช้ Google ที่ให้สิทธิ์แก่บริการในการเข้าถึงข้อมูลโปรไฟล์หรือที่อยู่อีเมลของตนเท่านั้น คุณได้รับสิทธิ์นี้โดยการส่งคําขอขอบเขต profile หรือ email SDK สําหรับ Sign In with 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 แล้วคลิก 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 ที่ต้องการเพื่อรับรหัสคีย์การลงนามจากส่วนหัวของโทเค็นความปลอดภัย
  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 ID ที่ออกโดยไลบรารีการลงชื่อเข้าใช้ด้วย 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 สําหรับผู้ใช้และปิดใช้การกู้คืนบัญชีโดยใช้อีเมลที่เชื่อมโยงกับบัญชี 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-purged แนะนํา: ลบบัญชีของผู้ใช้หรือมอบวิธีลงชื่อเข้าใช้แบบอื่นให้ผู้ใช้
https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required แนะนํา: เฝ้าระวังกิจกรรมที่น่าสงสัยในบริการและดําเนินการตามความเหมาะสม
https://schemas.openid.net/secevent/risc/event-type/verification สถานะ=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 เป็นเวลาหนึ่งชั่วโมง เมื่อโทเค็นหมดอายุ ให้สร้างโทเค็นใหม่เพื่อโทรออก 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 สามารถส่งกลับข้อผิดพลาดต่อไปนี้

รหัสข้อผิดพลาด ข้อความแสดงข้อผิดพลาด การดําเนินการที่แนะนํา
4,000 การกําหนดค่าสตรีมต้องมีช่อง $fieldname คําขอที่ส่งไปยังปลายทาง https://risc.googleapis.com/v1beta/stream:update ไม่ถูกต้องหรือแยกวิเคราะห์ไม่ได้ โปรดระบุ $fieldname ในคําขอ
401 ไม่ได้รับอนุญาต การให้สิทธิ์ล้มเหลว โปรดตรวจสอบว่าคุณได้แนบ โทเค็นการให้สิทธิ์กับคําขอแล้วและโทเค็นถูกต้องและยังไม่หมดอายุ
403 ปลายทางการส่งต้องเป็น URL แบบ HTTPS ปลายทางการส่ง (นั่นคือ ปลายทางที่คุณคาดว่าจะมีเหตุการณ์ 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 API ด้วยบัญชีบริการ
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