عنوان:

‫روش‌هایی برای جلوگیری از حملات SSRF در دات‌نت


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۲/۲۸ ۰۹:۲۸
آدرس: www.dntips.ir
در دنیای توسعه نرم‌افزار، امنیت یکی از مهم‌ترین جنبه‌هایی است که برنامه‌نویسان باید به آن توجه کنند. یکی از تهدیدات امنیتی رایج در برنامه‌های تحت وب، حملات جعل درخواست سمت سرور (Server-Side Request Forgery یا SSRF) است. این نوع حمله به مهاجمان اجازه می‌دهد تا از طریق سرور برنامه، درخواست‌هایی به مقاصد غیرمجاز مانند آدرس‌های داخلی شبکه یا حتی خود سرور محلی (localhost) ارسال کنند. در چارچوب دات‌نت، پیاده‌سازی اقدامات پیشگیرانه برای مقابله با این حملات از اهمیت ویژه‌ای برخوردار است. این مقاله به بررسی روش‌های جلوگیری از حملات SSRF در دات‌نت می‌پردازد و با ارائه راهکارهای عملی و کدهای نمونه، توسعه‌دهندگان را در ایجاد برنامه‌های امن‌تر یاری می‌کند.

حملات SSRF چیست و چرا اهمیت دارد؟
حمله جعل درخواست سمت سرور زمانی رخ می‌دهد که یک مهاجم بتواند سرور را وادار کند تا درخواست‌های HTTP را به آدرس‌هایی ارسال کند که نباید به آن‌ها دسترسی داشته باشد. به عنوان مثال، مهاجم ممکن است سرور را فریب دهد تا به آدرس‌های داخلی شبکه، مانند سرورهای پایگاه داده یا سرویس‌های محلی، درخواست ارسال کند. این مشکل به‌ویژه در برنامه‌هایی که از ورودی‌های کاربر برای ارسال درخواست‌ها استفاده می‌کنند، مانند وب‌هوک‌ها (webhooks)، شایع است. وب‌هوک‌ها آدرس‌های URL ارائه‌شده توسط کاربر هستند که سرور از آن‌ها برای ارسال اعلان‌ها استفاده می‌کند. اگر این آدرس‌ها به‌درستی اعتبارسنجی (validation) نشوند، مهاجم می‌تواند از آن‌ها برای دسترسی به منابع حساس سوءاستفاده کند.
یکی از نمونه‌های واقعی این تهدید، آسیب‌پذیری مربوط به آدرس IP "0.0.0.0" بود که اخیراً در مرورگرها کشف شد. این آدرس IP، اگرچه به‌ظاهر بی‌ضرر به نظر می‌رسد، در سیستم‌های لینوکس به ماشین محلی اشاره می‌کند و می‌تواند برای دور زدن محدودیت‌های امنیتی مورد استفاده قرار گیرد. این موضوع نشان‌دهنده اهمیت اعتبارسنجی دقیق آدرس‌های IP و نام‌های میزبان (hostname) در برنامه‌های دات‌نت است.

اصول پیشگیری از حملات SSRF
برای جلوگیری از حملات SSRF، توسعه‌دهندگان باید ورودی‌های کاربر را به‌دقت بررسی کنند و اطمینان حاصل کنند که درخواست‌ها به مقاصد غیرمجاز ارسال نمی‌شوند. اصول کلیدی پیشگیری شامل موارد زیر است:
  • اعتبارسنجی طرح (scheme): اطمینان از اینکه URL فقط از پروتکل‌های مجاز مانند HTTP یا HTTPS استفاده می‌کند.
  • بررسی آدرس‌های IP: شناسایی و مسدود کردن آدرس‌های IP داخلی، از جمله آدرس‌های لوکال‌هاست (localhost)، آدرس‌های خصوصی (private IPs) و آدرس خاص "0.0.0.0".
  • اعتبارسنجی نام میزبان: بررسی اینکه نام میزبان به آدرس‌های IP داخلی یا غیرمجاز اشاره نکند.
  • اعتبارسنجی مکرر: انجام بررسی‌های امنیتی قبل از هر درخواست، زیرا مهاجمان ممکن است ابتدا یک آدرس معتبر ارائه دهند و سپس آن را به آدرس داخلی تغییر دهند.
این اصول باید به‌صورت یکپارچه در کد برنامه پیاده‌سازی شوند تا از امنیت آن اطمینان حاصل شود.

پیاده‌سازی اعتبارسنجی در دات‌نت
برای پیاده‌سازی اعتبارسنجی امن در دات‌نت، می‌توان از کلاس‌ها و متدهایی استفاده کرد که آدرس‌های URL را بررسی کرده و از ارسال درخواست به مقاصد غیرمجاز جلوگیری می‌کنند. در ادامه، یک نمونه کد ارائه شده است که این اعتبارسنجی را انجام می‌دهد. این کد شامل کلاسی به نام SsrfValidator است که وظیفه بررسی ایمنی URL‌های ارائه‌شده توسط کاربر را بر عهده دارد.

کد اعتبارسنجی URL
کلاس SsrfValidator شامل متدهایی است که بررسی‌های مختلفی را روی URL انجام می‌دهند. متد اصلی، IsSafeUri، بررسی می‌کند که آیا URL مطلق (absolute) است، از پروتکل‌های مجاز استفاده می‌کند و به آدرس‌های داخلی اشاره نمی‌کند. ساختار این کد به شرح زیر است:
public static class SsrfValidator
{
    private static readonly string[] AllowedSchemes = new[] { "http", "https" };

    public static bool IsSafeUri(Uri uri)
    {
        if (uri == null)
            throw new ArgumentNullException(nameof(uri));
        return uri.IsAbsoluteUri &&
               HasValidScheme(uri) &&
               IsValidHostname(uri);
    }

    private static bool HasValidScheme(Uri uri)
    {
        return AllowedSchemes.Any(x => x.Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase));
    }

    private static bool IsValidHostname(Uri uri)
    {
        var hostName = uri.DnsSafeHost;
        if (IPAddress.TryParse(hostName, out IPAddress ipAddress))
        {
            return IsValidIpAddress(ipAddress);
        }

        var resolvedIpAddresses = GetIpAddressFromHostName(hostName);
        return HasValidIpAddress(resolvedIpAddresses);
    }

    private static bool HasValidIpAddress(params IPAddress[] ipAddresses)
    {
        return ipAddresses.Length != 0 && ipAddresses.All(IsValidIpAddress);
    }

    private static bool IsValidIpAddress(IPAddress ipAddress)
    {
        return !ipAddress.IsPrivate();
    }

    private static IPAddress[] GetIpAddressFromHostName(string hostName)
    {
        try
        {
            return Dns.GetHostEntry(hostName).AddressList;
        }
        catch (SocketException)
        {
            return Array.Empty<IPAddress>();
        }
    }
}
این کد ابتدا بررسی می‌کند که URL مطلق باشد و از پروتکل‌های HTTP یا HTTPS استفاده کند. سپس نام میزبان را استخراج کرده و بررسی می‌کند که آیا به یک آدرس IP داخلی اشاره می‌کند یا خیر. برای این منظور، از متد IsPrivate در افزونه IPAddressExtensions استفاده می‌شود.

بررسی آدرس‌های IP خصوصی
کلاس IPAddressExtensions شامل متدی به نام IsPrivate است که بررسی می‌کند آیا یک آدرس IP در محدوده‌های خصوصی قرار دارد یا خیر. این محدوده‌ها شامل آدرس‌های لوکال‌هاست، آدرس‌های لینک محلی (link-local)، و کلاس‌های A، B و C در IPv4، و همچنین آدرس‌های محلی در IPv6 هستند. کد این افزونه به شرح زیر است:
public static class IPAddressExtensions
{
    public static bool IsPrivate(this IPAddress ip)
    {
        if (ip.IsIPv4MappedToIPv6)
            ip = ip.MapToIPv4();

        if (IPAddress.IsLoopback(ip))
            return true;

        if (ip.Equals(IPAddress.Any))
            return true;

        if (ip.AddressFamily == AddressFamily.InterNetwork)
            return IsPrivateIPv4(ip.GetAddressBytes());

        if (ip.AddressFamily == AddressFamily.InterNetworkV6)
            return ip.IsIPv6LinkLocal ||
                   ip.IsIPv6UniqueLocal ||
                   ip.IsIPv6SiteLocal;

        throw new NotSupportedException(
                $"IP address family {ip.AddressFamily} is not supported, expected only IPv4 (InterNetwork) or IPv6 (InterNetworkV6).");
    }

    private static bool IsPrivateIPv4(byte[] ipv4Bytes)
    {
        bool IsLinkLocal() => ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254;
        bool IsClassA() => ipv4Bytes[0] == 10;
        bool IsClassB() => ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31;
        bool IsClassC() => ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168;

        return IsLinkLocal() || IsClassA() || IsClassC() || IsClassB();
    }
}
این کد آدرس‌های IP را با دقت بررسی می‌کند و از دسترسی به آدرس‌های غیرمجاز جلوگیری می‌کند. به عنوان مثال، آدرس "0.0.0.0" به‌طور خاص بررسی می‌شود، زیرا می‌تواند به ماشین محلی اشاره کند.

نکات پیشرفته برای افزایش امنیت
علاوه بر اعتبارسنجی اولیه، توسعه‌دهندگان می‌توانند از روش‌های پیشرفته‌تری برای افزایش امنیت استفاده کنند. به عنوان مثال، استفاده از لیست‌های سفید (whitelists) برای محدود کردن دامنه‌های مجاز، می‌تواند خطر حملات را کاهش دهد. همچنین، پیاده‌سازی محدودیت‌های نرخ (rate limiting) برای درخواست‌های ارسالی به URL‌های خارجی می‌تواند از سوءاستفاده‌های احتمالی جلوگیری کند.
یکی دیگر از روش‌های پیشنهادی، استفاده از سرویس‌های DNS امن است که از تغییر ناگهانی آدرس‌های IP توسط مهاجمان جلوگیری می‌کند. برای مثال، مهاجمان ممکن است ابتدا یک نام میزبان را به یک آدرس IP معتبر نگاشت کنند و سپس آن را به یک آدرس داخلی تغییر دهند. انجام بررسی‌های DNS در زمان واقعی (real-time) می‌تواند این مشکل را برطرف کند.

نتیجه‌گیری
حملات جعل درخواست سمت سرور تهدیدی جدی برای برنامه‌های تحت وب هستند، به‌ویژه در برنامه‌هایی که از ورودی‌های کاربر برای ارسال درخواست‌ها استفاده می‌کنند. در چارچوب دات‌نت، توسعه‌دهندگان می‌توانند با پیاده‌سازی اعتبارسنجی دقیق URL‌ها و آدرس‌های IP، از این حملات جلوگیری کنند. استفاده از کدهای نمونه ارائه‌شده، مانند کلاس‌های SsrfValidator و IPAddressExtensions، به برنامه‌نویسان کمک می‌کند تا درخواست‌های غیرمجاز را شناسایی و مسدود کنند. با رعایت اصول امنیتی و به‌کارگیری روش‌های پیشرفته مانند لیست‌های سفید و بررسی‌های DNS، می‌توان امنیت برنامه‌ها را به‌طور قابل‌توجهی ارتقا داد. توسعه‌دهندگان باید همیشه به‌روزرسانی‌های امنیتی را دنبال کنند و از منابع معتبر برای بهبود دانش خود استفاده کنند.


مشاهده مطلب اصلی