Application Security

محکم‌سازی APIهای شما: راهنمای عملی برای پیاده‌سازی OAuth2 Token Binding و DPoP

OAuth 2.0 نحوه احراز دسترسی به منابع وب را متحول کرد، اما آسیب‌پذیری امنیتی قابل توجهی به نام سرقت توکن ایجاد کرد. اگر مهاجمی به یک توکن دسترسی معتبر دست یابد، می‌تواند تا زمان انقضای توکن، هویت کاربر را جعل کند، صرف‌نظر از اینکه آیا توکن از طریق کانال امن اولیه ارسال شده است یا خیر. اگرچه امنیت لایه انتقال (TLS) از توکن‌ها در حین انتقال محافظت می‌کند، اما آن‌ها را به مشتری خاصی که به آن‌ها صادر شده است، پیوند نمی‌دهد. اینجاست که تفویض اثبات مالکیت (DPoP) وارد عمل می‌شود.

DPoP که در RFC 9449 استاندارد شده است، OAuth 2.0 را با اطمینان از پیوند رمزنگاری‌شده توکن‌های دسترسی به کلید عمومی مشتری که آن‌ها را در اختیار دارد، گسترش می‌دهد. این پست وبلاگ مکانیک‌های DPoP را بررسی کرده و یک راهنمای پیاده‌سازی عملی برای توسعه‌دهندگان متوسط تا پیشرفته‌ای که به دنبال ارتقای وضعیت امنیتی APIهای خود هستند، ارائه می‌دهد.

درک مکانیزم DPoP

مفهوم اصلی DPoP ساده اما قدرتمند است: مشتری باید ثابت کند که کلید خصوصی متناظر با کلید عمومی ارسال‌شده در درخواست را در اختیار دارد. به جای ارسال ساده توکن، مشتری یک نشانه وب JSON (JWT) ایجاد می‌کند که با کلید خصوصی امضا شده است. این JWT که به عنوان اثبات DPoP شناخته می‌شود، در یک هدر ویژه همراه با توکن دسترسی ارسال می‌شود.

اجزای کلیدی جریان DPoP شامل موارد زیر است:

  • تولید کلید DPoP: مشتری یک جفت کلید (معمولاً Ed25519 یا RSA) تولید می‌کند.
  • ایجاد اثبات DPoP: یک JWT حاوی کلید عمومی، زمان فعلی و URL توکن امضا می‌شود.
  • تزریق هدر: اثبات در هدر DPoP ارسال می‌شود و اثر انگشت کلید عمومی در هدر DPOP ارسال می‌گردد.
  • اعتبارسنجی سرور: سرور منبع امضا را اعتبارسنجی می‌کند، زمان‌ها را بررسی می‌کند و اطمینان حاصل می‌کند که توکن در همان نقطه پایانی استفاده شده است.

پیاده‌سازی سمت مشتری در Node.js

بیایید یک مثال عملی را با استفاده از Node.js و کتابخانه محبوب axios بررسی کنیم. ابتدا، به کتابخانه‌ای نیاز دارید که ایجاد JWT و امضای رمزنگاری را مدیریت کند. برای این مثال، فرض می‌کنیم که از jose برای عملیات JWT استفاده می‌شود.

این نحوه ایجاد اثبات DPoP و پیوست آن به یک درخواست API است:

const jose = require('jose');
const axios = require('axios');

async function makeDPOPRequest() {
  // 1. تولید یک جفت کلید (Ed25519 توصیه می‌شود)
  const { publicKey, privateKey } = await jose.generateKeyPair('EdDSA');
  
  // 2. ایجاد هدر JWT DPoP
  const publicKeyJWK = await jose.exportJWK(publicKey);
  const dpopHeader = {
    typ: 'dpop+jwt',
    alg: 'EdDSA',
    jwk: publicKeyJWK
  };

  // 3. ایجاد پیکربندی JWT DPoP
  const url = 'https://api.example.com/resource';
  const now = Math.floor(Date.now() / 1000);
  const payload = {
    iss: 'client_id',
    jti: crypto.randomUUID(),
    aud: url, // نقطه پایانی API
    iat: now,
    exp: now + 60 // عمر کوتاه، معمولاً 60 ثانیه
  };

  // 4. امضای JWT با کلید خصوصی
  const signer = await jose.importJWK(publicKeyJWK, 'EdDSA'); // توجه: jose با کلید خصوصی مشتق شده یا ارسال شده امضا می‌کند
  // وارد کردن صحیح کلید خصوصی برای امضا:
  const dpopSignedJwt = await new jose.SignJWT(payload)
    .setProtectedHeader(dpopHeader)
    .setIssuedAt()
    .setExpirationTime('2m')
    .sign(privateKey);

  // 5. محاسبه اثر انگشت کلید عمومی
  const thumbprint = await jose.calculateJwkThumbprint(publicKeyJWK, 'sha256');

  // 6. انجام درخواست HTTP
  const response = await axios.get(url, {
    headers: {
      'Authorization': 'Bearer ACCESS_TOKEN',
      'DPoP': dpopSignedJwt,
      'DPOP': thumbprint
    }
  });

  return response.data;
}

الزامات اعتبارسنجی سمت سرور

پیاده‌سازی مشتری تنها نبرد نیمه است. سرور منبع نیز باید برای اعتبارسنجی اثبات‌های DPoP پیکربندی شود. اگر سرور درخواستی با هدر DPOP دریافت کند، باید بررسی‌های زیر را انجام دهد:

  1. تأیید امضا: JWT موجود در هدر DPoP را رمزگشایی کنید و امضا را با استفاده از کلید عمومی ارائه‌شده در ادعای jwk بررسی کنید.
  2. اعتبارسنجی زمان: اطمینان حاصل کنید که ادعای iat (صادر شده در) خیلی قدیمی نیست (معمولاً در عرض 60 ثانیه) تا از حملات تکراری جلوگیری شود.
  3. تطبیق URL: بررسی کنید که ادعای aud با URL سرور منبع مطابقت دارد.
  4. پیوند توکن: توکن دسترسی را به کلید عمومی پیوند دهید. اگر توکن دسترسی با پارامتر پیوند صادر شده باشد، سرور باید اطمینان حاصل کند که کلید عمومی در اثبات DPoP با پیوند مورد انتظار مطابقت دارد.

بیشتر ارائه‌دهندگان هویت مدرن مانند Auth0، Keycloak و Microsoft Identity Web اکنون DPoP را به صورت پیش‌فرض پشتیبانی می‌کنند. برای پیاده‌سازی‌های سفارشی، کتابخانه‌هایی مانند passport-oauth2-provider یا Owin.Security.Providers پشتیبانی آزمایشی یا پایدار برای اعتبارسنجی DPoP اضافه کرده‌اند.

نتیجه‌گیری

پیاده‌سازی پیوند توکن OAuth2 از طریق DPoP به طور قابل توجهی نردبان را برای مهاجمان بالا می‌برد. با اطمینان از اینکه توکن‌های دزدیده شده بدون کلید خصوصی متناظر بی‌استفاده هستند، خطر تکرار و سرقت توکن را کاهش می‌دهید. اگرچه پیچیدگی پیاده‌سازی کمی در هر دو سمت مشتری و سرور افزایش می‌یابد، اما منافع امنیتی برای APIهای با ارزش بالا چشمگیر است. با حرکت اکوسیستم وب به سمت معماری‌های بدون اعتماد (zero-trust)، پذیرش DPoP دیگر تنها یک بهترین روش نیست—بلکه به یک ضرورت تبدیل شده است.

Share: