عنوان:

‫گذر از کلیدهای ثابت به احراز هویت بدون رمز (Secretless): راهنمای پیاده‌سازی OIDC در انتشار بسته‌های NuGet


نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۵/۱۴ ۱۱:۳۵
آدرس: www.dntips.ir
چکیده: با اعلام سیاست‌های جدید مایکروسافت و مایکروسافت نُگِت (NuGet.org)، طول عمر کلیدهای دسترسی API از تاریخ ۱۷ اوت به حداکثر ۳۰ روز کاهش یافته و کلیه کلیدهای طولانی‌مدت قدیمی در اول نوامبر غیرفعال می‌شوند. این تغییر سیاست، چالش‌های عملیاتی متعددی را برای چرخه‌های یکپارچه‌سازی و تحویل مداوم (CI/CD) و مدیریت سنتی کلیدها در GitHub Actions ایجاد می‌کند. راهکار مدرن و ایمن مایکروسافت برای حل این مشکل، استفاده از پروتکل OpenID Connect (OIDC) و قابلیت Trusted Publishing است. در این مقاله، ضمن بررسی تعاملی این الگوی احراز هویت بدون کلید (Secretless)، شیوه تنظیم آن بر روی بسته‌های دات‌نت (.NET) و خودکارسازی چرخه انتشار در پلتفرم GitHub Actions تشریح می‌شود.

۱. مقدمه
در زیست‌بوم توسعه نرم‌افزار، مدیریت کلیدهای دسترسی (API Keys) و رمزها (Secrets) همواره یکی از نقاط ضعف اصلی در امنیت زنجیره تأمین (Supply Chain Security) بوده است. ذخیره‌سازی کلیدهای طولانی‌مدت در تنظیمات مخازن یا Secret Managerها، خطر افشای ناخواسته، عدم چرخش منظم (Key Rotation) و دسترسی‌های غیرمجاز را افزایش می‌دهد.
سیاست اخیر NuGet مبنی بر محدودسازی طول عمر کلیدها به ۳۰ روز، توسعه‌دهندگان اکوسیستم دات‌نت را ملزم می‌سازد تا فرایندهای دستی دستیابی و بروزرسانی کلیدها را کنار بگذارند. قابلیت Trusted Publishing بر پایه استاندارد OpenID Connect (OIDC)، با حذف کامل نیاز به کلیدهای ثابت و جایگزینی آن با احراز هویت مبتنی بر هویت مبتنی بر ادعا (Claim-based Identity)، امنیت و پایداری چرخه انتشار بسته‌ها را ارتقا می‌دهد.

۲. مکانیسم عملکرد OIDC و Trusted Publishing
در روش سنتی، مخزن GitHub یک کلید ثابت متنی را در درخواست Push به سمت NuGet ارسال می‌کرد. اما در الگوی OIDC، احراز هویت طی یک چرخه سه جانبه و امن صورت می‌پذیرد:
[GitHub Runner] ---- (۱. درخواست Token با ادعاهای OIDC) ----> [GitHub OIDC Provider]
[GitHub Runner] <--- (۲. دریافت JSON Web Token / JWT امضا شده) -- [GitHub OIDC Provider]
[GitHub Runner] ---- (۳. تبادل JWT با کلید موقت ۱ ساعته) -----> [NuGet.org]
[GitHub Runner] <--- (۴. دریافت Short-lived API Key) -------- [NuGet.org]
  • صدور توکن ادعا (OIDC Token): هنگام اجرای Workflow در GitHub Actions، موتور اجراکننده از OIDC Providerِ اختصاصی GitHub درخواست یک توکن امضاشده (JWT) می‌کند. این توکن حاوی مشخصاتی چون نام مخزن، نام مالک، شاخه (Branch) و نام فایل Workflow است.
  • اعتبارسنجی توسط NuGet: توکن به NuGet.org ارسال می‌شود. NuGet امضای دیجیتال توکن را بررسی کرده و ادعاهای داخل آن را با «سیاست اعتماد» (Trust Policy) که قبلاً در حساب کاربری ثبت کرده‌اید، تطبیق می‌دهد.
  • تولید کلید کوتاه‌مدت: در صورت انطباق کامل اطلاعات، NuGet یک کلید API یک‌بارمصرف با اعتبار یک ساعته صادر کرده و بسته .nupkg را دریافت می‌کند.

۳. مراحل گام‌به‌گام پیاده‌سازی
گام اول: پیکربندی Trust Policy در NuGet.org
پیش از تغییر در کدها، باید رابطه اعتمادی میان مخزن GitHub و حساب NuGet برقرار شود:
  • وارد حساب کاربری خود در NuGet.org شده و به بخش Account Settings (یا Organization Settings) Trusted Publishing بروید.
  • گزینه Add Policy را انتخاب کرده و فیلدها را به شکل زیر تنظیم کنید:
  • Repository Owner: نام کاربری یا سازمان در گیت‌هاب (مثلاً my_name).
  • Repository: نام دقیق مخزن پروژه‌تان (مثلاً PersianCalendar).
  • Workflow File: نام دقیق فایل ورک‌فلو بدون مسیر (مثلاً publish.yml).
  • Environment (اختیاری): در صورت استفاده از GitHub Environments (مانند release) نام آن را ذکر کنید.
