عنوان:

‫امنیت نشست‌ها در عصر جدید وب: بررسی قابلیت آزمایشی DBSC در ASP.NET Core 11x


نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۶/۲۴ ۰۹:۳۵
آدرس: www.dntips.ir
چکیده: سرقت کوکی‌های احراز هویت (Session Hijacking / Cookie Theft) به‌ویژه از طریق بدافزارهای سارق اطلاعات (Infostealers)، یکی از چالش‌های بنیادین و پرخسارت در امنیت برنامه‌های تحت وب به شمار می‌رود. راهکار پیشنهادی وب نوین برای مقابله با این تهدید، استاندارد اعتبارنامه‌های نشست وابسته به دستگاه (Device Bound Session Credentials یا به اختصار DBSC) است. فریم‌ورک ASP.NET Core در نسخه .NET 11 (با انتشار نسخه آزمایشی پکیج در RC1)، پیاده‌سازی سمت سرور این پروتکل را معرفی کرده است. در این مقاله، به بررسی مکانیسم امنیتی DBSC، معماری لایه‌ای آن بر روی احراز هویت مبتنی بر کوکی، نحوه عملکرد اثبات مالکیت با کلید خصوصی سخت‌افزاری، و پیاده‌سازی گام‌به‌گام آن در ASP.NET Core می‌پردازیم.

۱. مقدمه
در مدل‌های سنتی احراز هویت مبتنی بر کوکی (Cookie-based Authentication)، کوکی نقش یک توکن حامل (Bearer Token) را ایفا می‌کند؛ به این معنا که هر نهادی که به مقدار کوکی دسترسی داشته باشد—صرف‌نظر از اینکه کاربر واقعی باشد یا بدافزاری که کوکی را از حافظه سیستم یا پایگاه داده مرورگر استخراج کرده است—می‌تواند تا زمان منقضی شدن کوکی، خود را به جای کاربر جا بزند و درخواست‌های معتبر ارسال کند. استفاده از پرچم‌های امنیتی مانند HttpOnly و SameSite خطر حملاتی نظیر XSS و CSRF را کاهش می‌دهد، اما نمی‌تواند مانع سرقت فیزیکی یا آلودگی‌های محلی از طریق تروجان‌ها و Infostealerها شود.
پروتکل DBSC که ابتکاری استانداردشده در وب نوین است، مفهوم نشست را از یک توکن حامل ساده به یک سازوکار اثبات مالکیت (Proof of Possession) مبتنی بر رمزنگاری نامتقارن ارتقا می‌دهد. هدف DBSC این است که چرخه تمدید نشست را مستقیماً به کلید خصوصی ذخیره‌شده در لایه سخت‌افزاری یا محافظت‌شده مرورگرِ همان دستگاه وابسته کند.

۲. مکانیسم عملکرد DBSC چگونه است؟


در پروتکل DBSC، ارتباط میان کلاینت و سرور به دو نوع نشانه (Credential) تفکیک می‌شود:
کوکی نشست با عمر کوتاه (Short-Lived Session Cookie):
  • این کوکی برای درخواست‌های عادی کلاینت استفاده می‌شود، اما طول عمر آن بسیار کوتاه است (برای مثال ۱۰ دقیقه). در صورتی که این کوکی سرقت شود، مهاجم تنها در یک بازه زمانی بسیار محدود و کوتاه امکان سوءاستفاده دارد.

کلید نامتقارن سمت کلاینت و کوکی تمدید (Refresh Cookie):
  • هنگام ثبت نشست اولیه، مرورگر یک جفت‌کلید رمزنگاری (Public/Private Key) تولید می‌کند. کلید خصوصی درون مرورگر (و در صورت امکان در ماژول‌های سخت‌افزاری مانند TPM) امن نگه داشته می‌شود و هرگز از سیستم کاربر خارج نمی‌شود. برای تمدید کوکی کوتاه‌مدت، مرورگر باید با استفاده از کلید خصوصی خود، درخواستی امضا شده تحت عنوان اثبات مالکیت (Proof of Possession) به اندپوینت تمدید سرور ارسال کند.

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


۳. پشتیبانی از DBSC در ASP.NET Core
مایکروسافت پیاده‌سازی سمت سرور این استاندارد را در قالب پکیج آزمایشی زیر ارائه کرده است:
<PackageReference Include="Microsoft.AspNetCore.Authentication.DeviceBoundSessions" Version="0.11.0-rc.1.26427.112" />
نکته وضعیت انتشار: این بسته در طول فاز توسعه .NET 11 آزمایشی (Experimental / Prerelease) خواهد ماند تا زمانی که خصوصیات پروتکل در مراجع وب تثبیت شود.

معماری پیاده‌سازی در ASP.NET Core
کامپوننت DBSC به جای جایگزینی سیستم احراز هویت پیشین، به عنوان یک لایه‌ی تکمیلی (Layering Component) بر روی شمای موجود احراز هویت با کوکی (CookieAuthenticationScheme) قرار می‌گیرد. این میان‌افزار وظایف زیر را مدیریت می‌کند:
  • ایجاد و پایش اندپوینت‌های ثبت‌نام دستگاه (Registration) و تمدید (Refresh).
  • تنظیم و هدایت کوکی با دامنه مسیر خاص برای عملیات Refresh (Path-scoped refresh cookie).
  • نظارت بر صدور و انقضای کوکی‌های نشست کوتاه‌مدت (Short-Lived Cookies).

۴. پیکربندی و راه‌اندازی در کد
پیکربندی DBSC بسیار ساده طراحی شده و با زنجیره Fluent متدهای احراز هویت ASP.NET Core یکپارچه است. در فایل Program.cs:
var builder = WebApplication.CreateBuilder(args);

// افزودن سرویس‌های احراز هویت و پیکربندی لایه DBSC
builder.Services
    .AddAuthentication("Application")
    .AddCookie("Application", options =>
    {
        // تنظیمات عمومی کوکی مانند مسیر لاگین
        options.LoginPath = "/Account/Login";
    })
    .AddDeviceBoundSession("Application", options =>
    {
        // تعیین طول عمر کوکی نشست کوتاه‌مدت
        options.ShortLivedCookieExpiration = TimeSpan.FromMinutes(10);
    });

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();
در قطعه کد بالا:
  • متد AddDeviceBoundSession با ارسال نام طرح احراز هویت کوکی ("Application")، لایه DBSC را به آن متصل می‌کند.
  • ویژگی ShortLivedCookieExpiration مدت اعتبار کوکی اصلیِ نشست را محدود می‌سازد (در این سناریو ۱۰ دقیقه). پس از پایان این بازه، مرورگر به‌صورت پس‌زمینه و شفاف برای کاربر، عملیات تمدید امضاشده را اجرا می‌کند.

۵. پیش‌نیازها و نکات تکمیلی در پیاده‌سازی عملیاتی
جهت استفاده و آزمایش مؤثر این قابلیت، نکات زیر حائز اهمیت است:
پشتیبانی مرورگر (Browser-Side Support):
  • این پروتکل نیازمند پیاده‌سازی متقابل در مرورگر کاربر است. در حال حاضر، مرورگرهایی نظیر Chromium/Google Chrome در حال پیاده‌سازی آزمایشی این قابلیت هستند و برای تست آن، ممکن است نیاز به فعال‌سازی Flagهای مربوط به DBSC در تنظیمات مرورگر (مانند chrome://flags) باشد.
پایداری وضعیت کلاینت‌ها:
  • در صورتی که کلاینتی فاقد پشتیبانی از DBSC باشد، سیستم معمولاً بر اساس پیکربندی، یا به حالت رفتار سنتی کوکی بازمی‌گردد (Graceful Fallback) یا با رد درخواست، سیاست‌های سخت‌گیرانه‌تری اعمال می‌کند.
همگام‌سازی ساعت سرور (Clock Skew):
  • از آنجا که طول عمر کوکی‌ها کوتاه است (مثلاً ۵ تا ۱۰ دقیقه) و اعتبارسنجی امضای کلاینت به برچسب‌های زمانی (Timestamp) وابسته است، همگام‌بودن ساعت سرور و کلاینت‌ها از اهمیت بالاتری نسبت به نشست‌های طولانی برخوردار است.

۶. نتیجه‌گیری
پروتکل DBSC گامی اساسی در ارتقای امنیت وب به سمت مدل‌های احراز هویت بدون امکان سوءاستفاده از سرقت توکن است. پیاده‌سازی ارائه‌شده در ASP.NET Core نشان‌دهنده همگامی مایکروسافت با آخرین استانداردهای امنیتی وب است. با وجود اینکه این قابلیت در حال حاضر در وضعیت پیش‌انتشار و آزمایشی قرار دارد، به معماران و توسعه‌دهندگان نرم‌افزار این امکان را می‌دهد تا زیرساخت‌های احراز هویت نرم‌افزارهای خود را برای نسل آینده وب مقاوم‌سازی کرده و وابستگی امنیت نشست‌ها را از مدل شکننده حامل (Bearer) به مدل قدرتمند و رمزنگاری‌شده وابسته به دستگاه (Proof-of-Possession) ارتقا دهند.

نظرات

  • وحید نصیری در ۱۴۰۵/۰۶/۲۵ ۰۸:۲۹
    این مطلب تکمیلی، بخش‌های عمیق پروتکل و تبادلات سطح شبکه (Wire Protocol) استاندارد DBSC را که در متن پیشین بررسی نشده بودند، پوشش می‌دهد:

    کالبدشکافی پروتکل DBSC: بررسی عمیق ساختار هدرها، توکن‌های JWT و چرخه تبادل شبکه

    ۱. اصل ارتقای تدریجی (Progressive Enhancement) و عدم شکست سازگاری
    یکی از ظرافت‌های طراحی DBSC، پیاده‌سازی آن به صورت ارتقای تدریجی است:
    • احراز هویت اولیه همچنان با همان منطق مرسوم (فرم لاگین، OAuth/OIDC و...) انجام می‌شود.
    • فعال‌سازی قابلیت تنها وابسته به ارسال یک هدر اختیاری از سمت سرور است؛ مرورگرهایی که پروتکل را نمی‌شناسند، به‌سادگی هدر مربوطه را نادیده گرفته و با همان کوکی سنتی ادامه می‌دهند. بنابراین، اضافه شدن DBSC هیچ‌گونه تغییر مخربی (Breaking Change) در رفتار سیستم ایجاد نمی‌کند.

    ۲. گام‌های تبادل پیام در سطح پروتکل HTTP
    چرخه عملیاتی پروتکل DBSC شامل ۴ مرحله دقیق تبادل هدر و توکن است:
    [Browser / Client]                                             [Server]
            |                                                          |
            | ----- (1) Initial Login Request -----------------------> |
            | <---- (2) Set-Cookie + Secure-Session-Registration ----- |
            |                                                          |
            | ----- (3) POST /dbsc/registration (JWT + Cookie) ------> |
            | <---- (4) 200 OK + JSON Instructions + Short-Lived Cookie|
            |                                                          |
            |  ... [Subsequent normal requests using Short-Lived Cookie] ...
            |                                                          |
            | ----- (5) POST /dbsc/refresh (Sec-Secure-Session-Id) --> |
            | <---- (6) 403 Forbidden + Secure-Session-Challenge ----- |
            |                                                          |
            | ----- (7) POST /dbsc/refresh (Signed JWT Challenge) ---> |
            | <---- (8) 200 OK + New Short-Lived Cookie -------------- |

    گام اول: فراخوانی اولیه و اعلام آمادگی سرور
    هنگام لاگین موفق، سرور علاوه بر کوکی احراز هویت همیشگی، هدر Secure-Session-Registration را ارسال می‌کند:
    HTTP/1.1 200 OK
    Secure-Session-Registration: (ES256 RS256); path="/dbsc/registration"; challenge="4a96e2cab06e76e2366b5e802bfcbabfe81e52c81abfcc71afc97010157ae9bd"
    Set-Cookie: MyAuthCookie=CfDJ8Apn9==; Path=/; SameSite=Lax; HttpOnly
    • (ES256 RS256): الگوریتم‌های رمزنگاری امضایی که سرور برای مرورگر مجاز دانسته است (مانند ECDSA P-256 یا RSA).
    • path: اندپوینتی که مرورگر باید برای ثبت نشست به آن درخواست بفرستد.
    • challenge: یک رشته تصادفی (Nonce/Challenge) که مرورگر ملزم به امضای آن جهت اثبات تصاحب کلید است.

    گام دوم: ثبت نشست توسط مرورگر با JWT مبتنی بر سخت‌افزار
    مرورگر پس از دریافت هدر، جفت‌کلید جدیدی درون سخت‌افزار امن (مانند TPM) ایجاد کرده و challenge را با کلید خصوصی امضا می‌کند. سپس یک درخواست به اندپوینت ثبت با هدر Secure-Session-Response ارسال می‌کند که حاوی یک توکن متقارن JWT از نوع dbsc+jwt است:
    POST /dbsc/registration HTTP/1.1
    Cookie: MyAuthCookie=CfDJ8Apn9==
    Secure-Session-Response: eyJhbGciOiJFUzI1NiIsImp3ayI6eyJ...

    ساختار رمزگشایی‌شده JWT:
    // Header
    {
      "alg": "ES256",
      "jwk": {
        "crv": "P-256",
        "kty": "EC",
        "x": "lHN3ar-wlVSSAdy8OJXqxhHwBiuur2rTlmbxcgim9_o",
        "y": "zf_y79p8rpb7ztnQGZ25xPX_Lq_r7o1Ch9jvne70tJ4"
      },
      "typ": "dbsc+jwt"
    }
    
    // Payload
    {
      "jti": "4a96e2cab06e76e2366b5e802bfcbabfe81e52c81abfcc71afc97010157ae9bd"
    }
    مرورگر در هدر JWT، کلید عمومی (jwk) خود را برای سرور ارسال می‌کند و در بدنه (jti)، مقدار چالش دریافتی را قرار می‌دهد.

    گام سوم: جایگزینی کوکی و پاسخ پیکربندی نشست
    سرور پس از بررسی امضا و انطباق چالش، نشست سنتی را باطل کرده و پاسخ پیکربندی نشست را همراه با کوکی موقت تحویل می‌دهد:
    HTTP/1.1 200 OK
    Content-Type: application/json
    Set-Cookie: MyDbscCookie=abc123; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=300
    
    {
      "session_identifier": "199d6f60681",
      "refresh_url": "/dbsc/refresh",
      "scope": {
        "origin": "https://example.com",
        "include_site": false
      },
      "credentials": [
        {
          "type": "cookie",
          "name": "MyDbscCookie",
          "attributes": "Path=/; Secure; HttpOnly; SameSite=Lax"
        }
      ]
    }
    • session_identifier: شناسه منحصربه‌فرد نشست DBSC که مرورگر باید در هر بار تمدید آن را ارائه دهد.
    • refresh_url: آدرسی که مرورگر برای دریافت کوکی جدید کوتاه‌مدت باید فراخوانی کند.
    • credentials: کوکی مورد نظر و مشخصات آن جهت اعمال در تراکنش‌ها.

    گام چهارم: الگوی بازخورد چالش دو مرحله‌ای در تمدید (Refresh Flow)
    تمدید کوکی یک مبادله دوطرفه هوشمند است:
    درخواست تمدید کلاینت: مرورگر شناسه نشست را به آدرس Refresh می‌فرستد:
    POST /dbsc/refresh HTTP/1.1
    Sec-Secure-Session-Id: 199d6f60681
    پاسخ سرور با وضعیت 403 Forbidden: سرور برای جلوگیری از Replay Attack و اطمینان از حضور کلاینت، درخواست را بلافاصله نمی‌پذیرد؛ بلکه یک خطای کنترل‌شده 403 همراه با چالش جدید برمی‌گرداند:
    HTTP/1.1 403 Forbidden
    Secure-Session-Challenge: "4524d32ab2b9"; id="199d6f60681"
    ارسال چالش امضاشده توسط کلاینت: مرورگر چالش را با کلید خصوصی داخل TPM امضا کرده و مجدداً ارسال می‌کند:
    POST /dbsc/refresh HTTP/1.1
    Sec-Secure-Session-Id: 199d6f60681
    Secure-Session-Response: <Signed-JWT-Containing-New-Challenge>
    صدور کوکی تازه: سرور امضا را با کلید عمومی ذخیره‌شده از گام دوم تطبیق داده و کوکی ۵ یا ۱۰ دقیقه‌ای جدیدی صادر می‌کند (200 OK).

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

    ۳. چالش‌های عملیاتی در دنیای واقعی
    • افزونه‌های مرورگر و Ad-Blockerها: با توجه به غیرمعمول بودن رفتارهایی مانند فراخوانی‌های پشت‌صحنه POST و مسیرهای خاص، برخی ابزارهای مسدودساز تبلیغات ممکن است به اشتباه درخواست‌های تمدید نشست یا ثبت نشست را مسدود کنند.
    • پیچیدگی پیاده‌سازی خام (Manual Implementation): مدیریت مواردی چون همگام‌سازی چالش‌ها، اعتبارسنجی JWT، مدیریت Race Conditionها در تمدید هم‌زمان، و تعامل با خطاهای شبکه بسیار حساس است. بنابراین توصیه می‌شود توسعه‌دهندگان به جای نوشتن دستی این فرآیند، از کتابخانه‌های رسمی مانند پکیج ارائه‌شده در ASP.NET Core استفاده کنند.
  • وحید نصیری در ۱۴۰۵/۰۶/۲۶ ۰۸:۲۲
    این مطلب تکمیلی بر نکات ناگفته، معماری درونی هندلر ASP.NET Core، مدیریت وضعیت (State Management) بدون دیتابیس و چالش‌های کارایی سخت‌افزاری تمرکز دارد.

    معماری داخلی DBSC در ASP.NET Core: رمزنگاری بدون وضعیت، سربار سخت‌افزاری و مدل‌های تهدید

    برخلاف تصور اولیه که پیاده‌سازی مکانیزم‌های اثبات تصاحب کلید (Proof-of-Possession) را نیازمند پایگاه‌داده متمرکز برای نگهداری کلیدهای عمومی و نانس‌ها می‌داند، پیاده‌سازی مایکروسافت در ASP.NET Core کاملاً بی‌وضعیت (Stateless) است. این لایه با تکیه بر لایه‌بندی سه‌گانه کوکی‌ها و سامانه ASP.NET Core Data Protection، همگام‌سازی کلاستری را بدون نیاز به حافظه اشتراکی حل کرده است.

    ۱. معماری لایه‌بندی سه‌گانه کوکی‌ها (Three-Cookie Architecture)
    هندلر AddDeviceBoundSession برای مدیریت چرخه حیات نشست، تعامل سه نوع کوکی مجزا را سازمان‌دهی می‌کند:
    • کوکی لاگین اولیه (Original Auth Cookie): کوکی استاندارد صادرشده در هنگام فراخوانی HttpContext.SignInAsync که کلاینت را به فرآیند ثبت DBSC هدایت می‌کند.
    • کوکی نشست کوتاه‌مدت (Short-Lived Session Cookie): کوکی سبک‌وزنی که برای احراز هویت درخواست‌های عادی وب استفاده شده و طول عمر آن با ShortLivedCookieExpiration (مثلاً ۱۰ دقیقه) محدود می‌شود.
    • کوکی تمدید با دامنه مسیر خاص (Path-Scoped Refresh Cookie): کوکی حساس و رمزنگاری‌شده‌ای که مرورگر تنها در زمان فراخوانی اندپوینت /.well-known/dbsc/refresh ارسال می‌کند.

    ۲. مدیریت بدون وضعیت نشست‌ها و کلیدها با Data Protection
    پکیج Microsoft.AspNetCore.Authentication.DeviceBoundSessions کلید عمومی دستگاه کاربر را در دیتابیس یا کش توزیع‌شده ذخیره نمی‌کند.
    • ذخیره کلید عمومی سمت کلاینت: در زمان ثبت نشست، مقدار کلید عمومی کلاینت (DbscPublicKeyJwk) به همراه شناسه نشست در ویژگی‌های احراز هویت (AuthenticationProperties) قرار داده شده و داخل کوکی تمدید (Refresh Cookie) مهروموم (Encrypt) می‌شود:
    refreshProperties.Items["DbscPublicKeyJwk"] = jwtResult.PublicKeyJwk;
    refreshProperties.Items["DbscSessionId"] = sessionId;
    await Context.SignInAsync(Options.RefreshScheme, principal, refreshProperties);
    • تأیید اصالت بدون نگهداری نانس (Stateless Nonces): سرور نانس‌های صادرشده در challenge را در حافظه ذخیره نمی‌کند. در عوض، هر چالش به صورت یک توکن رمزنگاری‌شده با Data Protection تولید می‌شود که حاوی هویت کاربر، شناسه نشست و تاریخ انقضا است. بررسی چالش صرفاً با رمزگشایی مجدد و راستی‌آزمایی فیلدهای درون آن انجام می‌گیرد.
    • جداسازی اهداف رمزنگاری (Purpose Isolation): چالش‌های ثبت اولیه (Registration) و تمدید (Refresh) از برچسب‌های Data Protection Purpose کاملاً مجزا استفاده می‌کنند؛ در نتیجه چالش صادرشده در یکی از جریان‌ها به هیچ وجه در جریان دیگر رمزگشایی و پذیرفته نمی‌شود.
    • مقیاس‌پذیری افقی (Horizontal Scalability): تمام سرورهای پشت لودبلانسر، در صورت اشتراک کلیدهای Data Protection، بدون نیاز به کش مشترک یا چسبندگی نشست (Sticky Sessions) قادر به تمدید کوکی‌ها هستند.

    ۳. مبادله کارایی و امنیت در تنظیم ShortLivedCookieExpiration
    تعیین طول عمر کوکی کوتاه‌مدت مستلزم ایجاد توازن میان امنیت و محدودیت‌های فیزیکی دستگاه است:
    • کاهش طول عمر: پنجره سوءاستفاده مهاجم در صورت سرقت کوکی کوتاه‌مدت را کوچک‌تر می‌کند.
    • سربار تراشه TPM: ماژول‌های سخت‌افزاری TPM سیستم‌های اشتراکی در سطح سیستم‌عامل هستند و برای جلوگیری از حملات منع سرویس محلی، نرخ عملیات رمزنگاری محدودی (Rate-Limiting) دارند.
    • هزینه فرکانس تمدید: انتخاب مقادیر بسیار کوتاه (مانند ۱ یا ۲ دقیقه) موجب افزایش چرخه‌های امضای نامتقارن، افزایش بار شبکه به دلیل فرآیند دو مرحله‌ای 403 Forbidden و ایجاد تأخیر در تجربه کاربری می‌شود. بازه پیش‌فرض ۱۰ دقیقه تعادلی بهینه میان امنیت و بار عملیاتی سخت‌افزار است.

    ۴. پیامدهای معماری و مرزهای امنیتی پروتکل
    • چالش ابطال نشست (Session Revocation): از آنجا که جدول وضعیت متمرکزی در سرور وجود ندارد، ابطال فوری نشست از طریق سرور میسر نیست؛ در صورت نیاز به لغو اضطراری، سامانه باید لیست سیاه نشانه‌ها را تا موعد تمدید بعدی بررسی کند.
    • حملات مبتنی بر دسترسی هم‌زمان (Malware Present at Registration): اگر پیش از ثبت‌نام DBSC، بدافزار روی ماشین قربانی حضور فعال داشته باشد، می‌تواند در حین نشست دستور امضا را به TPM ارسال کرده یا ترافیک رجیستریشن اولیه را شنود کند. DBSC دسترسی سرقت‌شده را محدود به همان دستگاه کرده و مانع استفاده مهاجم در ماشین‌های ثانویه می‌شود، اما جایگزین راهکارهای دفاع ضد بدافزار محلی نیست.
    • تفاوت تمدید با ثبت در ساختار JWT: توکن JWT ارسالی در درخواست تمدید برخلاف ثبت اولیه، فاقد شیء jwk در هدر خود است، چرا که سرور کلید عمومی را از درون کوکیِ تمدید بازگشایی می‌کند.
  • وحید نصیری در ۱۴۰۵/۰۷/۰۱ ۰۸:۴۸
    مطلب تکمیلی: یکپارچه‌سازی عملی با ASP.NET Core Identity

    کالبدشکافی DBSC در ASP.NET Core: یکپارچه‌سازی با Identity، لاگ‌های سرور و چرخه‌حیات Cookie Handlers
    ۱. نحوه یکپارچه‌سازی با ASP.NET Core Identity
    در پروژه‌های متداول، فراخوانی AddDefaultIdentity() یا AddIdentityCookies() زنجیره پیکربندی احراز هویت را در خود کپسوله می‌کند و شیء AuthenticationBuilder را مستقیماً برنمی‌گرداند. برای غلبه بر این محدودیت و اتصال لایه DBSC به کوکی پیش‌فرض Identity، می‌توان از الگوی نمونه‌سازی دستی AuthenticationBuilder استفاده کرد:
    // ثبت پیش‌فرض Identity
    builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    
    // متصل کردن لایه DBSC به شمای کوکی پیش‌فرض Identity
    new AuthenticationBuilder(builder.Services)
        .AddDeviceBoundSession(sourceScheme: IdentityConstants.ApplicationScheme, options =>
        {
            options.ShortLivedCookieExpiration = TimeSpan.FromMinutes(10);
        });
    پوشش چندگانه (Multi-Scheme Wrapping): متد AddDeviceBoundSession می‌تواند چندین بار با مقادیر مختلف فراخوانی شود؛ بنابراین در صورتی که سامانه از چند طرح کوکی مجزا استفاده کند، می‌توان هرکدام را به طور مستقل به DBSC مجهز نمود.

    ۲. ساختار داخلی طرح‌های احراز هویت (Behind the Schemes)
    زمانی که یک شمای مبدأ مانند IdentityConstants.ApplicationScheme به DBSC ارجاع داده می‌شود، پکیج سه ساختار زیر را در پشت صحنه رجیستر می‌کند:
    [ Incoming Request ]
            │
            ▼
    [ PolicySchemeHandler: Identity.Application.Dbsc ]
            │
            ├── آیا کوکی کوتاه‌مدت نشست در هدر موجود است؟
            │         ├── بله ──► مسیریابی به [ Identity.Application.Dbsc.Session ]
            │         └── خیر  ──► عقب‌نشینی به [ Identity.Application ] (Source Scheme)
            │
    [ Refresh Scheme: Identity.Application.Dbsc.Refresh ] (محدود به مسیر /.well-known/dbsc)
    • طرح کوکی نشست (Identity.Application.Dbsc.Session): این طرح مسئول مدیریت کوکی احراز هویت اصلی با طول عمر کوتاه است (با پیشوند نام .AspNetCore.Identity.Application.Dbsc.Session).
    • طرح کوکی تمدید (Identity.Application.Dbsc.Refresh): کوکی این طرح به‌طور سخت‌گیرانه به مسیر /.well-known/dbsc محدود (Path-scoped) شده و تنها در درخواست‌های تمدید خوانده می‌شود.

    مسیریاب پویای نشست (PolicySchemeHandler):
    • برای جلوگیری از شکست نشست در زمان لاگین کاربرانی که هنوز نشست DBSC را نهایی نکرده‌اند یا مرورگرهای فاقد پشتیبانی، یک PolicyScheme با عنوان {sourceScheme}.Dbsc تزریق می‌شود.
    • ویژگی ForwardDefaultSelector این طرح، وجود کوکی نشست کوتاه‌مدت را در درخواست ورودی جستجو می‌کند؛ در صورت یافتن نام کوکی، اعتبارسنجی را به طرح Session واگذار می‌کند و در غیر این صورت، درخواست را به طرح اولیه (Source Scheme) ارجاع می‌دهد تا خروج ناخواسته (Logout) رخ ندهد.

    ۳. نقش رویدادها وIPostConfigureOptionsدر تزریق رفتار
    اتصال خودکار رفتارها بدون نیاز به دستکاری کدهای کاربر، از طریق سه پیاده‌سازی داخلی از اینترفیس IPostConfigureOptions صورت می‌گیرد:
    • PostConfigureDeviceBoundSessionCookieOptions: با زنجیره‌سازی متد OnSigningIn، فراخوانی DeviceBoundSessionRegistrationHeader.Emit() را انجام می‌دهد تا هدر Secure-Session-Registration همراه با پاسخ لاگین ارسال شود. همچنین در رویداد خروج، حذف هر سه کوکی را برنامه‌ریزی می‌کند.
    • PostConfigureDeviceBoundSessionDerivedCookieOptions: پارامترهای پایه‌ای امنیتی نظیر HttpOnly، Secure، SameSite و دامنه را از کوکی اصلی خوانده و روی کوکی‌های نشست و تمدید اعمال می‌نماید.
    • PostConfigureDeviceBoundSessionAuthenticationOptions: نام شمای پیش‌فرض سیستم احراز هویت را به شمای سیاست‌گذاری (PolicyScheme) جدید هدایت می‌کند.

    ۴. چالش‌های عیب‌یابی و ردگیری درخواست‌ها (Observability & Debugging)
    تست و مشاهده رفتار DBSC در مرورگر و سرور به دلیل ماهیت آزمایشی و ملاحظات امنیتی با پیچیدگی‌هایی همراه است:
    نامرئی بودن تبادلات در Developer Tools مرورگر
    • کروم تبادلات اندپوینت‌های ثبت (/registration) و تمدید (/refresh) را در تب اصلی Network نمایش نمی‌دهد.
    • جهت ارزیابی فعال بودن پروتکل، باید به تب اختصاصی Device Bound Sessions در ابزار توسعه مرورگر مراجعه کرد؛ این بخش وضعیت نشست و وضعیت تصمیم‌گیری برای تعویق درخواست کاربر (Deferral Decision) را مشخص می‌کند.

    ردگیری وقایع از طریق لاگ‌های سرور (Server Trace)
    جهت مشاهده فرآیند، باید سطح لاگ کتابخانه‌های هاستینگ و احراز هویت روی Information تنظیم شود. جریان ثبت نشست اولیه در خروجی لاگ سرور به شرح زیر ثبت می‌گردد:
    info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
          Request starting HTTP/2 POST https://localhost:7029/.well-known/dbsc/registration - - 0
    info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[10]
          AuthenticationScheme: Identity.Application.Dbsc.Refresh signed in.
    info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[10]
          AuthenticationScheme: Identity.Application.Dbsc.Session signed in.
    info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[11]
          AuthenticationScheme: Identity.Application signed out.
    info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
          Request finished HTTP/2 POST https://localhost:7029/.well-known/dbsc/registration - 200 - application/json
    در این گزارش سه گام هم‌زمان ثبت شده است:
    • ورود موفق به طرح Refresh
    • ورود موفق به طرح Session
    • خروج و ابطال طرح بلندمدت پیشین (Identity.Application signed out)

    دخالت اکستنشن‌های مسدودکننده (Ad-Blockers)
    تست‌های تجربی نشان می‌دهد که برخی مسدودسازهای تبلیغات، درخواست‌های پس‌زمینه کلاینت به اندپوینت‌های /.well-known/dbsc را به دلیل شباهت رفتاری با ردیاب‌ها مسدود می‌کنند؛ از این رو در محیط‌های تست، غیرفعال‌سازی موقت این افزونه‌ها برای اطمینان از عملکرد پروتکل ضروری است.
  • وحید نصیری در ۱۴۰۵/۰۷/۱۵ ۱۰:۳۳
    این مطلب تکمیلی بر پیاده‌سازی دستی مکانیزم اثبات کلید در لایه کلاینت با WebCrypto API، اعتبارسنجی رمزنگاری ECDSA در NET.، حل تفاوت فرمت‌های امضا و تفاوت‌های بنیادین میان Fingerprinting و Device Key تمرکز دارد.

    پیاده‌سازی دستی اثبات تصاحب کلید (PoP): تعامل WebCrypto در فرانت‌اند با رمزنگاری ECDSA در #C

    ۱. تمایز بنیادین: Fingerprint / DeviceId در برابر Device Key
    در اعتبارسنجی کلاینت‌های وب، تفکیک سه مفهوم زیر اهمیت دارد:
    • شناسه دستگاه (DeviceId): یک توکن یا رشته متنی حامل است. در صورتی که مهاجم آن را به همراه کوکی سرقت کند، می‌تواند همان مقدار را بازپخش (Replay) نماید.
    • اثر انگشت مرورگر (Browser Fingerprinting): متکی بر ویژگی‌های محیطی (رزولوشن، فونت‌ها، کارت گرافیک و...) است. این روش مبتنی بر حدس و شباهت ظاهری است، دقت قطعی ندارد و با تغییر تنظیمات یا به‌روزرسانی سیستم مخدوش می‌شود.
    • کلید دستگاه (Device Key): متکی بر رمزنگاری نامتقارن است. کلاینت دارا بودن کلید خصوصی را بدون ارسال آن و صرفاً با امضای یک چالش به اثبات می‌رساند. سرور به جای اعتماد به یک مقدار ارسالی، عملیات ریاضیاتی صحت امضا را ارزیابی می‌کند.

    ۲. تولید کلید غیرقابل استخراج در مرورگر با WebCrypto API
    در سناریوهایی که خارج از پروتکل‌های مرورگرمحور (مانند DBSC خودکار) قصد داریم درخواست‌های API را با کلید دستگاه امضا کنیم، می‌توان از استاندارد بومی window.crypto.subtle استفاده کرد:
    // تولید جفت‌کلید ECDSA روی منحنی استاندارد P-256
    const keyPair = await window.crypto.subtle.generateKey(
        {
            name: "ECDSA",
            namedCurve: "P-256"
        },
        false, // extractable: کلید خصوصی قابل اکسپورت نیست
        ["sign", "verify"]
    );
    • مفهوم extractable = false: مرورگر دسترسی توابعی مانند exportKey را به کلید خصوصی مسدود می‌کند؛ در نتیجه کدهای جاوااسکریپت عادی نمی‌توانند بایت‌های خام کلید را استخراج کنند.
    • محدودیت امنیتی در برابر XSS: تنظیم extractable: false مانع از سرقت خود کلید می‌شود، اما اگر برنامه‌ دچار آسیب‌پذیری Cross-Site Scripting (XSS) باشد، کد مخرب تزریق‌شده همچنان می‌تواند با فراخوانی متد crypto.subtle.sign() در همان کانتکست مرورگر، پیام‌های دلخواه خود را توسط کلید قربانی امضا کند. بنابراین پیاده‌سازی لایه‌های دفاع در عمق مانند CSP و بهداشت کد کماکان الزامی است.

    ۳. اعتبارسنجی امضای ECDSA در دات‌نت و چالش دوگانگی فرمت امضا
    هنگام انتقال امضای کلاینت وب به سمت سرور ASP.NET Core، یک چالش فنی رایج رخ می‌دهد: ناهمخوانی فرمت بایت‌های امضا.

    امضاهای بیضوی (ECDSA) معمولاً به دو شکل بازنمایی می‌شوند:
    • فرمت خام (IEEE P1363): یک اتصال ساده از دو پارامتر r و s به‌صورت فیلدهای ثابت (r || s). این فرمت پیش‌فرض خروجی متد crypto.subtle.sign در مرورگرها است (برای منحنی P-256 طول آن دقیقا ۶۴ بایت است).
    • فرمت ASN.1/DER (RFC 3279): ساختار ساخت‌یافته حاوی متادیتا که اغلب در پلتفرم‌های رمزنگاری کلاسیک و گواهی‌های X.509 استفاده می‌شود.
    کلاس System.Security.Cryptography.ECDsa در دات‌نت از طریق enum با عنوان DSASignatureFormat قابلیت اعتبارسنجی هر دو ساختار را فراهم می‌سازد:
    using System.Security.Cryptography;
    using System.Text;
    
    public class SignatureVerificationService
    {
        public bool VerifyClientSignature(string base64PublicKey, string rawMessage, string base64Signature)
        {
            byte[] publicKeyBytes = Convert.FromBase64String(base64PublicKey);
            byte[] dataBytes = Encoding.UTF8.GetBytes(rawMessage);
            byte[] signatureBytes = Convert.FromBase64String(base64Signature);
    
            using var ecdsa = ECDsa.Create();
            
            // ایمپورت کلید عمومی ارسالی از کلاینت (فرمت استاندارد SubjectPublicKeyInfo / SPKI)
            ecdsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _);
    
            // تلاش برای اعتبارسنجی با پشتیبانی از هر دو فرمت IEEE P1363 و ASN.1 DER
            return TryVerify(ecdsa, dataBytes, signatureBytes, DSASignatureFormat.IeeeP1363FixedFieldConcatenation) ||
                   TryVerify(ecdsa, dataBytes, signatureBytes, DSASignatureFormat.Rfc3279DerSequence);
        }
    
        private static bool TryVerify(ECDsa ecdsa, byte[] data, byte[] signature, DSASignatureFormat format)
        {
            try
            {
                return ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256, format);
            }
            catch (CryptographicException)
            {
                return false;
            }
        }
    }

    ۴. اجزای لازم برای ارتقای Demo به یک سیستم مقاوم در برابر Replay
    امضای یک متن ثابت (مانند "Hello") تنها صحت عملکرد الگوریتم را نشان می‌دهد اما در محیط عملیاتی فاقد امنیت است، زیرا همان امضای ثابت قابل شنود و ارسال مکرر (Replay Attack) خواهد بود. برای تبدیل این مکانیزم به یک پروتکل کامل امضای درخواست (HTTP Request Signing):
    • مکانیزم Nonce یک‌بارمصرف: سرور در هر درخواست یا نشست، یک مقدار تصادفی موقت (Nonce) صادر می‌کند و بلافاصله پس از بررسی، آن را باطل می‌سازد.
    • برچسب زمانی (Timestamp): بازه زمانی مشخصی (مثلاً حداکثر ۳۰ ثانیه اختلاف ساعت) برای پذیرش امضا تعیین می‌شود.
    • پوشش کامل عناصر درخواست (Canonical Request): رشته‌ای که برای تولید امضا به crypto.subtle.sign داده می‌شود، باید ترکیبی هش‌شده از مشخصات کلیدی درخواست باشد: DataToSign = HttpMethod + Path + Nonce + Timestamp + SHA256(RequestBody)
    • این رویکرد تضمین می‌کند که هدرها، متد، مسیر و بدنه ارسالی در حین انتقال قابل دستکاری (Tampering) توسط واسطه‌ها نخواهند بود.