نشت مدل در پایپ‌لاین‌های یادگیری ماشین؛ تهدیدات و راهکارهای MLSecOps

فهرست مطالب

مدل، یک دارایی استراتژیک است

فکر می‌کنید که بعد از آموزش مدل، دیگر همه چیز تمام شده است؟ اما اشتباه می‌کنیم. خطرناک‌ترین مرحله دقیقن بعد از استقرار (deployment) شروع می‌شود.

در دنیای یادگیری ماشین، خودِ مدل؛ نه فقط داده، یک دارایی ارزشمند محسوب می‌شود. سال‌ها زمان، هزینه محاسباتی و داده‌های اختصاصی صرف ساختن یک مدل دقیق می‌شود. نشت مدل (Model Leakage) به معنای از دست رفتن تمام این سرمایه در یک لحظه است. لحظه‌ای که شاید در زندگی حرفه‌ای، هیچ‌گاه فراموشش نکنیم.

«نشت مدل» یعنی دسترسی غیرمجاز به جزئیات مدل: معماری (Architecture)، وزن‌ها (weights)، خروجی‌های میانی و یا توانایی استخراج اطلاعات حساس از طریق تعامل با مدل.

چرا نشت مدل خطرناک‌تر از نشت داده است؟

اگر داده‌هایمان لو برود، فقط داده را از دست داده‌ایم (که البته این نیز بشدت غم انگیز است و مشکلات فراوانی را برای مجموعه در آینده به ارمغان خواهد آورد). اما اگر مدل‌مان لو برود، الگوریتم و دانش پنهان درون داده را هم از دست داده‌ایم. در ادامه سه دلیل کلیدی برای این مشکل، یعنی نشت مدل را ذکر می‌کنم:

  • اول: مدل، دانش و الگوهای درون داده را خلاصه کرده است. مهاجم با داشتن مدل، بدون نیاز به دیدن داده اصلی، می‌تواند همان پیش‌بینی‌ها را انجام دهد.
  • دوم: با داشتن مدل، مهاجم می‌تواند حملات پیشرفته‌تری مثل Membership Inference و Model Inversion انجام دهد که داده را هم بازسازی می‌کند.
  • سوم: مدل شما ممکن است برای سال‌ها در معرض استفاده باشد. نشت آن یعنی همیشه یک کپی از «مغز» سیستم شما در دست دشمن است.

پس باید به هر روش ممکن، به هکرها اجازه ندهیم که به مدل ما دسترسی پیدا کنند یا به اصطلاح جلوی نشت مدل را باید بگیریم.

چهار بردار حمله رایج که باید بشناسیم

در ابتدا باید ذکر کنم که بردار حمله (Attack Vector) یعنی مسیری که مهاجم از آن وارد سیستم می‌شود یا نقطه ضعفی که از آن سوء استفاده می‌کند تا بنوعی در سیستم ما رخنه کند. در ادامه به ذکر چهار مورد از بردارهای حمله می‌پردازیم. سپس، بعد از آشنایی با این موارد به این خواهیم پرداخت که MLSecOps چگونه بما کمک خواهد کرد.

دسترسی بی‌محابا به Model Registry؛ ساده‌ترین راه برای مهاجم

بیایید صادق باشیم: اکثر تیم‌ها Model Registry خود را مثل یک پوشه عمومی تنظیم می‌کنند و این یک فاجعه است.

در بسیاری از پلتفرم‌های ML (مثل MLflow، Kubeflow، Sagemaker) مدل‌ها در یک ریجستری ذخیره می‌شوند. اگر کنترل دسترسی ضعیف باشد، مهاجم می‌تواند فایل مدل ما (مثلن .h5 یا .pt) را دانلود کند (جهت مشاهده‌ی انواع فایل‌های مدل در دنیای یادگیری ماشین، این ریپازیتوری عمومی در گیت‌هاب را ببینید). در برخی از موارد، مهاجم تنها با یک درخواست HTTP کل مدل را می‌برد.