نکته برای مخازن خصوصی (Private Repositories): زمانی که برای یک مخزن خصوصی policy تعریف می‌کنید، NuGet آن را به مدت ۷ روز فعال موقت نگه می‌دارد. پس از اولین انتشار موفق، این وضعیت به صورت دائمی تثبیت می‌شود.

گام دوم: پیاده‌سازی GitHub Actions Workflow
فایل .github/workflows/publish.yml را در پروژه خود ایجاد یا ویرایش کنید.
مهم‌ترین بخش این فایل، اعطای دسترسی permissions: id-token: write است تا Runner مجاز به دریافت توکن OIDC باشد:
name: Publish to NuGet

on:
  release:
    types: [published] # اجرا هنگام انتشار یک Release جدید
  workflow_dispatch:      # امکان اجرای دستی ورک‌فلو

jobs:
  publish:
    runs-on: ubuntu-latest
    environment: release
    permissions:
      contents: read
      id-token: write # الزام دسترسی برای احراز هویت OIDC

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup .NET SDK
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '9.0.x' # نسخه SDK مورد نظر شما

      - name: Restore dependencies
        run: dotnet restore

      - name: Build project
        run: dotnet build --configuration Release --no-restore

      - name: Run Tests
        run: dotnet test --configuration Release --no-build

      - name: Pack Package
        run: dotnet pack --configuration Release --no-build --output ./artifacts

      # ۱. تبادل توکن OIDC با کلید موقت ۱ ساعته NuGet
      - name: NuGet Login via OIDC
        id: login
        uses: NuGet/login@v1
        with:
          user: ${{ secrets.NUGET_USER }} # نام کاربری پروفایل NuGet (نه ایمیل)

      # ۲. ارسال بسته به NuGet با کلید موقت دریافت شده
      - name: Push Package to NuGet.org
        run: |
          dotnet nuget push ./artifacts/*.nupkg \
            --source https://api.nuget.org/v3/index.json \
            --api-key "${{ steps.login.outputs.NUGET_API_KEY }}" \
            --skip-duplicate

گام سوم: تعریف متغیر NUGET_USER
در این الگوی جدید، نیازی به ذخیره کلیدهای طولانی‌مدت (API Key) در Secrets نیست؛ تنها کافی است نام کاربری خود در NuGet را ثبت کنید:
  • در مخزن گیت‌هاب به Settings ، Secrets and variables ، Actions بروید.
  • یک Secret جدید با نام NUGET_USER تعریف کرده و مقدار آن را برابر با نام کاربری خود در NuGet (برای مثال my_name) قرار دهید.

۴. نکات تکمیلی
برای تبدیل این چرخه به یک خط لوله تمام‌عیار و حرفه‌ای، رعایت موارد زیر توصیه می‌شود:
  • خودکارسازی شماره‌گذاری نسخه (Versioning Automation):
  • به جای تغییر دستی نسخه در فایل .csproj پیش از هر انتشار، پیشنهاد می‌شود از ابزارهایی مانند MinVer یا GitVersion استفاده کنید. این ابزارها بر اساس Tagهای Git (مانند v1.2.0)، نسخه بسته را به صورت خودکار در زمان Build تعیین می‌کنند.
  • امضای دیجیتال بسته‌ها (Package Signing):
  • علاوه بر احراز هویت OIDC در زمان انتشار، می‌توانید با استفاده از گواهی‌های دیجیتال (Code Signing Certificate) و دستور dotnet nuget sign بسته‌های خود را امضا کنید تا اصالت فایل پس از دانلود توسط کاربران قابل راستی‌آزمایی باشد.
  • استفاده از سیستم Reproducible Builds:
  • افزودن تنظیمات در فایل پروژه دات‌نت هنگام اجرا در محیط CI، باعث می‌شود که فایل‌های DLL و nupkg تولیدشده کاملاً قطعی و بدون وابسته بودن به محیط سیستم‌عامل محلی امضا و بسته بندی شوند.

۵. نتیجه‌گیری
محدودسازی طول عمر API Keyها توسط مایکروسافت، گامی جدی در راستای ارتقای امنیت در اکوسیستم .NET است. اگرچه این تغییر ممکن است در نگاه اول یک چالش عملیاتی به نظر برسد، اما انتقال به Trusted Publishing و OIDC نه تنها چالش چرخش دستی کلیدها را برای همیشه حل می‌کند، بلکه با حذف رمزهای ثابت از مخازن، زیرساخت CI/CD پروژه‌ها را با استانداردهای مدرن امنیت زنجیره تأمین همگام می‌سازد.

نظرات

  • وحید نصیری در ۱۴۰۵/۰۵/۲۲ ۱۳:۵۴
    یک نکته‌ی تکمیلی: نحوه‌ی تکمیل پارامترهای action گیت‌هاب

    اگر پس از ایجاد یک policy جدید در قسمت trusted publishing ، به چنین خروجی رسیدید:


    الف) متغیر secrets.NUGET_USER تعریف شده‌ی در فایل workflows/publish.yml، دقیقا به مقدار package owner فوق اشاره می‌کند (که در مخزن گیت‌هاب در قسمت Settings ، Secrets and variables ، Actions با کلید NUGET_USER قابل تعریف است).
    ب) همچنین ذکر دقیق environment: release بر اساس جزئیات policy‌ تعریف شده، در اکشن گیت‌هاب (فایل yml ایجاد شده)، ضروری است که در مثال ذکر شده‌ی در مقاله‌ی فوق، قابل مشاهده‌است.