อัตโนมัติสิทธิ์ในการขอลบข้อมูล

*เนื้อหานี้แปลโดยใช้ AI (เวอร์ชัน Beta) และอาจมีข้อผิดพลาด หากต้องการดูหน้านี้เป็นภาษาอังกฤษ ให้คลิกที่นี่

กฎหมายคุ้มครองข้อมูลทั่วไป GDPR เป็นกฎหมายของยุโรปเกี่ยวกับการคุ้มครองข้อมูลและความเป็นส่วนตัวมันให้สิทธิ์แก่บุคคลในการขอลบข้อมูลส่วนบุคคลที่รู้จักกันในชื่อ สิทธิ์ในการลบหากคุณจัดเก็บข้อมูลระบุตัวบุคคลใดๆ (ข้อมูลที่ใช้ระบุตัวบุคคลได้ (PII)) ของผู้ใช้ของคุณ เช่น รหัสผู้ใช้ของพวกเขา คุณต้องปฏิบัติตามข้อกำหนดของ GDPR โดยลบข้อมูลนี้เมื่อได้รับคำขอจากผู้ใช้

แทนที่จะจัดการคำขอด้วยตนเองคุณสามารถ ตั้งค่าเว็บฮุค และใช้บอทภายในแอปส่งข้อความของบุคคลที่สามเพื่ออัตโนมัติ化กระบวนการเนื่องจาก ร้านข้อมูล เป็นวิธีที่พบบ่อยที่สุดสำหรับการจัดเก็บข้อมูล PII คู่มือนี้จะให้ตัวอย่างวิธีการสร้างบอทภายใน Guilded หรือ Discord ที่ใช้ Open Cloud API สำหรับร้านข้อมูล เพื่อลบข้อมูล PII ในฐานะที่เป็นโซลูชันการอัตโนมัติ

การไหลของงาน

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

  1. การสนับสนุน Roblox ได้รับสิทธิ์ในการร้องขอการลบจากผู้ใช้
  2. เวบฮุคของ Roblox ถูกกระตุ้นโดยมีรหัสผู้ใช้และรายการรหัสสถานที่เริ่มต้นสำหรับประสบการณ์ที่พวกเขาได้เข้าร่วมในเพย์โหลด
  3. บอทของคุณฟังการแจ้งเตือนเว็บฮุคเหล่านี้ ตรวจสอบความถูกต้อง และใช้ API เปิดคลาวด์สำหรับร้านข้อมูล เพื่อลบข้อมูล PII ที่เก็บไว้ในร้านข้อมูล
  4. บอทตอบกลับข้อความ webhook ใน Discord หรือ Guilded ด้วยสถานะการลบ

กำหนดค่าเว็บฮุคด้วยการผสานรวมของบุคคลที่สาม

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

ตั้งค่าเซิร์ฟเวอร์

ขั้นตอนต่อไปแสดงวิธีการตั้งค่าเซิร์ฟเวอร์โดยใช้ Guilded หรือ Discord

  1. สร้างเซิร์ฟเวอร์ Guilded ใหม่ หากคุณไม่คุ้นเคยกับกระบวนการ ดู การสนับสนุน Guilded
  2. ภายใต้การตั้งค่า ความเป็นส่วนตัว ตั้งเซิร์ฟเวอร์ให้เป็นส่วนตัวเซิร์ฟเวอร์สร้างช่องส่วนตัว #general โดยอัตโนมัติเป็นช่องเริ่มต้นของคุณ
  3. สร้างการผสานรวมเว็บฮุคกับเซิร์ฟเวอร์ใหม่และให้ชื่อที่คุณสามารถเข้าใจได้ง่ายเช่น GDPR Hookหากคุณไม่คุ้นเคยกับกระบวนการดู การสนับสนุน Guilded
  4. คัดลอก URL ของ webhook และเก็บไว้ในสถานที่ปลอดภัยอนุญาตให้สมาชิกทีมที่เชื่อถือได้เข้าถึงเท่านั้น เนื่องจากการรั่วไหลของ URL สามารถเปิดใช้งานผู้กระทำผิดร้ายแรงที่สามารถส่งข้อความปลอมและลบข้อมูลผู้ใช้ของคุณได้

กำหนดค่าเว็บฮุคบน Roblox

หลังจากได้รับ URL เซิร์ฟเวอร์ของบุคคลที่สามแล้วใช้มันเพื่อ กำหนดค่าเว็บฮุค แดชบอร์ดครีเอเตอร์:

  • เพิ่ม URL เซิร์ฟเวอร์ของ Guilded หรือ Discord เป็น URL เว็บฮุค * รวมความลับที่กําหนดเอง ลับ แม้ว่าความลับจะเป็นตัวเลือกสำหรับการสรุปการกำหนดค่า คุณควรรวมหนึ่งเพื่อป้องกันไม่ให้ผู้แสวงหาประโยชน์ปลอมตัวเป็น Roblox และลบข้อมูลของคุณสำหรับข้อมูลเพิ่มเติมเกี่ยวกับการใช้งานของความลับดูที่ ตรวจสอบความปลอดภัยของเว็บฮุค
  • เลือก สิทธิ์ในการขอลบ ภายใต้ การกระตุ้น .

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

Example notification on Guilded

กำหนดค่าบอท

หลังจากที่คุณเพิ่ม webhook ใช้มันเพื่อกำหนดค่าบอทด้วยขั้นตอนต่อไปนี้:

  1. เปิดรายการ เซิร์ฟเวอร์ทั้งหมด โดยคลิกที่ไอคอนหรือใช้ลัด:

    • CtrlS บน Windows
    • S บน Mac
  2. เลือกเซิร์ฟเวอร์ของคุณเพื่อรับสิทธิ์แจ้งเตือนการลบ

  3. ขยายรายการภายใต้ บ้านเซิร์ฟเวอร์ และเลือก จัดการบอท

  4. เซิร์ฟเวอร์สร้างช่องส่วนตัว #general โดยอัตโนมัติเป็นช่องเริ่มต้นของคุณ

  5. คลิกที่ปุ่ม สร้างบอท และเพิ่มชื่อบอท Guilded เปลี่ยนเส้นทางคุณไปยังหน้าการกำหนดค่าบอท

  6. เลือกส่วน API บนหน้าการกำหนดค่าบอท

  7. ในส่วน โทเค็น คลิกปุ่ม สร้างโทเค็น

  8. บันทึกและเก็บโทเคนที่สร้างขึ้นในสถานที่ปลอดภัย

สร้างคีย์ API เปิดของคลาวด์

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

รับตัวระบุประสบการณ์และสถานที่

เพื่อให้บอทค้นหาข้อมูล PII ที่ผู้ใช้ร้องขอให้ลบ รับตัวระบุต่อไปนี้ของประสบการณ์ทั้งหมดที่คุณตั้งใจจะใช้บอท:

  • ID จักรวาล **** รหัสที่ไม่ซ้ำกันของประสบการณ์ของคุณ
  • รหัสสถานที่เริ่มต้น ID ซึ่งเป็นรหัสระบุเฉพาะของสถานที่เริ่มต้นของประสบการณ์

เพื่อรับตัวระบุเหล่านี้:

  1. นําทางไปยัง แดชบอร์ดผู้สร้าง

  2. เลื่อนเมาส์ไปที่ภาพรวมของประสบการณ์, คลิกปุ่ม และเลือก คัดลอก ID จักรวาล และ คัดลอก ID จุดเริ่มต้น ตามลำดับ

เพิ่มสคริปต์

หลังจากที่คุณเสร็จสิ้นการตั้งค่าเว็บฮุค บอท และคีย์ API สําหรับร้านข้อมูล เพิ่มลงในสคริปที่ใช้โลจิสติกส์การอัตโนมัติของบอทตัวอย่างต่อไปนี้ใช้ Python 3:

  1. ติดตั้งไลบรารี Python โดยใช้คำสั่งต่อไปนี้:

    ติดตั้งไลบรารี

    pip3 install guilded.py==1.8.0
    pip3 install requests
    pip3 install urllib3==1.26.6
  2. คัดลอกและบันทึกสคริปต์ต่อไปนี้ตามส่วนต่างๆ ของโลจิสติกของบอทในไดเรกที่เดียวกัน:

    บอท_config.py

    BOT_TOKEN = ""
    OPEN_CLOUD_API_KEY = ""
    ROBLOX_WEBHOOK_SECRET = ""
    # สารานุกรมของรหัสสถานที่เริ่มต้นไปยัง
    # (ID จักรวาล, รายการ (ชื่อเก็บข้อมูล, ขอบเขต, คีย์)) สำหรับ
    # ร้านข้อมูลมาตรฐาน
    # ข้อมูลผู้ใช้ที่จัดเก็บภายใต้รายการเหล่านี้จะถูกลบ
    STANDARD_DATA_STORE_ENTRIES = {
    # รหัสสถานที่เริ่มต้น
    111111111: (
    # ID จักรวาล
    222222222,
    [
    ("StandardDataStore1", "Scope1", "Key1_{user_id}"),
    ("StandardDataStore1", "Scope1", "Key2_{user_id}"),
    ("StandardDataStore2", "Scope1", "Key3_{user_id}")
    ]
    ),
    33333333: (
    444444444,
    [
    ("StandardDataStore3", "Scope1", "Key1_{user_id}")
    ]
    )
    }
    # สารานุกรมของรหัสสถานที่เริ่มต้นไปยัง
    # (ID จักรวาล, รายการ (ชื่อเก็บข้อมูล, ขอบเขต, คีย์)) สำหรับ
    # จัดเก็บข้อมูลตามลําดับ
    # ข้อมูลผู้ใช้ที่จัดเก็บภายใต้รายการเหล่านี้จะถูกลบ
    ORDERED_DATA_STORE_ENTRIES = {
    111111111: (
    222222222,
    [
    ("OrderedDataStore1", "Scope2", "Key4_{user_id}")
    ]
    )
    }
    data_stores_api.py

    import requests
    import bot_config
    from collections import defaultdict
    """
    Calls Data Stores Open Cloud API to delete all entries for a user_id configured in
    STANDARD_DATA_STORE_ENTRIES. Returns a list of successful deletions and failures to delete.
    """
    def delete_standard_data_stores(user_id, start_place_ids):
    successes = defaultdict(list)
    failures = defaultdict(list)
    for owned_start_place_id in bot_config.STANDARD_DATA_STORE_ENTRIES:
    if owned_start_place_id not in start_place_ids:
    continue
    universe_id, universe_entries = bot_config.STANDARD_DATA_STORE_ENTRIES[owned_start_place_id]
    for (data_store_name, scope, entry_key) in universe_entries:
    entry_key = entry_key.replace("{user_id}", user_id)
    response = requests.delete(
    f"https://apis.roblox.com/datastores/v1/universes/{universe_id}/standard-datastores/datastore/entries/entry",
    headers={"x-api-key": bot_config.OPEN_CLOUD_API_KEY},
    params={
    "datastoreName": data_store_name,
    "scope": scope,
    "entryKey": entry_key
    }
    )
    if response.status_code in [200, 204]:
    successes[owned_start_place_id].append((data_store_name, scope, entry_key))
    else:
    failures[owned_start_place_id].append((data_store_name, scope, entry_key))
    return successes, failures
    """
    Calls Ordered Data Stores Open Cloud API to delete all entries for a user_id configured in
    ORDERED_DATA_STORE_ENTRIES. Returns a list of successful deletions and failures to delete.
    """
    def delete_ordered_data_stores(user_id, start_place_ids):
    successes = defaultdict(list)
    failures = defaultdict(list)
    for owned_start_place_id in bot_config.ORDERED_DATA_STORE_ENTRIES:
    if owned_start_place_id not in start_place_ids:
    continue
    universe_id, universe_entries = bot_config.ORDERED_DATA_STORE_ENTRIES[owned_start_place_id]
    for (data_store_name, scope, entry_key) in universe_entries:
    entry_key = entry_key.replace("{user_id}", user_id)
    response = requests.delete(
    f"https://apis.roblox.com/ordered-data-stores/v1/universes/{universe_id}/orderedDatastores/{data_store_name}/scopes/{scope}/entries/{entry_key}",
    headers={"x-api-key": bot_config.OPEN_CLOUD_API_KEY}
    )
    if response.status_code in [200, 204, 404]:
    successes[owned_start_place_id].append((data_store_name, scope, entry_key))
    else:
    failures[owned_start_place_id].append((data_store_name, scope, entry_key))
    return successes, failures
    message_parser.py

    import time
    import hmac
    import hashlib
    import re
    import base64
    import bot_config
    """
    Parses received message for Roblox signature and timestamp, the footer is only set if you
    configured webhook secret
    """
    def parse_footer(message):
    if not message.embeds[0].footer or \
    not message.embeds[0].footer.text:
    return "", 0
    footer_match = re.match(
    r"Roblox-Signature: (.*), Timestamp: (.*)",
    message.embeds[0].footer.text
    )
    if not footer_match:
    return "", 0
    else:
    signature = footer_match.group(1)
    timestamp = int(footer_match.group(2))
    return signature, timestamp
    """
    Verifies Roblox signature with configured secret to check for validity
    """
    def validate_signature(message, signature, timestamp):
    if not message or not signature or not timestamp:
    return False
    # ป้องกันการโจมตีเล่นซ้ำภายในหน้าต่าง 300 วินาที
    request_timestamp_ms = timestamp * 1000
    window_time_ms = 300 * 1000
    oldest_timestamp_allowed = round(time.time() * 1000) - window_time_ms
    if request_timestamp_ms < oldest_timestamp_allowed:
    return False
    # ตรวจสอบลายเซ็น
    timestamp_message = "{}.{}".format(timestamp, message.embeds[0].description)
    digest = hmac.new(
    bot_config.ROBLOX_WEBHOOK_SECRET.encode(),
    msg=timestamp_message.encode(),
    digestmod=hashlib.sha256
    ).digest()
    validated_signature = base64.b64encode(digest).decode()
    if signature != validated_signature:
    return False
    # ลายเซ็นที่ถูกต้อง
    return True
    """
    Parses a received webhook messaged on Discord or Guilded. Extracts user ID, prevents replay attack
    based on timestamp received, and verifies Roblox signature with configured secret to check for
    validity.
    """
    def parse_message(message):
    # แยกข้อความที่ได้รับสำหรับรหัสผู้ใช้และรหัสเกม
    if len(message.embeds) != 1 or \
    not message.embeds[0].description:
    return "", []
    description_match = re.match(
    r"You have received a new notification for Right to Erasure for the User Id: (.*) in " +
    r"the game\(s\) with Ids: (.*)",
    message.embeds[0].description
    )
    if not description_match:
    return "", []
    user_id = description_match.group(1)
    start_place_ids = set(int(item.strip()) for item in description_match.group(2).split(","))
    signature, timestamp = parse_footer(message)
    if validate_signature(message, signature, timestamp):
    return user_id, start_place_ids
    else:
    return "", []
    guilded_bot.py

    import guilded
    import json
    import bot_config
    import data_stores_api
    import message_parser
    def run():
    client = guilded.Client()
    @client.event
    async def on_ready():
    print(f"{client.user} is listening to Right to Erasure messages")
    """
    Handler for webhook messages from Roblox
    """
    @client.event
    async def on_message(message):
    # แยกและตรวจสอบข้อความ
    user_id, start_place_ids = message_parser.parse_message(message)
    if not user_id or not start_place_ids:
    return
    # ลบข้อมูลสตอเรจข้อมูลมาตรฐานของผู้ใช้
    [successes, failures] = data_stores_api.delete_standard_data_stores(user_id, start_place_ids)
    if successes:
    await message.reply(f"Deleted standard data stores data for " +
    f"user ID: {user_id}, data: {dict(successes)}")
    if failures:
    await message.reply(f"Failed to delete standard data stores data for " +
    f"user ID: {user_id}, data: {dict(failures)}")
    # ลบข้อมูลสตอเรจข้อมูลที่สั่งซื้อของผู้ใช้
    [successes, failures] = data_stores_api.delete_ordered_data_stores(user_id, start_place_ids)
    if successes:
    await message.reply(f"Deleted ordered data stores data for " +
    f"user ID: {user_id}, data: {dict(successes)}")
    if failures:
    await message.reply(f"Failed to delete ordered data stores data for " +
    f"user ID: {user_id}, data: {dict(failures)}")
    client.run(bot_config.BOT_TOKEN)
    if __name__ == "__main__":
    run()
  3. ในไฟล์ bot_config.py สำหรับการกำหนดค่าหลักของบอท:

    1. ตั้ง BOT_TOKEN ให้เป็นโทเค็นที่สร้างโดยบอทของคุณ
    2. ตั้ง OPEN_CLOUD_API_KEY เป็นคีย์ API ที่คุณสร้าง
    3. ตั้ง ROBLOX_WEBHOOK_SECRET เป็นความลับที่คุณกำหนดเมื่อกำหนดค่าเว็บฮุคบนแดชบอร์ดของผู้สร้าง
    4. ใน STANDARD_DATA_STORE_ENTRIES และ ORDERED_DATA_STORE_ENTRIES สารานุกรมสำหรับการค้นหาคลังข้อมูลของแต่ละบันทึกที่จะลบ:
      1. เพิ่ม ID จุดเริ่มต้นที่คัดลอกของคุณเป็นคีย์
      2. เพิ่ม ID จักรวาลเป็นองค์ประกอบแรกของค่า tuple
      3. แทนที่องค์ประกอบที่สองของ tuple ด้วยชื่อ ขอบเขต ชื่อกุญแจเข้า และรหัสผู้ใช้ที่เกี่ยวข้องกับคลังข้อมูลของคุณหากคุณใช้สเคมาโลจิกข้อมูลที่แตกต่างกันแก้ไขให้เหมาะกับสเคมาโลจิกข้อมูลของคุณตามลำดับ
  4. ดำเนินการตามคำสั่งต่อไปนี้เพื่อดําเนินการบอท:

    เรียกใช้บอท Guilded

    python3 guilded_bot.py
  5. บอทจะเริ่มฟังและตรวจสอบ webhooks Roblox สําหรับสิทธิ์ในการลบคําขอและโทรไปที่จุดสิ้นสุดของเมฆเปิดเพื่อลบคลังข้อมูลที่เกี่ยวข้อง