مثال واقعی: یک Server-Side Request Forgery (SSRF) در یک API ساده می‌تواند به مهاجم اجازه دهد مدل ذخیره شده در MinIO یا S3 داخلی را دانلود کند و ما باید بتوانیم جلوی حملات این‌چنینی را در هنگام توسعه، با طراحی درست، بگیریم.

حمله تشخیص عضویت یا Membership Inference Attack؛ حتی بدون دیدن مدل، حریم خصوصی را می‌شکنیم

شاید فکر کنید اگر مدل خود را مخفی نگه داریم، در امان خواهیم بود. اما این اشتباه است. حتی APIهای عمومی مدل هم اطلاعات خطرناکی را فاش می‌کند.

در این حمله، مهاجم فقط به خروجی مدل دسترسی دارد (مثلن از طریق API). با ارسال درخواست‌های زیاد و تحلیل پاسخ‌ها، می‌فهمد که آیا یک نمونه خاص در داده‌ی آموزش مدل بوده یا نه. کوچکترین اطلاعاتی برای تیم‌هایی که مجموعه‌ی ما را مورد حمله قرار می‌دهندف برای ما می‌تواند فاجعه به بار آورد. اما چگونه. اجازه بدهید یک مثال ساده بزنم:

مثال: فرض کنید یک مدل تشخیص بیماری داریم. مهاجم می‌تواند بفهمد که «آیا شخص X در dataset آموزش بوده؟» که این معادل فهمیدن بیماری آن شخص است. شخصی که شاید نخواهد کسی از مشکل فعلی ایشان خبردار شود. چه برسد افرادی که نیت سوء دارند.

قطعه کد پایتونی زیر را در نظر بگیرید. در برخی از موارد به همین سادگی اطلاعاتی را به مهاجم می‌دهم که خودش نیز باورش نمی‌شد به همین راحتی بوده.

#########################
"""
یک حمله
Membership Inference
ساده روی یک طبقه‌بند
"""

import numpy as np
from sklearn.ensemble import RandomForestClassifier

"""
فرض کنید مدل ما یک
RandomForest
است که روی داده 
X_train
آموزش دیده است
"""

model = RandomForestClassifier()
model.fit(X_train, y_train)

"""
مهاجم فقط به
 API
دسترسی دارد: ورودی می‌دهد، خروجی (احتمال کلاس) می‌گیرد
"""

# تابع زیر همان چیزی است که مهاجم می‌بیند
def model_api_prediction(input_data):
    return model.predict_proba([input_data])[0]

"""
تست عضویت: آیا نمونه
test_sample
در داده آموزش بوده؟
"""

 # نمونه‌ای که می‌خواهیم بررسی کنیم
test_sample = X_test[0] 

 # بیشترین احتمال
confidence = model_api_prediction(test_sample).max() 

print(f"Confidence: {confidence:.3f} - {'Member' if confidence > 0.9 else 'Non-member'}")
#########################

حمله وارونه‌سازی مدل یا Model Inversion Attack؛ بازسازی داده اصلی از روی مدل

حتی بدتر از Membership Inference، در این حالت، مهاجم نه فقط وجود یک نمونه را می‌فهمد، بلکه خودِ نمونه را نیز بازسازی می‌کند.

در این حمله، مهاجم ورودی‌هایی را می‌سازد که مدل را مجبور می‌کند داده‌های اصلی را فاش کند. با تکرار و بهینه‌سازی، می‌توان به تصویری نزدیک به داده‌ی آموزش رسید. برای درک بهتر مثال زیر را ببینید.

مثال: فرض کنید شرکتی مدل تشخیص چهره را روی تصاویر کارمندان خود آموزش داده است. محققان امنیتی نشان داده‌اند که اگر مهاجم فقط به خروجی مدل دسترسی داشته باشد (مثلن از طریق یک API که می‌گوید «آیا این دو چهره مشابه هستند یا خیر؟)، می‌تواند به تدریج و با ارسال درخواست‌های هوشمندانه، تصویری نزدیک به چهره‌ی اصلی هر کارمند را بازسازی کند. در یک حمله‌ی معروف، محققان توانستند از روی یک مدل تشخیص چهره که روی تصاویر افراد مشهور آموزش دیده بود، چهره‌هایی را تولید کنند که به طرز وحشتناکی به افراد واقعی شبیه بود؛ بدون اینکه هیچ‌ کدام از آن تصاویر اصلی را دیده باشند. یعنی مدل «چهره میانگین» یا «ویژگی‌های بازساختی» هر فرد را در وزن‌های خود ذخیره کرده بود و مهاجم با پرسش‌های دقیق، آن اطلاعات را بیرون کشید. این حمله نشان می‌دهد که حتی اگر داده‌ی آموزش را هرگز منتشر نکنیم، خود مدل می‌تواند سکوی پرشی برای بازسازی آن داده‌ها باشد. برای دسترسی به مقاله فوق در گوگل عنوان زیر را جستجو کنید:

Model Inversion Attacks that Exploit Confidence Information and Basic Countermeasures

نشت از طریق Side-Channel؛ وقتی مدل ناخواسته حرف می‌زند

گاهی مدل چیزی را فاش می‌کند که شما حتی نمی‌دانستید وجود دارد؛ از طریق رفتار جانبی خود. اجازه بدهید با بیان چند مثال این را بیشتر برایتان تشریح کنم.

مثال‌های رایج:

  • لاگ‌های بیش از حد دقیق: چاپ خروجی لایه‌های میانی برای دیباگ، معماری مدل را لو می‌دهد.
  • تفاوت زمانی پاسخ: مدل برای نمونه‌های خاص زمان متفاوتی می‌خواهد که اطلاعاتی درباره ساختار داخلی را فاش می‌کند.
  • پیام‌های خطا: خطایی که بگوید «لایه سوم با ابعاد ۱۲۸x۱۲۸ خطا داد» معماری را لو می‌دهد.
  • و مثال بسیار ساده‌تر: اگر مدل برای چهره‌های خاص ۲ ثانیه و برای بقیه ۰.۵ ثانیه جواب بدهد، مهاجم حدس می‌زند آن چهره‌ها در داده آموزش بوده‌اند.

راهکارهای MLSecOps: چگونه از نشت مدل جلوگیری کنیم؟

حالا که برخی از تهدیدها را شناختیم، بیایید ببینیم چه راهکارهایی جواب می‌دهد. از ساده‌ترین تا پیشرفته‌ترین؛ و صد البته که این فهرست کامل نیست و باید هر روز دانش و مهارت خودمان در این وادی را ارتقاء دهیم.

1- مدل را در Model Registry قفل کنید

اولین خط دفاعی، خودِ مخزن مدل است. Model Registry (مثل MLflow یا S3) باید طوری تنظیم شوند که هیچ‌کس بدون احراز هویت قوی نتواند مدل را دانلود کند. اما تنها احراز هویت کافی نیست. باید مدل را در حال استراحت (at rest) رمزگذاری کنیم. یعنی حتی اگر مهاجم از طریق SSRF یا نفوذ به سرور به فایل مدل دسترسی پیدا کند، بدون کلید رمز نتواند آن را باز نموده و بررسی نماید. همچنین تمام دانلودها را لاگ کنید تا اگر کسی بیش از حد معمول مدل را دانلود می‌کند، متوجه شوید. بسیار مشاهده و گزارش شده است که، مجموعه‌ها از درون لطمه می‌بینند. یعنی یک دسترسی بیش از حد باز به یک عضو از کادر فنی مجموعه ایشان را وسوسه نموده تا یک بک‌آپ از مدل را در جایی و برای روز مبادا ذخیره کند.

قطعه کد زیر را ببینید که کلیت فرایند رمزنگاری مدل در مخزن چگونه است.

#########################
"""
رمزگذاری یک مدل
TensorFlow/Keras
قبل از ذخیره در
S3
یا
MinIO
"""

import tensorflow as tf
from cryptography.fernet import Fernet

"""
ابتدا مدل را ذخیره می‌کنیم
"""

model.save("temp_model.h5")

"""
سپس کلید رمز تولید می‌کنیم
این کلید را در جای امن مثل
Vault
نگه دارید
"""

key = Fernet.generate_key()
cipher = Fernet(key)

"""
در این مرحله، فایل مدل را می‌خوانیم و رمزگذاری می‌کنیم
"""

with open("temp_model.h5", "rb") as f:
    encrypted_model = cipher.encrypt(f.read())

"""
فایل رمزگذاری شده را در
Registry
ذخیره می‌کنیم
"""

with open("model_encrypted.bin", "wb") as f:
    f.write(encrypted_model)

"""
حالا فقط کسی که کلید را دارد
می‌تواند مدل را بارگذاری کند
"""
#########################

2-  API استنتاج را ببندید

دومین درب ورودی به مدل شما، APIای است که به کاربران سرویس می‌دهد. مهاجم می‌تواند بدون دیدن خود مدل، فقط با ارسال درخواست‌های زیاد به API، اطلاعات حساسی مثل Membership Inference را استخراج کند. اما راهکار چیست؟

محدودیت تعداد درخواست یا همان Rate limiting اولین قدم است. حملات Membership Inference معمولن به هزاران درخواست نیاز دارند. پس اگر هر کاربر نتواند در دقیقه بیشتر از ۱۰ درخواست بزند، حمله عملن غیرممکن می‌گردد.

#########################
"""
محدودیت نرخ درخواست با
Flask + Redis
"""

from flask import Flask, request, jsonify
from functools import wraps
import redis

app = Flask(__name__)
redis_client = redis.Redis(host='localhost', port=6379, db=0)

def rate_limit(max_requests=10, window_seconds=60):
    def decorator(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            user_ip = request.remote_addr
            key = f"rate_limit:{user_ip}"
            current = redis_client.get(key)
            
            if current is None:
                redis_client.setex(key, window_seconds, 1)
            elif int(current) >= max_requests:
                return jsonify({"error": "Too many requests"}), 429
            else:
                redis_client.incr(key)
            return f(*args, **kwargs)
        return decorated
    return decorator

"""
مسیر
/predict
با محدودیت
پنج درخواست در دقیقه
برای هر کاربر
"""

@app.route('/predict', methods=['POST'])
@rate_limit(max_requests=5, window_seconds=60)
def predict():
    data = request.json
    # انجام پیش‌بینی با مدل
    result = data.get("features", [0])[0] 
    return jsonify({"prediction": result})

"""
حالا مهاجم نمی‌تواند
هزاران درخواست برای
Membership Inference
بزند
"""
#########################

قدم بعدی، کوانتیزه کردن (quantization) خروجی مدل است. برای مثال، اگر مدل شما برای تصاویر خروجی تولید می‌کند، می‌توانید دقت اعداد خروجی را کاهش دهید (مثلاً از ۳۲ بیت ممیز شناور به ۸ بیت صحیح) یا به آن نویز اضافه کنید. این کار جزئیات ریز خروجی را حذف می‌کند و در نتیجه مهاجم نمی‌تواند از اختلافات ظریف بین خروجی‌ها برای تشخیص عضویت یک نمونه در داده‌ی آموزش استفاده کند. برای مشاهده یک مقاله‌ی معروف و جدید در این وادی عنوان زیر را در گوگل جستجو نمائید (یا تنها این لینک را ببینید):

An Indicator of Membership Inference Security in Post-Training Quantized Models

3- در زمان آموزش از Differential Privacy استفاده کنید

بهترین دفاع، دفاعی است که از ریشه مشکل را حل کند. مشکل اصلی این است که مدل «جزئیات نمونه‌های خاص» را به یاد می‌سپارد. تکنیکی به نام حریم خصوصی تفاضلی (Differential Privacy) تضمین می‌کند که خروجی مدل به هیچ نمونه‌ی خاصی در داده‌ی آموزش وابسته نباشد.

اما روش مذکور چطور کار می‌کند؟ در زمان آموزش، به جای اینکه مدل دقیقن روی داده‌ی واقعی تمرین کند، مقداری نویز کنترل‌ شده به گرادیان (gradient) در حین آموزش اضافه می‌کنیم. در نتیجه مدل الگوهای عمومی را یاد می‌گیرد، اما نمی‌تواند بفهمد که «آیا علی در داده‌ی آموزش بوده یا نه». این کار کمی دقت مدل کاهش می‌یابد، اما امنیت آن به شدت بالا می‌رود (اگر در این زمینه نیاز به مطالعه‌ی تخصصی دارید، این مقاله را بررسی کنید).

4- امضای دیجیتال برای مدل‌های مستقر شده

چطور مطمئن شویم مدلی که در پروداکشن مستقر یا به اصطلاح دیپلوی می‌کنیم، همان مدل اصلی است و دستکاری نشده؟ مهاجم ممکن است مدل شما را عوض کند و یک مدل آلوده (به انواع و اقسام کدهای مخرب) به جای آن قرار دهد. اما راهکار در این خصوص چیست؟

قبل از استقرار، روی مدل امضای دیجیتال بزنید (مثل امضای یک سند مهم). در زمان بارگذاری، سیستم ابتدا امضا را بررسی می‌کند و اگر با کلید عمومی شرکت مطابقت نداشت، مدل را اجرا نمی‌کند. این کار از دو حمله جلوگیری می‌کند:

  • جایگزینی مدل با مدل جعلی
  • دستکاری وزن‌های مدل اصلی

توجه بفرمائید که هش مدل را در جایی به غیر از محل ذخیره و نگهداری آن باید بارگذاری کنیم. اگر هم مدل و هم هش آن در یک مکان باشند، مهاجم براحتی ابتدا مدل را تغییر داده و سپس هش مدل تغییر یافته را ایجاد کرده و در محل آپلود می‌کند. ما نیز که از همه جا بی‌خبر هستیم و هش و مدل را به سیستم می‌سپاریم برای بررسی. سیستم نیز بما چراغ سبز نشان می‌دهد.

5- مانیتورینگ رفتاری برای تشخیص حملات

آخرین لایه دفاعی، چشمی‌ است که همیشه باز هست، 24 ساعت شبانه‌روز و هفت روز هفته. حتی با همه‌ی راهکارهای بالا، ممکن است حمله‌ای از شبکه عبور کند. مانیتورینگ رفتاری یعنی سیستم‌تان الگوهای عادی درخواست به API مدل را یاد بگیرد و هر چیزی غیر عادی را گزارش دهد. مثلن اگر یک کاربر ناگهان شروع به ارسال هزاران درخواست مشابه با تغییرات جزئی کند (الگوی Membership Inference) سیستم هشدار بدهد. یا اگر دانلود مدل از Registry از یک IP ناشناس انجام شد، لاگ کند و به تیم امنیت اطلاع دهد.

پس اگر بخواهیم 5 مورد بالا رو تیتروار بعنوان یک برگه‌ی تقلب جمع‌بندی کنیم خواهیم داشت:

  • قفل کردن Model Registry (رمزگذاری + احراز هویت + لاگ)
  • محدود کردن API استنتاج (Rate limiting + کوانتیزه کردن)
  • Differential Privacy در زمان آموزش (حل مسئله از ریشه)
  • امضای دیجیتال مدل (اطمینان از اصالت مدل)
  • مانیتورینگ رفتاری (تشخیص حملات در لحظه)

مثال واقعی: نشت یک مدلِ تشخیص چهره در یک شرکت

در ادامه یک سناریوی واقعی را قدم به قدم برایتان تعریف می‌کنم تا ببینید تهدیدها چطور ترکیب می‌شوند و چگونه ما بفنا می‌رویم. فرض کنید شرکتی مدل تشخیص چهره‌ی کارمندان را ساخته و از API آن برای حضور و غیاب استفاده می‌کند. چیزی که این روزها اتفاقن در خیلی از ادارات باب شده است.

قدم اول؛ نفوذ اولیه: مهاجم یک SSRF در یک سرویس جانبی (مثل سرویس آپلود تصویر پروفایل) پیدا می‌کند.

قدم دوم؛ دسترسی به Model Registry: از همان SSRF به Model Registry داخلی که روی پورت ۸۰۰۰ باز است درخواست می‌فرستد و مسیر فایل مدل را پیدا می‌کند.

قدم سوم؛ دانلود مدل: مدل (مثلن یک فایل 950 مگابایتی .pt) را دانلود می‌کند.

قدم چهارم؛ اجرای حملات: حالا با اجرای محلی مدل، مهاجم می‌تواند:

  • بفهمد کدام کارمند در dataset آموزش بوده (Membership Inference).
  • با Model Inversion تصاویر تقریبی کارمندان را بازسازی کند.
  • مدل را برای مقاصد خودش استفاده نماید (مثلن تقلب در حضور و غیاب).

در نتیجه در عرض ۱۰ دقیقه پس از SSRF، کل سیستم حضور و غیاب بی‌اعتبار شده است. شاید به خودتان بگوئید «مگر فیلم هالیوودی است که اینقدر راحت بیان بزنن و برن و اصلن می‌خوان چیکار؟». نکته همین جاست. این نوع حملات برای افراد آموزش دیده، سخت نیست. اما هدفی که آنها دنبال می‌کنند ممکن است بسیار پیچیده باشد.

راهکارهای جلوگیری در این مثال با توجه به مواردی که در بالا ذکر کردم: رمزگذاری مدل در Registry + امضای دیجیتال + جداسازی شبکه Registry از سرویس‌های عمومی + لاگ و هشدار.

کلام پایانی؛ از امروز مدل را مثل یک دارایی درجه یک ببینید

شاید این مقاله را خواندید و فکر کردید «اینها برای شرکت‌های بزرگ است». اما اشتباه می‌کنید. کوچک‌ترین استارتاپ هم مدل‌هایی را آموزش داده‌اند که ارزش حیاتی برای کسب‌وکارش دارند.

نشت مدل یکی از خطرات جدی اما کم‌توجه در چرخه حیات مدل‌های یادگیری ماشین است. خوشبختانه راهکارهای MLSecOps عملی و قابل پیاده‌سازی هستند. از هم اکنون شروع کنید. شاید فردا دیر باشد. حتمن در اولین فرصت و طبق زمان‌بندی خودتان سه مورد زیر را پیگیری کنید:

  1. امروز بررسی کنید Model Registry شما چه سطح دسترسی دارد.
  2. این هفته رمزگذاری مدل‌ها را فعال کنید.
  3. این ماه rate limiting را روی API استقرار دهید.

در دنیایی که بخصوص توسعه دهندگان، دیگر حس و حال کدنویسی خط به خط را ندارند، و بیشتر از AI برای کارهای خود استفاده می‌کنند، بسیار محتمل است که در طول روند ساخت یک محصول یا آموزش مدل یادگیری ماشین، بخاطر تعجیل در تحویل کار یا هر چیز دیگر، استانداردهایی را کنار بگذاریم که کل سیستم ما را در آینده دچار مشکلات عدیده نماید. به گمان بنده، مباحث امنیتی، بهمان دلیلی که در بالا عرض کردم، هر روز جدی‌تر می‌شود. ما توانسته‌ایم فاز توسعه را تسریع کنیم، اما از امنیت غافل شدیم. اما برادرانه بشما می‌گویم؛ امنیت را به موازات کارهای تست و توسعه، در ذهن بپرورانید.

برخی از اطلاعات مقاله:

سایر مقالات مجموعه:

پست‌های مرتبط با این مقاله:

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *