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

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

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

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

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

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

ภาพรวม

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

  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 คุณจะรับคีย์สาธารณะด้วยรหัสคีย์ที่มีอยู่ในขั้นตอนก่อนหน้าได้ หากเอกสารไม่มีคีย์ที่มี ID ที่คุณหา อาจเป็นเพราะโทเค็นของเหตุการณ์ความปลอดภัยไม่ถูกต้อง และปลายทางของคุณควรแสดงข้อผิดพลาด 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] โทเค็น
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-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 เป็นเวลา 1 ชั่วโมง เมื่อโทเค็นหมดอายุ ให้สร้างโทเค็นใหม่เพื่อโทรออก RISC API ต่อไป

2. เรียกใช้ API การกําหนดค่าสตรีม RISC

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

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

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

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

เช่น

Java

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

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

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

// ...

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

Python

import requests

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

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

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

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

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

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

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

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

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

หากต้องการขอโทเค็นการยืนยัน ให้ส่งคําขอ 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 อาจแสดงข้อผิดพลาดต่อไปนี้

รหัสข้อผิดพลาด ข้อความแสดงข้อผิดพลาด การดําเนินการที่แนะนํา
16 การกําหนดค่าสตรีมต้องมีช่อง $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 API ด้วยบัญชีบริการ
403 ปลายทางการส่งไม่ได้อยู่ในโดเมนของโปรเจ็กต์ ทุกโปรเจ็กต์มีชุดโดเมนที่ได้รับอนุญาต หากปลายทางการนําส่ง (นั่นคือปลายทางที่คุณคาดหวังว่าเหตุการณ์ RISC จะไปถึง) ไม่ได้โฮสต์อยู่บนเว็บปลายทางใดโดเมนหนึ่ง เรากําหนดให้คุณต้องเพิ่มโดเมนของปลายทางในชุดดังกล่าว
403 หากต้องการใช้ API นี้ โปรเจ็กต์ของคุณต้องกําหนดค่าไคลเอ็นต์ OAuth อย่างน้อย 1 รายการ RISC จะใช้งานได้เฉพาะในกรณีที่สร้างแอปที่รองรับ Google Sign-In การเชื่อมต่อนี้ต้องใช้ไคลเอ็นต์ OAuth หากโปรเจ็กต์ไม่มีไคลเอ็นต์ OAuth ก็เป็นไปได้ว่า RISC อาจไม่เป็นประโยชน์สําหรับคุณ ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ OAuth สําหรับ API ของ Google
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