پرش به محتوای اصلی
OpenAI

۲۳ اردیبهشت ۱۴۰۵

مهندسیامنیت

اجرای Codex در ویندوز» / «راه‌اندازی Codex در Windows

نوشتهٔ David Wiesen، عضو کادر فنی

در حال بارگذاری…

وقتی در سپتامبر 2025 به تیم مهندسی Codex پیوستم، Codex برای ویندوز هیچ پیاده‌سازی سندباکس نداشت؛ یعنی کاربران ویندوز هنگام استفاده از عامل‌های کدنویسی OpenAI ناچار بودند میان دو گزینه نامطلوب یکی را انتخاب کنند:

  1. تأیید تقریبا هر فرمانی که یک عامل کدنویسی می‌خواست اجرا کند (حتی خواندن‌ها)، که هم ناکارآمد بود و هم آزاردهنده. یکی از مزایای اصلی استفاده از Codex این است که لازم نیست همه کارهای خسته‌کننده را خودتان انجام دهید.
  2. فعال کردن حالت دسترسی کامل: اجازه دادن به Codex برای اجرای همه فرمان‌ها بدون تأیید یا محدودیت، که اصطکاک را از بین می‌برد اما به بهای کاهش نظارت است.

Codex، عامل کدنویسی ما، روی لپ‌تاپ‌های توسعه‌دهندگان اجرا می‌شود—چه از طریق CLI، چه افزونه IDE، یا برنامه دسکتاپ. این عامل، گفت‌وگویی را میان یک انسان پشت صفحه‌کلید و یک مدل که در محیط ابری اجرا می‌شود مدیریت می‌کند تا استنتاج را انجام دهد.

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

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

نموداری که مرزهای ایزوله‌سازی سیستم‌عامل در سندباکس Codex را نشان می‌دهد.

Codex برای پیاده‌سازی یک سندباکس مؤثر به قابلیت‌های ایزوله‌سازی نیاز دارد که توسط سیستم‌عامل رایانه اعمال شوند. برخی سیستم‌عامل‌ها ابزارهایی دارند که این کار را به‌خوبی انجام می‌دهند (مثلا Seatbelt در MacOs، یا seccomp و bubblewrap در لینوکس)؛ اما ویندوز در حال حاضر این نوع قابلیت را به‌صورت آماده ارائه نمی‌کند.

برای این‌که استفاده از Codex در ویندوز مانند جاهای دیگر امن و خوشایند باشد، باید سندباکس خودمان را پیاده‌سازی می‌کردیم.

جایی که ابزارهای موجود ویندوز کم آوردند

ویندوز چند ابزار و سازوکار ابتدایی برای ایزوله‌سازی ارائه می‌کند. با این‌که هیچ‌کدام دقیقا با نیازهای ما منطبق نبودند، چند راه‌حل بالقوه را بررسی کردیم—یعنی AppContainer، سندباکس ویندوز و برچسب‌گذاری Mandatory Integrity Control.

AppContainer

  • چیست: AppContainer، سندباکس بومی ویندوز است؛ یک مدل ایزوله‌سازی مبتنی بر قابلیت که برای برنامه‌هایی ساخته شده که از قبل دقیقا می‌دانند به چه چیزهایی نیاز به دسترسی دارند.
  • چرا: جذاب است، چون به‌جای اینکه فقط تلاش خود را بکند محدودیت‌هایی ایجاد کند، یک مرز واقعی در سطح سیستم‌عامل ارائه می‌کند.
  • چرا نه: Codex یک برنامه با دامنه بسیار محدود و مشخص نیست. این قابلیت جریان‌های کاری باز توسعه‌دهندگان را پیش می‌برد: پوسته‌ها، Git، Python، مدیران بسته، ابزارهای ساخت، و هر باینری دیگری که عامل تشخیص دهد به آن نیاز دارد. در عمل، همین باعث شد AppContainer تناسب لازم را با آن مسئله نداشته باشد. این ایزوله‌سازی قوی بود، اما برای دسته‌ای بسیار محدودتر از بارهای کاری نسبت به «اجازه دادن به یک عامل برای عمل کردن مانند یک توسعه‌دهنده».

ویندوز سندباکس

  • چیست: ویندوز سندباکس یک ماشین مجازی سبک و یک‌بارمصرف از مایکروسافت است. یک دسکتاپ تازه ویندوز با یک مرز ایزوله‌سازی قوی در اختیار می‌گیرید و هر کاری که داخل آن انجام دهید با پایان جلسه از بین می‌رود.
  • چرا: به دلایل واضحی جالب است—از AppContainer با نرم‌افزارهای دلخواه بسیار سازگارتر است و از منظر امنیت هم محیطی بسیار محکم‌تر فراهم می‌کند.
  • چرا نه: Codex باید مستقیما روی چک‌اوت، ابزارها و محیط واقعی کاربر عمل کند، نه داخل یک دسکتاپ جداگانه و موقتی که نیاز به راه‌اندازی و پل‌زدن بین میزبان و مهمان داشته باشد. همچنین یک مشکل بنیادین در محصول داشت: ویندوز سندباکس حتی روی SKUهای ویندوز نسخه Home هم در دسترس نیست.

برچسب‌گذاری یکپارچگی در Mandatory Integrity Control (MIC)

  • چیست: ویندوز مفهومی به نام «سطوح یکپارچگی» دارد، مانند پایین، متوسط و بالا، که تعیین می‌کنند سیستم تا چه اندازه به اشیا و فرایندها اعتماد دارد. قاعده اصلی این است که یک فرایند با یکپارچگی پایین‌تر نمی‌تواند در شیئی با سطح یکپارچگی بالاتر بنویسد، حتی اگر ACL (فهرست‌های کنترل دسترسی) معمولی در غیر این صورت اجازه آن را بدهد. برای مثال، یک فرایند با سطح یکپارچگی پایین، کمتر قابل‌اعتماد تلقی می‌شود؛ بنابراین ویندوز مانع از نوشتن آن در اشیای معمولی با سطح یکپارچگی متوسط می‌شود، مگر اینکه آن اشیا به‌صراحت دوباره برچسب‌گذاری شده باشند تا این کار را مجاز کنند.
  • چرا: MIC روی کاغذ شیک به نظر می‌رسید—Codex را با یکپارچگی پایین اجرا کنیم، ریشه‌های قابل‌نوشتن را با یکپارچگی پایین برچسب‌گذاری کنیم، و بگذاریم ویندوز ممنوعیت نوشتن در بقیه جاها را اعمال کند. این می‌توانست راهکاری بدون نیاز به دسترسی ادمین برایمان فراهم کند که پشتوانه‌اش یک سازوکار واقعی سیستم‌عامل باشد.
  • چرا نه: مانند ACLها، برچسب‌های یکپارچگی سیستم فایل واقعی میزبان را تغییر می‌دهند، و در این مورد این تغییر معنایی به‌ویژه گسترده است. علامت‌گذاری یک فضای کاری با سطح یکپارچگی پایین صرفاً به این معنا نیست که «Codex می‌تواند اینجا بنویسد.» این موضوع یعنی فرایندهای با سطح یکپارچگی پایین به‌طور کلی می‌توانند در آنجا بنویسند. در یک ماشین واقعی توسعه‌دهنده، این کار چک‌اوت واقعی کاربر را به یک مقصد با سطح یکپارچگی پایین برای میزبان تبدیل می‌کند؛ ریسکی که بسیار بیشتر از اعطای ACLهای دقیقاً هدف‌گذاری‌شده به یک طراحی سندباکس واحد است. حتی اگر ابزارهای توسعه‌دهنده با سطح یکپارچگی متوسط همچنان کار کنند، مدل اعتماد زیربنایی فضای کاری به‌گونه‌ای تغییر کرده است که مهار آن دشوار و توجیه آن دشوارتر است.

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

اولین نمونه اولیه: «سندباکس بدون ارتقا»

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

محدود کردن نوشتن فایل

اگر اصلا نوشتن فایل را محدود نمی‌کردیم، با یک مسئله ایمنی روبه‌رو می‌شدیم. اگر هم بیش از حد آن را محدود می‌کردیم، سندباکس به بهره‌وری کاربر آسیب می‌زد و مدام نیاز به تأیید پیدا می‌کرد. برای حل این مسئله، به دو جزء مهم در ویندوز تکیه کردیم: SIDها (شناسه‌های امنیتی) و توکن‌های با محدودیت نوشتن.

SIDها به ما اجازه می‌دهند به سندباکس یک هویت بدهیم

SID یا شناسه امنیتی، هویتی است که ویندوز آن را به مجوزها پیوند می‌دهد. هر کاربر یک SID دارد، گروه‌ها SID دارند، و حتی یک نشست ورود منفرد نیز SID اختصاصی خودش را دریافت می‌کند. برای مثال، یک نشست فعلی که کاربر در آن وارد شده است ممکن است SID مانند S-1-5-5-X-Y داشته باشد. شناسه امنیتی (SID) اختصاص‌یافته به گروه مدیران محلی ممکن است S-1-5-32-544 باشد.

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

توکن‌های محدودشده نوشتاری محدوده‌ای را که Codex می‌تواند در آن فایل‌ها را تغییر دهد محدود می‌کنند

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

برای این‌که یک عملیات نوشتن موفق شود، دو بررسی باید با موفقیت بگذرند:

  1. هویت عادی کاربر (مالک توکن) باید اجازه انجام آن را داشته باشد.
  2. حداقل یکی از SIDهای موجود در فهرست SIDهای محدودشده توکن نیز باید دسترسی گرفته باشد
نموداری با عنوان «نوشتن در sandbox مستلزم هر دو دسترسیِ کاربر عادی و دسترسیِ SIDِ sandbox-write است».

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

با SIDها و توکن‌های محدودشده نوشتاری، سندباکس بدون ارتقای ما این‌گونه کار می‌کرد:

  1. راه‌اندازی سندباکس یک SID مصنوعی به نام سندباکس-نوشتن ایجاد می‌کرد.
  2. به SID‏ سندباکس-نوشتن دسترسی نوشتن، اجرا و حذف داده شده بود تا
    1. دایرکتوری کاری فعلی
    2. هر writable_roots اضافی که در config.toml پیکربندی شده باشد.
  3. راه‌اندازی سندباکس صراحتاً دسترسی نوشتن همان SID را به مکان‌های «فقط‌خواندنی درون فضای قابل‌نوشتن» مانند این‌ها رد می‌کرد:
    1. <cwd>/.git
    2. <cwd>/.codex
    3. <cwd>/.agents
  4. Codex فرمان‌ها را تحت یک توکن محدودشده نوشتاری اجرا می‌کرد که فهرست SID محدودشده آن شامل همه، SID نشست جاری واردشده، و SID مصنوعی سندباکس-نوشتن بود.

این روش عملاً مشکل محدودکردن نوشتن فایل را حل کرد و امیدوارکننده به نظر می‌رسید. حالا به راه‌حلی برای محدود کردن دسترسی شبکه در سندباکس نیاز داشتیم.

محدود کردن دسترسی شبکه

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

بدون فایروال ویندوز به‌عنوان یک گزینه، مواردی را که می‌توانستیم کنترل کنیم محدود کردیم. ما تلاش کردیم محیط فرزند را برای انواع ابزارهای شبکه‌ای که توسعه‌دهندگان واقعاً استفاده می‌کنند، به‌گونه‌ای طراحی کنیم که در حالت بسته دچار خطا شود؛ به‌طوری‌که فرمان‌های Git، نصب‌کننده‌های بسته و موارد مشابه در سندباکس با خطا مواجه شوند و کاربر مجبور باشد هرگونه عملیات رو به اینترنت را تأیید کند. ایده این بود که راه‌های فرار آشکار را مسموم کنیم: ترافیکِ آگاه از پراکسی را به یک نقطه پایان مرده بفرستیم، کاری کنیم ترابری HTTP(S) در Git نیز همین کار را انجام دهد، و کاری کنیم Git از طریق SSH بلافاصله شکست بخورد. علاوه بر این، یک دایرکتوری کوچک denybin را به ابتدای PATH اضافه کردیم و ترتیب PATHEXT را تغییر دادیم تا اسکریپت‌های استاب SSH و SCP پیش از باینری‌های واقعی فراخوانی شوند.

برای نمونه، این‌ها چند بازنویسی مشخص محیطی بودند که برای محدود کردن دسترسی شبکه استفاده کردیم:

  • HTTPS_PROXY=http://127.0.0.1:9
  • ALL_PROXY=http://127.0.0.1:9
  • GIT_HTTPS_PROXY=http://127.0.0.1:9
  • NO_PROXY=localhost,127.0.0.1,::1
  • GIT_SSH_COMMAND=cmd /c exit 1
نموداری که معماری سندباکس با دسترسی ارشد را با قوانین فایروال و یک کاربر اختصاصی ویندوز نشان می‌دهد.

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

رویکرد بدون ارتقا با مصالحه‌هایی همراه بود

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

  • سرعت راه‌اندازی: اعمال ACLهای فضای کاری بسته به توپولوژی پوشه فضای کاری می‌تواند پرهزینه باشد.
  • ردپا: ما ACLهای واقعی را روی سیستم توسعه‌دهنده اعمال می‌کردیم، هرچند این ردپا چندان تهاجمی نیست چون همه ACLهای اعمال‌شده مربوط به یک SID مصنوعی سفارشی هستند که فقط سندباکس از آن استفاده می‌کند.
  • معناشناسی که تغییر آن دشوار است: اتکا به ACLها برای محدودیت‌های مبتنی بر فایل به این معناست که تغییر معناشناسی سندباکس پرهزینه و پیچیده است. در حالی که در macOS، می‌توانیم نحوه تولید .sbpl را به‌صورت پویا تغییر دهیم فایلی که برای پیکربندی Seatbelt استفاده می‌شود، سندباکس ویندوز ممکن است برای تنظیم ACLها به عملیاتی کند و سنگین نیاز داشته باشد.
  • حفاظت از شبکه ضعیف است. همان‌طور که پیش‌تر گفتیم، این رویکرد «advisory» بود، قطعا توسط برخی برنامه‌ها که پشته شبکه خودشان را پیاده‌سازی می‌کردند دور زده می‌شد، و برای مقاومت در برابر کد خصمانه طراحی نشده بود.

سه مسئله اول ذاتا به یک پیاده‌سازی سندباکس سفارشی مربوط می‌شوند که برای جریان‌های عمل‌محور به اندازه کافی انعطاف‌پذیر باشد. اما داستان سرکوب شبکه متفاوت بود.

سرکوب شبکه بیش از حد مهم است

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

برای به‌دست آوردن سرکوب شبکه بهتر، می‌خواستیم از فایروال ویندوز استفاده کنیم، چون به ما اجازه می‌دهد ترافیک خروجی شبکه را برای کاربران یا برنامه‌ها مسدود کنیم. متأسفانه، به چند دلیل نمی‌توانستیم به‌طور مؤثر یک قانون فایروال کاربردی بسازیم که فقط به فرمان‌هایی اعمال شود که محیط اجرای Codex اجرا می‌کند:

  • ویندوز اجازه نمی‌دهد یک قانون فایروال با هویت غیراصلی یک توکن محدودشده تطبیق داده شود. این یعنی نمی‌توانستیم یک قانون فایروال را برای «هر توکنی که SID ما را در فهرست SID محدودشده خود دارد» اعمال کنیم.
  • با این‌که می‌توانستیم یک قانون فایروال بسازیم که با یک باینری مشخص تطبیق پیدا کند، این فقط به ما اجازه می‌داد شبکه codex.exe را محدود کنیم. این کار بر فرایندهایی که عامل از طرف کاربر راه‌اندازی می‌کند اعمال نمی‌شود، مانند فرایندهای Git یا Python.
  • سایر ابعاد تطبیق فایروال نیز ساختار نادرستی داشتند. قوانین در محدوده کاربر، در طراحی بدون ارتقای سطح دسترسی، همچنان با کاربر واقعی ویندوز تطبیق داده می‌شدند، نه فقط با پردازه فرزند محدودشده. قوانین مبتنی بر مسیر برنامه بیش‌ازحد کلی بودند: می‌توانستند codex.exe یا python.exe را به‌طور کلی مسدود کنند، اما نه این اجرای خاص python.exe در محیط سندباکس. قواعد مبتنی بر پورت یا آدرس نیز اساساً خط‌مشی کاملاً نادرستی محسوب می‌شدند. برای مثال، ما نمی‌خواستیم درگاه ۴۴۳ را مسدود کنیم؛ می‌خواستیم دسترسی خروجی دلخواه را برای این درخت فرایند محدودشده خاص مسدود کنیم.

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

طراحی مجدد: «سندباکس ارتقایافته»

نسخه بعدی سندباکس، که پیاده‌سازی فعلی ما است، در زمان راه‌اندازی اولیه به مجوزهای مدیریتی ارتقایافته نیاز دارد. بنابراین از آن با عنوان «سندباکس ارتقایافته» یاد می‌کنم. در مرزی که Codex یک فرمان را روی سیستم اجرا می‌کند، سندباکس دارای سطح دسترسی ارتقایافته شبیه سندباکس بدون ارتقای سطح دسترسی به نظر می‌رسد. این همچنان فرایندهای فرزند را تحت یک توکن محدودشده اجرا می‌کند—به‌طور مشابه، یک توکن write_restricted با همان فهرست SIDهای محدودشده [Everyone, Logon, Synthetic]—بااین‌حال، هویت اصلی این توکن دیگر کاربر واقعی ویندوز نیست، بلکه یکی از دو کاربر محلی است که خود Codex ایجاد کرده است:

  • CodexSandboxOffline (کاربری که قوانین فایروال او را هدف می‌گیرند)
  • CodexSandboxOffline (کاربری که قوانین فایروال او را هدف نمی‌گیرند)

این جزئیات که ظاهرا کوچک به نظر می‌رسند، در واقع برای سندباکس، کسانی که می‌توانند از آن استفاده کنند، و پیچیدگی راه‌اندازی و اجرای زمان‌اجرای آن پیامدهای بزرگی دارد.

نموداری که موارد نادیده‌گرفته‌شده محیط شبکه را برای سندباکس بدون دسترسی ارشد نشان می‌دهد.

از نظر بصری شبیه نمونه اولیه بدون ارتقاست، با این تفاوت که قوانین فایروال و یک کاربر اختصاصی ویندوز به آن اضافه شده‌اند؛ همان کاربری که عملا فرمان‌ها را اجرا می‌کند. (البته اضافه شدن این مفاهیم جدید یعنی پیش از آن‌که سندباکس بتواند فرمان‌ها را اجرا و از آن‌ها محافظت کند، کارهای راه‌اندازی بیشتری باید انجام شود.)

اکنون به یک گام راه‌اندازی درجه‌یک نیاز داریم

طراحی سندباکس بدون ارتقا یک گام راه‌اندازی ساده داشت، اما آن گام نسبتا کوچک بود:

  • در صورت نیاز، یک SID مصنوعی بسازید
  • ACLها را برای SID مصنوعی سندباکس-نوشتن اعمال کنید

اما سندباکس ارتقایافته کارهای بیشتری برای انجام دادن دارد.

  • ایجاد یک SID مصنوعی، اگر قبلا ساخته نشده
  • ایجاد کاربران سندباکس آنلاین و آفلاین، اگر قبلاً ساخته نشده‌اند
  • ذخیره اعتبارنامه‌های کاربران تازه‌ساخته‌شده را به‌صورت محلی و رمزگذاری با استفاده از Windows Data Protection API (DPAPI) (رابط برنامه‌نویسی کاربردی ویندوز) در جایی که کاربران سندباکس واقعا نتوانند آن را بخوانند
  • ساختن قوانین فایروالی که همه دسترسی خروجی شبکه را برای کاربر CodexSandboxOffline مسدود می‌کنند یا، اگر از قبل وجود دارند، اعتبارسنجی درستی آن‌ها

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

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

  • C:\Users\<real-user>
  • C:\Windows\
  • C:\Program Files\
  • C:\Program Files (x86)\
  • C:\ProgramData\

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

ما منطق راه‌اندازی را در باینری جداگانه خودش کپسوله کردیم، تا حدی به این دلیل که فقط در صورت نیاز از مرز UAC عبور کنیم. اما دلیل عمیق‌تر، معماری بود: راه‌اندازی سندباکس اساساً وظیفه‌ای متفاوت از codex.exe دارد. نگه داشتن منطق راه‌اندازی سندباکس در یک باینری اختصاصی اجازه داد codex.exe یک مهارکننده عادی و بدون دسترسی ارشد باقی بماند؛ ماشین‌های راه‌اندازی مختص ویندوز، codex.exe را روی سایر پلتفرم‌ها حجیم نکنند؛ کارهای راه‌اندازی طولانی‌مدت از طول عمر فرایند اصلی جدا شوند؛ و یک محل واحد برای رسیدگی به مسیرهای مختلف راه‌اندازی موردنیاز سندباکس داشته باشیم.

نموداری که مرحله راه‌اندازیِ درجه‌یکِ sandbox ارتقایافته را نشان می‌دهد.

