HasUser из 13 endpoint-файлов сведён в AuthHelpers.HasUser; 15 приватных предикатов заменены extension-методами с удалением дублирующих приватных методов: IsCommunicationFailure, IsTransportFailure, IsPrivateEndpoint, IsConfigured, IsTrue, IsExpired, IsFailedLogin/IsSuccessfulLogin, HasChanges, HasBudget, HasAnyTerm, IsCurrencyLetter, IsEmojiCodePoint, ContainsFooterHint, IsTypeLabel.
56 lines
2.2 KiB
C#
56 lines
2.2 KiB
C#
using System.Net;
|
|
using System.Net.Sockets;
|
|
|
|
namespace Deal.Infrastructure.Integrations;
|
|
|
|
/// <summary>
|
|
/// Расширения <see cref="Uri"/> для SSRF-гейта интеграций (проверка приватности адреса).
|
|
/// </summary>
|
|
internal static class UriExtensions
|
|
{
|
|
/// <summary>
|
|
/// Проверяет, указывает ли URL на приватный/loopback/link-local адрес (SSRF-гейт).
|
|
/// Распознаются IP-литералы (IPv4/IPv6) и имя localhost; DNS-имена считаются публичными
|
|
/// (полный egress-контроль с резолвом выполняется на сетевом периметре).
|
|
/// </summary>
|
|
/// <param name="uri">Абсолютный http(s)-адрес.</param>
|
|
/// <returns>True — адрес приватный/локальный (HTTP к нему запрещён).</returns>
|
|
public static bool IsPrivateEndpoint(this Uri uri)
|
|
{
|
|
string host = uri.Host;
|
|
if (string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (!IPAddress.TryParse(host, out IPAddress? address))
|
|
{
|
|
return false; // DNS-имя — резолв вне этого слоя
|
|
}
|
|
|
|
if (address.IsIPv4MappedToIPv6)
|
|
{
|
|
address = address.MapToIPv4();
|
|
}
|
|
|
|
if (IPAddress.IsLoopback(address))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (address.AddressFamily == AddressFamily.InterNetwork)
|
|
{
|
|
byte[] bytes = address.GetAddressBytes();
|
|
return bytes[0] == 10
|
|
|| (bytes[0] == 172 && bytes[1] is >= 16 and <= 31)
|
|
|| (bytes[0] == 192 && bytes[1] == 168)
|
|
|| bytes[0] == 169 && bytes[1] == 254 // link-local (включая 169.254.169.254 metadata)
|
|
|| bytes[0] == 0;
|
|
}
|
|
|
|
// IPv6: уникальные локальные (fc00::/7) и link-local (fe80::/10).
|
|
byte[] v6 = address.GetAddressBytes();
|
|
return (v6[0] & 0xFE) == 0xFC || (v6[0] == 0xFE && (v6[1] & 0xC0) == 0x80);
|
|
}
|
|
}
|