عنوان:

‫مقابله با جعل آدرس IP در برنامه‌های ASP.NET Core پشت سرور پروکسی معکوس Nginx


نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۶/۱۸ ۰۹:۱۵
آدرس: www.dntips.ir
چکیده: در معماری‌های وب متداول، سرویس برنامه‌های کاربردی دات‌نت (ASP.NET Core Kestrel) در لایه داخلی شبکه و پشت یک پروکسی معکوس مانند Nginx قرار می‌گیرد. در چنین چینش‌هایی، تشخیص صحیح آدرس IP واقعی کلاینت (Client IP) برای ثبت وقایع، محدودسازی نرخ تراکنش‌ها (Rate Limiting)، اعتبارسنجی‌های امنیتی و کشف حملات از اهمیت حیاتی برخوردار است. با این حال، استفاده نادرست از هدرهای HTTP نظیر X-Forwarded-For و X-Real-IP بدون اعتبارسنجی مرز اعتماد شبکه، منجر به آسیب‌پذیری جعل هدر (Header Spoofing / IP Spoofing) می‌شود. مهاجمان با ارسال هدرهای ساختگی (مانند آدرس‌های خصوصی نظیر 10.0.0.1) هویت واقعی خود را پنهان کرده و سدهای نظارتی و امنیتی نرم‌افزار را دور می‌زنند. این مقاله با تحلیل ریشه‌ای این آسیب‌پذیری در تعامل Nginx و Kestrel، خطرات پارس دستی هدرها در کدهای #C را بررسی کرده و راهکار استاندارد و دومرحله‌ای اصلاح پیکربندی Nginx و به‌کارگیری میدل‌ویر رسمی ForwardedHeadersMiddleware در دات‌نت را تشریح می‌کند.

۱. مقدمه
هنگامی که یک سرویس مبتنی بر Kestrel پشت پروکسی معکوس Nginx قرار می‌گیرد، ارتباط سوکت TCP میان کلاینت و Kestrel قطع است. سوکت مستقیم Kestrel همواره با آدرس محلی سرور (مانند 127.0.0.1 یا آدرس سوکت یونیکس) برقرار می‌شود. در نتیجه، ویژگی پیش‌فرض HttpContext.Connection.RemoteIpAddress آدرس پروکسی داخلی را نمایش می‌دهد، نه کلاینت بیرونی را.
برای انتقال اطلاعات فرستنده واقعی، از هدرهای استاندارد یا شبه‌استاندارد X-Forwarded-* استفاده می‌شود. چالش اصلی زمانی رخ می‌دهد که توسعه‌دهنده بدون در نظر گرفتن رفتار پیش‌فرض Nginx و سناریوهای مخرب، مقدار این هدرها را به عنوان منبع موثق هویت شبکه در نظر بگیرد. کلاینت مخرب می‌تواند با ارسال بسته‌های HTTP که حاوی مقادیر دلخواه در هدرهای X-Forwarded-For یا X-Real-IP هستند، رفتار برنامه را دست‌کاری کند.

۲. تحلیل ریشه‌ای آسیب‌پذیری
۲.۱. رفتار فریبنده proxy_set_header با متغیر $proxy_add_x_forwarded_for
یکی از خطاهای متداول در مستندات پیکربندی Nginx، توصیه به استفاده از دستور زیر است:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
متغیر $proxy_add_x_forwarded_for مقدار قبلی هدر X-Forwarded-For موجود در درخواست کلاینت را حفظ کرده و IP سوکت ورودی ($remote_addr) را به انتهای آن (با کاما) الحاق می‌کند.

سناریوی حمله:
اگر مهاجم در درخواستی هدر زیر را تزریق کند:
X-Forwarded-For: 10.0.0.1
Nginx مقدار بالا را دریافت کرده و آدرس واقعی مهاجم (مثلاً 203.0.113.50) را به انتهای آن اضافه می‌کند:
X-Forwarded-For: 10.0.0.1, 203.0.113.50

۲.۲. خطای رایج در الگوریتم‌های اعتبارسنجی #C
برخی کدهای اعتبارسنجی در برنامه‌های دات‌نت تلاش می‌کنند لیست آدرس‌ها را از راست به چپ پیمایش کنند تا اولین آدرس عمومی (Public IP) را بیابند. اگر مهاجم هدرهای چندگانه یا آدرس‌های خصوصی تزریق کرده باشد و متد نتواند آدرس عمومی را برگزیند، در حالت Fallback به اولین عنصر لیست (چپ‌ترین ورودی که توسط خود مهاجم تزریق شده) اتکا می‌کند. نتیجه این رخداد، ثبت آدرس جعلی 10.0.0.1 به جای هویت واقعی مهاجم در سیستم لاگ و ابزارهای مانیتورینگ خواهد بود.

۳. راهکار اجرایی و استاندارد
حل پایدار این مسئله نیازمند پاکسازی در مرز ورود به شبکه (Edge) و مصرف امن داده در محیط دات‌نت است.
[ کلاینت / مهاجم ] 
       │
       ▼  (درخواست با هدرهای جعلی X-Forwarded-For: 10.0.0.1)
┌────────────────────────────────────────────────────────┐
│ Nginx (Reverse Proxy)                                  
│  - پاکسازی هدرهای قبلی                                 
│  - ست کردن هدر با IP سوکت فیزیکی ($remote_addr)        
└────────────────────────────────────────────────────────┘
       │
       ▼  (X-Forwarded-For: 203.0.113.50)
┌────────────────────────────────────────────────────────┐
│ ASP.NET Core (Kestrel)                                 
│  - ForwardedHeadersMiddleware                          
│  - اعتماد فقط به پروکسی معتبر (127.0.0.1)             
│  - ثبت دقیق در HttpContext.Connection.RemoteIpAddress   
└────────────────────────────────────────────────────────┘

۳.۱. گام اول: پیکربندی امن Nginx (بازنویسی مرزی)
در حالتی که سرویس پشت شبکه توزیع محتوا (CDN) قرار ندارد و مستقیماً ترافیک کاربران را دریافت می‌کند، Nginx به عنوان اولین نقطه تماس، دارای مطمئن‌ترین داده لایه انتقال یعنی متغیر $remote_addr است. این متغیر از روی سوکت TCP استخراج می‌شود و در لایه پروتکل HTTP قابل جعل نیست.
کانفیگ Nginx باید هدرهای ورودی را کاملاً نادیده گرفته و آن‌ها را با $remote_addr بازنویسی کند:
server {
    listen 80;
    server_name example.com;

    location / {
        # بازنویسی قطعی با IP سوکت واقعی کلاینت (حذف هدرهای جعلی ورودی)
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;

        # حفظ متادیتای مورد نیاز پروتکل و دامنه
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_pass http://127.0.0.1:5000;
    }
}
پس از ویرایش، تنظیمات را با اجرای دستورات زیر بررسی و بارگذاری کنید:
sudo nginx -t && sudo systemctl reload nginx

۳.۲. گام دوم: پیاده‌سازی اصولی در ASP.NET Core
به جای نگارش متدهای توسعه‌یافته (Extension Methods) پیچیده برای تفکیک دستی رشته‌های هدر، رویکرد پیشنهادی مایکروسافت استفاده از میدل‌ویر UseForwardedHeaders است. این میدل‌ویر هدرهای دریافتی از پروکسی‌های معتبر و مشخص را پردازش کرده و مستقیماً مقادیر HttpContext.Connection.RemoteIpAddress و HttpContext.Request.Scheme را به مقادیر واقعی تغییر می‌دهد. در نتیجه، تمام بخش‌های برنامه، لاگرها و کتابخانه‌های ثالث بدون تغییر کد به آدرس صحیح دسترسی خواهند داشت.
تنظیمات در Program.cs:
using System.Net;
using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

// ۱. تنظیم گزینه‌های هدرهای ارسالی
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;

    // پاکسازی شبکه‌ها و پروکسی‌های پیش‌فرض
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();

    // تعیین دقیق مرز اعتماد: تنها Nginx لوکال مجاز به بازنویسی هدرها است
    options.KnownProxies.Add(IPAddress.Loopback);
    options.KnownProxies.Add(IPAddress.IPv6Loopback);
});

var app = builder.Build();

// ۲. ثبت میدل‌ویر در ابتدای پایپ‌لاین قبل از احراز هویت و لاگینگ
app.UseForwardedHeaders();

app.MapGet("/api/client-ip", (HttpContext context) =>
{
    // مقدار RemoteIpAddress به طور خودکار حاوی آدرس واقعی کلاینت خواهد بود
    var clientIp = context.Connection.RemoteIpAddress?.ToString();
    return Results.Ok(new { ClientIP = clientIp });
});

app.Run();
نکته کلیدی در معماری امن: متغیرهای KnownProxies و KnownNetworks دیواره آتشین میدل‌ویر هستند. اگر بسته‌ای مستقیماً (با دور زدن Nginx) به پورت Kestrel ارسال شود، چون فرستنده آن در لیست KnownProxies نیست، ASP.NET Core هدرهای X-Forwarded-* را نادیده گرفته و دست‌کاری رد خواهد شد.

۴. ملاحظات تکمیلی برای افزایش پایداری و امنیت
برای پوشش کامل معماری و پوشش شرایط ویژه سازمانی، نکات زیر باید مد نظر قرار گیرند:

۴.۱. نگاشت IPv6 به IPv4 (IPv4 Mapped to IPv6)
در دات‌نت، اتصالات دوگانه‌ای که از لایه سوکت سیستم‌عامل عبور می‌کنند ممکن است آدرسی شبیه ::ffff:192.0.2.1 بازگردانند. اگر متدها یا لاگرهای شما نیاز به استانداردسازی آدرس دارند، استفاده از متد داخلی دات‌نت توصیه می‌شود:
var rawIp = context.Connection.RemoteIpAddress;
var normalizedIp = rawIp is not null && rawIp.IsIPv4MappedToIPv6 
    ? rawIp.MapToIPv4() 
    : rawIp;

۴.۲. سناریوی حضور در پشت شبکه توزیع محتوا (CDN)
اگر ساختار شبکه در آینده تغییر کرده و سایتی پشت CDN (مانند Cloudflare) قرار گیرد، Nginx دیگر مستقیماً با کلاینت ارتباط نخواهد داشت، بلکه درخواست‌ها از سرورهای میانی CDN عبور می‌کنند. در این سناریو:
  • استفاده از $remote_addr آدرس گره‌های CDN را ثبت می‌کند.
  • راهکار امن، بهره‌گیری از ماژول ngx_http_realip_module در Nginx است:
  • با دستور set_real_ip_from، تنها بازه‌های IP رسمی سرورهای CDN به عنوان مبدا معتبر معرفی می‌شوند.
  • هدر ارائه‌شده توسط CDN (مانند CF-Connecting-IP) به عنوان منبع استخراج آدرس واقعی کلاینت معین می‌گردد.

۴.۳. مسدودسازی مهاجمان در لایه وب‌سرور
پاسخ‌دهی به ترافیک حملات در سطح اپلیکیشن، پردازنده و حافظه سرویس دات‌نت را درگیر می‌کند. با ثبت آدرس واقعی در لاگ Nginx، می‌توان حملات شناسایی‌شده را مستقیماً در لایه Nginx یا به کمک ابزارهایی نظیر Fail2ban و قوانین فایروال (iptables / nftables) در ابتدای پشته شبکه مسدود کرد:
# نمونه مسدودسازی در Nginx
deny 203.0.113.50;

۵. نتیجه‌گیری
اعتماد بدون فیلتر به هدرهای HTTP دریافتی، ریشه اصلی مخاطرات جعل هویت شبکه است. در سامانه‌های مبتنی بر ASP.NET Core که پشت پروکسی Nginx مستقر هستند، دستیابی به آدرس IP دقیق و بدون تحریف مستلزم اتخاذ یک سازوکار هماهنگ است:
  • در سطح وب‌سرور (Nginx): هدرهای غیرمطمئن ارسالی از سوی کلاینت‌ها با مقدار $remote_addr جایگزین شوند تا جعل هدر در لایه لبه متوقف شود.
  • در سطح برنامه دات‌نت: با جایگزینی متدهای دستی با میدل‌ویر استاندارد ForwardedHeadersMiddleware و تعیین صریح KnownProxies، اطلاعات هویتی درخواست به شکل ایمن و شفاف در چرخه حیات HttpContext در دسترس قرار گیرد.

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