XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEToolBox

【WPF】XFE工具箱

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFEToolBox

新增 ForwardedClientIpResolver 及相关测试

新增 ForwardedClientIpResolver 工具类,支持从受信任代理请求头解析真实客户端 IP,并规范化处理。Program.cs 中获取客户端 IP 的逻辑改为调用该工具类,提升安全性。补充了多种场景下的测试用例,并完善了 using 指令。

26de6c1
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

3 个文件 +87 -3
Added XFEToolBox.Server.Core/Utilities/ForwardedClientIpResolver.cs +48 -0
@@ -0,0 +1,48 @@
1 using System.Net;
2
3 namespace XFEToolBox.Server.Core.Utilities;
4
5 /// <summary>
6 /// Resolves the client address forwarded by a trusted reverse proxy.
7 /// </summary>
8 public static class ForwardedClientIpResolver
9 {
10 /// <summary>
11 /// Resolves an EdgeOne client address, falling back to the transport peer address.
12 /// The origin must only accept traffic from the trusted proxy before forwarded
13 /// headers can be treated as authoritative.
14 /// </summary>
15 public static string Resolve(
16 string connectionIp,
17 string? edgeOneConnectingIp,
18 string? forwardedFor)
19 {
20 if (TryNormalize(edgeOneConnectingIp, out var clientIp))
21 return clientIp;
22
23 if (!string.IsNullOrWhiteSpace(forwardedFor))
24 {
25 var addresses = forwardedFor.Split(
26 ',',
27 StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
28 if (addresses.Length > 0 && TryNormalize(addresses[^1], out clientIp))
29 return clientIp;
30 }
31
32 return TryNormalize(connectionIp, out clientIp) ? clientIp : connectionIp;
33 }
34
35 private static bool TryNormalize(string? value, out string address)
36 {
37 if (IPAddress.TryParse(value?.Trim(), out var parsed))
38 {
39 address = parsed.IsIPv4MappedToIPv6
40 ? parsed.MapToIPv4().ToString()
41 : parsed.ToString();
42 return true;
43 }
44
45 address = string.Empty;
46 return false;
47 }
48 }
Modified XFEToolBox.Server.Test/Program.cs +33 -1
@@ -27,7 +27,8 @@ var tests = new (string Name, Action Run)[]
27 27 ("私密群号具备足够长度并限制账户枚举", ChatGroupNumbersAreStrongAndRateLimited),
28 28 ("聊天附件分块传输校验完整性和访问权限", ChatAttachmentTransferIsAuthorizedAndVerified),
29 29 ("实时票据绑定用途且只能消费一次", ChatRealtimeTicketIsAudienceBoundAndSingleUse),
30 ("实时票据限制每用户和全局待使用数量", ChatRealtimeTicketLimitsAreEnforced)
30 ("实时票据限制每用户和全局待使用数量", ChatRealtimeTicketLimitsAreEnforced),
31 ("EdgeOne 回源请求可解析真实客户端 IP", ForwardedClientIpIsResolved)
31 32 };
32 33
33 34 var failed = 0;
@@ -450,6 +451,37 @@ static void ChatRealtimeTicketLimitsAreEnforced()
450 451 Assert(store.OutstandingTicketCount == 3, "消费票据后没有释放全局配额。");
451 452 }
452 453
454 static void ForwardedClientIpIsResolved()
455 {
456 Assert(
457 ForwardedClientIpResolver.Resolve(
458 "10.0.0.8",
459 "203.0.113.21",
460 "198.51.100.7, 203.0.113.20") == "203.0.113.21",
461 "没有优先使用 EdgeOne 提供的 EO-Connecting-IP。");
462
463 Assert(
464 ForwardedClientIpResolver.Resolve(
465 "10.0.0.8",
466 null,
467 "198.51.100.7, 203.0.113.20") == "203.0.113.20",
468 "没有使用 X-Forwarded-For 中 EdgeOne 追加的最右侧地址。");
469
470 Assert(
471 ForwardedClientIpResolver.Resolve(
472 "::ffff:192.0.2.10",
473 "not-an-ip",
474 "also-invalid") == "192.0.2.10",
475 "非法转发请求头没有回退到连接 IP。");
476
477 Assert(
478 ForwardedClientIpResolver.Resolve(
479 "192.0.2.10",
480 null,
481 "198.51.100.7, not-an-ip") == "192.0.2.10",
482 "X-Forwarded-For 最右侧值无效时不应信任左侧可伪造地址。");
483 }
484
453 485 static void WithChatRepository(Action<ChatRepository, string> test)
454 486 {
455 487 var root = Path.Combine(Path.GetTempPath(), "XFEToolBox.Server.Chat.Test", Guid.NewGuid().ToString("N"));
Modified XFEToolBox.Server/Program.cs +6 -2
@@ -3,6 +3,7 @@ using XFEToolBox.Core.Models.Users;
3 3 using XFEToolBox.Server.Core.Chat;
4 4 using XFEToolBox.Server.Core.Options;
5 5 using XFEToolBox.Server.Core.Services;
6 using XFEToolBox.Server.Core.Utilities;
6 7 using XFEToolBox.Server.Profiles;
7 8 using XFEToolBox.Server.Profiles.Data;
8 9 using XFEToolBox.Server.Realtime;
@@ -125,8 +126,11 @@ var server = XFEServerBuilder.CreateBuilder()
125 126 options.AcceptGet = true;
126 127 options.AcceptPost = true;
127 128 options.AcceptNonStandardJson = true;
128 // 只有在可信反向代理层完成源地址校验时才应采用 X-Forwarded-For。
129 options.GetIPFunction = static args => args.ClientIP;
129 // 源站必须只允许可信 EdgeOne 节点回源,防止客户端绕过代理伪造请求头。
130 options.GetIPFunction = static args => ForwardedClientIpResolver.Resolve(
131 args.ClientIP,
132 args.RequestHeaders["EO-Connecting-IP"],
133 args.RequestHeaders["X-Forwarded-For"]);
130 134 options.BindIP(ServerProfile.HttpAddress);
131 135 options.MainEntryPoint = "api";
132 136 options.ServerCoreName = "XFEToolBoxServer";