ทดสอบ

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

  1. ส่งคำขอ HTTP POST ไปยังเซิร์ฟเวอร์เว็บฮุคของ Guilded หรือ Discord ร่างกาย:

    ตัวอย่างคำขอ

    curl -X POST {serverUrl}
    -H 'Content-Type: application/json'
    -d '{
    "embeds":[{
    "title":"RightToErasureRequest",
    "description":"You have received a new notification for Right to Erasure for the User Id: {userIds} in the game(s) with Ids: {gameIds}",
    "footer":{
    "icon_url":"https://create.roblox.com/dashboard/assets/webhooks/roblox_logo_metal.png",
    "text":"Roblox-Signature: {robloxSignature}, Timestamp: {timestamp}"
    }
    }]
    }'
  2. หากคุณมีความลับเว็บฮุค:

    1. สร้าง Roblox-Signature โดยใช้การเข้ารหัส HMAC-SHA256 กับคีย์ลับของ webhook ของคุณ
    2. ตั้งเวลาปัจจุบันโดยใช้เวลาที่บันทึก UTC ในวินาทีเป็น Timestamp
  3. รวบรวม description ในรูปแบบต่อไปนี้:

    Description Field Format

    {Timestamp}. You have received a new notification for Right to Erasure for the User Id: {userId} in the game(s) with Ids: {gameIds}`.

    ตัวอย่าง:

    Example Description Field

    1683927229. You have received a new notification for Right to Erasure for the User Id: 2425654247 in the game(s) with Ids: 10539205763, 13260950955

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

ตัวอย่างร่างกาย

{
"embeds": [
{
"title": "RightToErasureRequest",
"description": "You have received a new notification for Right to Erasure for the User Id: 2425654247 in the game(s) with Ids: 10539205763, 13260950955",
"footer": {
"icon_url": "https://create.roblox.com/ashboard/assets/webhooks/roblox_logo_metal.png",
"text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Timestamp: 1683927229"
}
}
]
}