حریم خصوصی شهروندان

چگونه بازاستفاده از کلید SSH می‌تواند هویت و سرور کاربران را آشکار کند؟

کلید عمومی SSH برای انتشار طراحی شده است، اما استفاده از یک کلید واحد در گیت‌هاب و سرورهای خصوصی می‌تواند آن را به شناسه‌ای برای پیوند دادن هویت آنلاین کاربر به زیرساخت فنی تبدیل کند.

مارک پشم‌فروش
مارک پشم‌فروشمعاون فناوری و توسعه‌دهنده ارشد، کدنویس شبکه، کرنل و سیستم‌های توزیع‌شده
۲۲ مرداد ۱۴۰۵
7 دقیقه مطالعه
چگونه بازاستفاده از کلید SSH می‌تواند هویت و سرور کاربران را آشکار کند؟

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

این روش امکان ورود به سرور را فراهم نمی‌کند، اما می‌تواند یک رابطه فنی میان «هویت»، «کلید عمومی» و «آدرس سرور» ایجاد کند که در صورت جمع‌آوری گسترده داده‌ها، ارزش اطلاعاتی قابل‌توجهی برای نهادهای نظارتی و مهاجمان خواهد داشت.

کلید عمومی امن است، اما الزاماً ناشناس نیست

پروتکل SSH برای مدیریت و دسترسی رمزگذاری‌شده به سیستم‌های راه دور استفاده می‌شود. در احراز هویت مبتنی بر کلید، کاربر یک جفت کلید عمومی و خصوصی دارد که باید محرمانه باقی بماند، درحالی‌که کلید عمومی روی سرور مقصد قرار می‌گیرد. هنگام ورود، کاربر باید با استفاده از کلید خصوصی ثابت کند که مالک کلید عمومی ثبت‌شده روی سرور است. در نتیجه، دانستن کلید عمومی به‌تنهایی برای ورود کافی نیست.

1package main
2
3import (
4 "fmt"
5 "io"
6
7 "golang.org/x/crypto/ssh"
8)
9
10const (
11 username = "root"
12 server = "10.2.10.5:22"
13 publicKey = "ssh-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
14)
15
16func main() {
17 parsed, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKey))
18 if err != nil {
19 panic(err)
20 }
21
22 signer := &DummySigner{PubKey: parsed}
23 authMethod := []ssh.AuthMethod{ssh.PublicKeysCallback(
24 func() ([]ssh.Signer, error) {
25 return []ssh.Signer{signer}, nil
26 },
27 )}
28
29 config := &ssh.ClientConfig{
30 User: username,
31 Auth: authMethod,
32 HostKeyCallback: ssh.InsecureIgnoreHostKey(),
33 }
34
35 _, _ = ssh.Dial("tcp", server, config)
36
37 if signer.Accepted {
38 fmt.Println("Public key was accepted by server")
39 return
40 }
41
42 fmt.Println("Public key was rejected by server")
43}
44
45type DummySigner struct {
46 PubKey ssh.PublicKey
47 Accepted bool
48}
49
50func (signer *DummySigner) PublicKey() ssh.PublicKey {
51 return signer.PubKey
52}
53func (signer *DummySigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
54 signer.Accepted = true
55 return &ssh.Signature{Format: signer.PubKey.Type()}, nil
56}
57
58نتیجه:
59
60Public key was accepted by server

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

پاسخ سرور چگونه به یک نشانه هویتی تبدیل می‌شود؟

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

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

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

گیت‌هاب به کاربران اجازه می‌دهد برای دسترسی به مخازن خود از SSH استفاده کنند. کلیدهای عمومی متصل به حساب‌ها نیز از طریق نشانی‌هایی مانند github.com/username.keys و رابط برنامه‌نویسی گیت‌هاب قابل مشاهده‌اند. مستندات رسمی گیت‌هاب تأیید می‌کند که فهرست کلیدهای عمومی تأییدشده کاربران را می‌توان برای منابع عمومی، بدون احراز هویت دریافت کرد. در نتیجه، یک جمع‌آوری‌کننده داده می‌تواند میان نام کاربری گیت‌هاب و کلیدهای عمومی آن حساب ارتباط برقرار کند.

فیلیپو والسوردا، مهندس و پژوهشگر رمزنگاری، در سال ۲۰۲۳ نشان داد که جمع‌آوری انبوه این داده‌ها امکان‌پذیر است. او مجموعه‌ای حدوداً پنج گیگابایتی از کلیدهای عمومی کاربران گیت‌هاب ایجاد و سپس آن را به یک پایگاه داده کمتر از ۴۰۰ مگابایت تبدیل کرد. پروژه آزمایشی او می‌توانست کلیدهای عرضه‌شده هنگام اتصال SSH را با حساب‌های گیت‌هاب مطابقت دهد. این آزمایش نشان می‌دهد که تبدیل کلیدهای عمومی به یک پایگاه داده قابل جست‌وجو صرفاً یک احتمال نظری نیست.

از حساب کاربری تا شناسایی سرور

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

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

خطر زمانی بیشتر می‌شود که کاربر:

  • یک کلید واحد را در گیت‌هاب و چند سرور استفاده کرده باشد،
  • سرویس SSH را مستقیماً در اینترنت در دسترس قرار داده باشد،
  • کلید را برای حساب‌هایی مانند root یا نام‌های کاربری قابل حدس ثبت کرده باشد،
  • حساب گیت‌هاب خود را به نام واقعی، ایمیل یا محل کارش متصل کرده باشد.

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

چگونه می‌توان این خطر را کاهش داد؟

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

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

جمع‌بندی

این یافته‌ها نشان می‌دهد که امنیت رمزنگاری‌شده SSH مانع استفاده اطلاعاتی از فراداده‌های آن نمی‌شود. مهاجم برای شناسایی ارتباط میان یک کاربر و سرور الزاماً به کلید خصوصی یا دسترسی داخلی نیاز ندارد و گاهی پاسخ اولیه سرور به یک کلید عمومی، در کنار داده‌های منتشرشده در گیت‌هاب، برای ساختن این ارتباط کافی است.

مسئله اصلی ضعف رمزنگاری SSH نیست، بلکه بازاستفاده از یک شناسه عمومی در چند محیط متفاوت است. برای کاربران در معرض نظارت، توسعه‌دهندگان مستقل و سازمان‌های حساس، تفکیک کلیدها باید بخشی از سیاست پایه امنیت عملیاتی باشد.

اشتراک‌گذاری: