以下是完整的C代碼實現方案,結合IIS動態配置與全自動跳轉功能:
一、核心功能實現代碼
using Microsoft.Web.Administration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
public class IPFilterAttribute : ActionFilterAttribute
{
private static List<string> _allowedIPs = new List<string>
{
"192.168.1.1",
"10.0.0.0-10.0.0.255"
};
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string clientIP = GetClientIP();
if (!IsAllowedIP(clientIP))
{
filterContext.Result = new RedirectResult("https://external-block-page.com");
LogBlockedIP(clientIP);
}
}
private string GetClientIP()
{
string ip = HttpContext.Current.Request.Headers["X-Forwarded-For"]?.Split(',')[0];
return string.IsNullOrEmpty(ip) ? HttpContext.Current.Request.UserHostAddress : ip;
}
private bool IsAllowedIP(string ip)
{
return _allowedIPs.Contains(ip) || IPRangeContains(ip, _allowedIPs);
}
private bool IPRangeContains(string ip, List<string> ipRanges)
{
foreach (var range in ipRanges.Where(r => r.Contains('-')))
{
var start = IPAddress.Parse(range.Split('-')[0]);
var end = IPAddress.Parse(range.Split('-')[1]);
var client = IPAddress.Parse(ip);
if (client.AddressFamily == AddressFamily.InterNetworkV6)
throw new NotSupportedException("IPv6 range check not implemented");
var bytes = client.GetAddressBytes();
var startBytes = start.GetAddressBytes();
var endBytes = end.GetAddressBytes();
if (bytes.CompareTo(startBytes) >= 0 && bytes.CompareTo(endBytes) <= 0)
return true;
}
return false;
}
private void LogBlockedIP(string ip)
{
File.AppendAllText("blocked_ips.log", $"{DateTime.Now}: Blocked IP - {ip}\n");
}
public static void SyncIISWhitelist()
{
try
{
using (ServerManager serverManager = new ServerManager())
{
var siteName = "OA_SITE";
var site = serverManager.Sites.FirstOrDefault(s => s.Name == siteName);
if (site == null) throw new Exception($"網站 '{siteName}' 未找到");
var config = serverManager.GetApplicationHostConfiguration();
var ipSecuritySection = config.GetSection("system.webServer/security/ipSecurity", siteName);
if (ipSecuritySection == null)
{
ipSecuritySection = config.CreateSection("system.webServer/security/ipSecurity", siteName);
ipSecuritySection["allowUnlisted"] = false;
}
var ipCollection = ipSecuritySection.GetCollection();
ipCollection.Clear();
foreach (var ip in _allowedIPs)
{
var addElement = ipCollection.CreateElement("add");
addElement["ipAddress"] = ip;
addElement["action"] = "Allow";
addElement["allowed"] = true;
ipCollection.Add(addElement);
}
serverManager.CommitChanges();
}
}
catch (Exception ex)
{
File.AppendAllText("sync_error.log", $"{DateTime.Now}: {ex.Message}\n");
}
}
}
二、關鍵配置說明
IIS配置文件修改
在 web.config 中添加以下配置,啟用IP安全規則:
<system.webServer>
<security>
<ipSecurity allowUnlisted="false" />
</security>
</system.webServer>
引用說明:此配置確保未在白名單中的IP自動被拒絕。
管理員權限要求
程序需以管理員身份運行,否則無法修改IIS配置。
在Windows中可通過:
右鍵exe -> 屬性 -> 兼容性 -> 以管理員身份運行此程序
IP范圍支持
代碼支持IP段格式(如 10.0.0.0-10.0.0.255 ),通過 IPRangeContains 方法實現范圍檢查。
三、部署與調用步驟
初始化白名單同步
在網站啟動時調用 SyncIISWhitelist() 方法,確保IIS規則與代碼一致:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
SyncIISWhitelist();
}
應用層攔截配置
在 Global.asax 中注冊全局過濾器:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new IPFilterAttribute());
}
}
日志與監控
封禁日志記錄到 blocked_ips.log ,便于審計。
建議集成ELK或Prometheus監控日志文件,異常IP封禁時觸發告警。
四、功能驗證
白名單生效驗證
通過IIS管理器檢查網站配置,確認 ipSecurity 節點已正確添加白名單IP。
使用非白名單IP訪問網站,應自動跳轉至外部頁面。
IP范圍測試
添加IP段 192.168.2.0-192.168.2.100 到白名單,驗證該網段內所有IP均可訪問。
五、安全增強建議
雙重驗證機制
在跳轉前增加驗證碼驗證,防止自動化工具繞過IP限制。
動態白名單更新
通過管理后臺提供界面,支持手動添加/刪除白名單IP。
定期從AD域控同步內部員工IP。
備份與回滾
定期備份IIS配置文件( %windir%\System32\inetsrv\config\applicationHost.config )。
實現配置變更回滾功能,防止誤操作導致服務中斷。
通過以上方案,可實現全自動化的IP白名單控制與訪問跳轉,有效抵御外部攻擊。建議配合WAF(Web應用防火墻)和DDoS防護服務,構建縱深防御體系。
該文章在 2025/3/15 17:40:26 編輯過