اجراکننده فرمان یک باینری جدید است که عملا فرمان‌های کاربر را اجرا می‌کند

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

  • codex.exe به‌عنوان کاربر واقعی ویندوز اجرا می‌شود. سپس، Codex به‌ترتیب:
    • LogonUserW(...) را برای کاربر سندباکس فراخوانی می‌کند.
    • تابع CreateRestrictedToken(...) را روی آن توکن کاربر سندباکس فراخوانی می‌کند.
    • با استفاده از آن توکن محدود کاربر سندباکس، CreateProcessAsUserW(...) را فراخوانی می‌کند تا پردازه فرزند نهایی را اجرا کند.

در عمل، آن جریان مطلوب به‌دلیل وجود مانع سطح دسترسی در CreateProcessAsUserW(...) کار نکرد. این یعنی codex.exe می‌توانست برای کاربر سندباکس یک توکن محدود ایجاد کند، اما نمی‌توانست از سمت کاربر واقعی مرز، یک فرایند فرزند را با آن توکن به‌طور قابل اعتماد راه‌اندازی کند. ما به فرایندی نیاز داشتیم که از قبل به‌عنوان کاربر سندباکس در حال اجرا باشد—این باعث می‌شد مرحله اعمال محدودیت و ایجاد نهایی فرایند در سمت کاربر سندباکس مرز انجام شود، نه در سمت کاربر واقعی.

آن نیاز منجر به codex-command-runner.exe شد؛ یک باینری جدید که تنها وظیفه‌اش ایجاد یک توکن محدودشده و اجرای فرمان درخواست‌شده است. به‌جای این‌که از codex.exe بخواهیم کل جریان را خودش انجام دهد (کاربر واقعی ← کاربر سندباکس ← توکن محدود‌شده ← فرایند فرزند)، این جریان را به دو بخش تقسیم کردیم:

بخش ۱

  • codex.exe ، CreateProcessWithLogonW(...) را فراخوانی می‌کند تا codex-command-runner.exe را به‌عنوان کاربر سندباکس اجرا کند، بدون این‌که هنوز از توکن محدود‌شده‌ای استفاده شود.

بخش ۲

  • داخل اجراکننده، تابع OpenProcessToken(GetCurrentProcess(), ...) توکن خود اجراکننده را باز می‌کند؛ توکنی که از قبل متعلق به کاربر سندباکس است.
  • اجراکننده تابع GetTokenInformation(...) را برای استخراج SID ورود سندباکس فراخوانی می‌کند و سپس CreateRestrictedToken(...) را فراخوانی می‌کند تا توکن محدود نهایی را بسازد.
  • اجراکننده همچنان در داخل خودش، با استفاده از آن توکن محدود‌شده، تابع CreateProcessAsUserW(...) را فراخوانی می‌کند تا فرزند واقعی را اجرا کند.
نموداری که جریان اجراکننده فرمان را برای اجرای فرمان‌های محدود‌شده نشان می‌دهد.

تصویر کلی

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

  • خود codex.exe
  • codex-windows-sandbox-setup.exe برای رسیدگی به همه کارهای راه‌اندازی مرتبط با سطح دسترسی ارشد
  • codex-command-runner.exe برای اجرای فرمان‌های توکن محدودشده
  • فرایند فرزند

وقتی نخستین بار با این پروژه آشنا شدم، تصور روشنی از این‌که در نهایت به کجا می‌رسد نداشتم. رویکرد من این بود که از ابزارگذاری قابلیت سندباکسینگ در مرز میان Codex و سیستم‌عامل شروع کنم. این رویکرد بسیار نزدیک به نحوه پیاده‌سازی سندباکس در Codex روی MacOs و Linux است.

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

این سیستم چندان ساده نیست، اما هر بخش از پیچیدگی آن از روی ضرورت اضافه شده تا سندباکسی ساخته شود که هم ایمن باشد و هم تا حد ممکن مزاحم کارهای کاربر نشود.

نموداری که معماری نهایی سندباکس ویندوز را نشان می‌دهد.

متعادل کردن ایمنی با کارایی واقعی

در تلاش برای ارائه تجربه کاربری خوب برای کاربران Codex در ویندوز، هدف ما این بود که چیزی امن بسازیم که از کارایی کوتاه نیاید—کل هدف استفاده از Codex این است که عامل‌ها بتوانند بدون نیاز به توجه دائمی شما کار انجام دهند.

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

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

آیا کنجکاوید سندباکس مربوط به Codex را در عمل ببینید؟ آن را امتحان کنید.