如何在ASP.NET中获取用户的客户端IP地址?


387

我们必须Request.UserHostAddress在ASP.NET中获取IP地址,但这通常是用户的ISP的IP地址,而不完全是例如单击链接的用户的计算机IP地址。如何获得真实的IP地址?

例如,在Stack Overflow用户配置文件中,它是:“上次帐户活动:从86.123.127.8开始的4小时前”,但是我的计算机IP地址有些不同。Stack Overflow如何获得该地址?

在某些Web系统中,出于某些目的需要进行IP地址检查。例如,使用某个IP地址,用户是否可以每24小时仅点击5次下载链接?此IP地址应该是唯一的,而不是对于拥有大量客户端或Internet用户的ISP。

我听得懂吗


4
它们通常会做同样的事情,并且对于共享IP地址无法正常工作。在这方面没有什么可以做的。
Mehrdad Afshari

您要在此处解决的问题是什么?为什么您认为需要IP地址?
史蒂夫

3
我有一个检查特定链接点击的应用程序,并且特定用户(按IP)一天不能点击链接超过5次。问题是,如果Request.UserHostAddress适用于ISP或网络或特定用户的?
Mehdi

Answers:


142

正如其他人所说,您不能做您要问的事情。如果您描述您要解决的问题,也许有人可以提供帮助?

例如

  • 您是否试图唯一标识您的用户?
  • 您能否使用Cookie或会话ID代替IP地址?

编辑您在服务器上看到的地址不应是ISP的地址,因为您说的范围很大。宽带上的家庭用户的地址将是其路由器上的地址,因此房屋内的每个设备在外部都将显示为相同,但是路由器使用NAT来确保将流量正确路由到每个设备。对于从办公环境访问的用户,该地址对于所有用户而言可能都是相同的。使用IP地址作为ID的网站冒着将其弄错的风险-您所举的例子很好,但常常失败。例如,我的办公室在英国,分支点(我“似乎”在互联网上)是在其他国家,那里是我们主要IT设施所在的国家,因此从我的办公室,我的IP地址似乎不在英国。因此,我无法仅访问英国的网页内容,例如BBC iPlayer)。在任何给定时间,我公司中都会有数百甚至数千人似乎正在从同一IP地址访问网络。

在编写服务器代码时,您永远无法确定所看到的IP地址是指什么。一些用户喜欢这种方式。有些人故意使用代理或VPN进一步困扰您。

当您说您的计算机地址与StackOverflow上显示的IP地址不同时,如何找到您的计算机地址?如果您只是在本地使用ipconfig或类似的工具,由于上述原因,我希望它会有所不同。如果您想再次检查外界的想法,请访问whatismyipaddress.com/

NAT上的Wikipedia链接将为您提供一些背景知识。


因此我了解在服务器端应用程序中我们无法确定IP地址。这意味着客户端编程是解决方案??您的意思是,例如,使用一些JavaScript代码,我们可以做到这一点??
Mehdi

11
不,这毫无意义,客户端“认为”它拥有的IP地址将位于家庭或办公室内部,而在外部世界中将毫无意义。例如,大多数家用路由器分发的IP地址在192.168.1.xxx范围内,因此成千上万的计算机在其自己的网络上具有相同的地址。
史蒂夫

12
不,它不是唯一的。使用NAT的同一路由器后面的两个用户将具有相同的IP地址。我真的认为您需要对此进行阅读,请参阅我的编辑中的链接。
史蒂夫

1
那么,为什么像AWS,Azure等公司在安全组规则中使用IP地址,而仅允许该IP地址连接到VM?

2
@ user5950947:因为Azure希望您成为一家拥有静态公共IP地址的公司。可以肯定地说,您的公司只会从其公共IP地址进行访问,因此这是一个不错的安全功能。但是IP地址可能是伪造的,也可能是您的网络被黑客攻击的,因此它永远不是唯一的安全措施。
Deantwo

449

通常,您会想知道访问您网站的某人的IP地址。尽管ASP.NET有几种方法可以做到这一点,但我们已经看到的最好的方法之一是使用ServerVariables集合的“ HTTP_X_FORWARDED_FOR”。

这就是为什么

有时,您的访客位于代理服务器或路由器的后面,而标准Request.UserHostAddress仅捕获代理服务器或路由器的IP地址。在这种情况下,用户的IP地址将存储在服务器变量(“ HTTP_X_FORWARDED_FOR”)中。

因此,我们要做的是首先检查“ HTTP_X_FORWARDED_FOR”,如果该值为空,则只需返回即可ServerVariables("REMOTE_ADDR")

尽管此方法并非万无一失,但可以带来更好的结果。下面是VB.NET中的ASP.NET代码,摘自James Crowley的博客文章“ Gotcha:HTTP_X_FORWARDED_FOR返回多个IP地址”

C#

protected string GetIPAddress()
{
    System.Web.HttpContext context = System.Web.HttpContext.Current; 
    string ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

    if (!string.IsNullOrEmpty(ipAddress))
    {
        string[] addresses = ipAddress.Split(',');
        if (addresses.Length != 0)
        {
            return addresses[0];
        }
    }

    return context.Request.ServerVariables["REMOTE_ADDR"];
}

VB.NET

Public Shared Function GetIPAddress() As String
    Dim context As System.Web.HttpContext = System.Web.HttpContext.Current
    Dim sIPAddress As String = context.Request.ServerVariables("HTTP_X_FORWARDED_FOR")
    If String.IsNullOrEmpty(sIPAddress) Then
        Return context.Request.ServerVariables("REMOTE_ADDR")
    Else
        Dim ipArray As String() = sIPAddress.Split(New [Char]() {","c})
        Return ipArray(0)
    End If
End Function

24
请确保不要出于安全目的使用此代码,因为任何人都可以伪造HTTP_X_FORWARDED_FOR或类似的标头。因此,如果将其用于与安全相关的日志记录或安全检查,则攻击者可以轻松绕过它。
博士 邪恶的

4
从您的链接开始,what we actually needed to be doing was take the last IP address但是您的代码获得了第一个addresses[0]。哪个是对的?
纳尔逊·罗瑟梅尔

4
@NelsonRothermel基于en.wikipedia.org/wiki/X-Forwarded-For#Format如果您想要客户端(而不是先前的代理),则使用第一个
理查德(Richard)

4
@ dr.evil那么您会提出什么建议呢?因为我想通过指定的IP设置对WCF服务的访问。
krypru 2015年

7
addresses.Length != 0是没有必要的,因为它永远是0
詹姆斯威尔金斯

78

更新: 感谢Bruno Lopes。如果可能会有多个IP地址,则需要使用此方法:

    private string GetUserIP()
    {
        string ipList = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

        if (!string.IsNullOrEmpty(ipList))
        {
            return ipList.Split(',')[0];
        }

        return Request.ServerVariables["REMOTE_ADDR"];
    }

3
如另一个答案中所述,HTTP_X_FORWARDED_FOR可以是IP列表,以逗号分隔。
布鲁诺·洛佩斯

作为对函数的响应,我每次都会得到:: 1。我不能获得完整的IP地址吗???
farhangdon '16

1
它将在本地主机上返回:: 1。在productino环境中尝试,应该没问题。
本尼·玛格丽特

1
@farhangdon,下面的代码将返回本地主机中的IP地址,如下面的@Bat_Programmer所述System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList[1].ToString();
Benny Margalit

不要这样做!注意不要简单地使用列表中的第一个IP地址。您应该只跳过从最右边的条目开始的已知代理IP,以避免中间人攻击和标头欺骗。
Jpsy


23

您还认为用户IP地址还有什么?如果您需要网络适配器的IP地址,恐怕无法在Web应用程序中进行操作。如果您的用户使用NAT或其他工具,您也将无法获得IP。

更新:虽然有些网站使用IP来限制用户(例如Rapidshare),但它们在NAT环境中无法正常工作。


22

我想我应该与大家分享我的经验。好吧,我发现在某些情况下,REMOTE_ADDR不会为您提供所需的东西。例如,如果您在后台有一个负载均衡器,并且尝试获取客户端的IP,则将遇到麻烦。我使用IP屏蔽软件对其进行了检查,还与位于不同大洲的同事进行了检查。所以这是我的解决方案。

当我想了解客户的IP时,我尝试选择所有可能的证据,以便确定它们是否唯一:

在这里,我找到了另一个服务器变量,如果您想获得客户端的确切IP,它可以为您提供帮助。所以我正在使用:HTTP_X_CLUSTER_CLIENT_IP

HTTP_X_CLUSTER_CLIENT_IP始终为您提供客户端的确切IP。无论如何,如果它没有给您带来价值,那么您应该寻找HTTP_X_FORWARDED_FOR,因为它是获得客户端IP的第二佳选择,然后是REMOTE_ADDR var,它可能会也可能不会返回您的IP,但我拥有所有这些三是我发现监控它们的最佳方法。

我希望这对某些人有所帮助。


需要说的是,可以很容易地将http_x _...标头欺骗为remote_addr变量。因此remote_addr仍然是客户端ip地址的最可靠来源。
Ciro Corvino

1
@CiroCorvino是正确的,但是当您将网站托管在负载均衡器后面运行的服务器上时(正如我在我的帖子中已经提到的那样),那么remote_addr不会为您提供所需的IP。我已经体会到您在说什么,但是当我研究解决方案时,我所说的最适合我。
KMX

抱歉@KMX,实际上我还使用remote_addr和http_x_forwarded的组合来推断有关请求源的一些假设。如果您编辑自己的信息,我会更新我的投票
西罗·科维诺

15

您可以使用:

System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList.GetValue(0).ToString();

1
谢谢,对我来说,这是唯一真正在本地主机上返回我的IP的方法。
本尼·玛格丽特

实际上,我用来获取本地主机IP地址的是System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList[1].ToString();
Benny Margalit,2016年

1
该代码仅对我有用,并且我能够分别使用GetValue(0)和GetValue(1)获得IPv6和IPv4。谢谢!
拉吉·巴拉

但是,我在摆弄小提琴时遇到错误 Request for the permission of type 'System.Net.DnsPermission, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
vibs2006

OP询问如何获取远程Web用户的IP,而不是其自己的本地主机的地址。
t.durden

13

IP地址是“七层堆栈”中网络层的一部分。网络层可以使用IP地址做任何想做的事情。这就是代理服务器,NAT,中继或其他情况发生的情况。

应用层不应以任何方式依赖IP地址。特别是,IP地址并不意味着是网络连接一端的标识符以外的任何标识符。关闭连接后,您应该期望(同一用户的)IP地址发生更改。


1
很好,但是当多租​​户系统的一个客户要求其用户帐户只能从指定的IP地址登录时,您会怎么做?
罗尼·欧弗比

1
然后,他们必须告诉您服务器将看到哪个IP地址。如果他们需要一个特定的地址,那么他们将不能在NAT或类似地址之后。
约翰·桑德斯

@RonnieOverby我处于相同的情况。我需要知道他们从哪个IP连接,并且它必须在我的白名单中。然后,该应用可以根据其IP打开或关闭某些功能。这就是客户想要的。
t.durden

10

到目前为止,所有答复都考虑了非标准化但常见的X-Forwarded-For标题。有一个标准化的Forwarded标头,解析起来有些困难。一些示例如下:

Forwarded: for="_gazonk"
Forwarded: For="[2001:db8:cafe::17]:4711"
Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
Forwarded: for=192.0.2.43, for=198.51.100.17

我编写了一个类,在确定客户端的IP地址时要考虑这两个标头。

using System;
using System.Web;

namespace Util
{
    public static class IP
    {
        public static string GetIPAddress()
        {
            return GetIPAddress(new HttpRequestWrapper(HttpContext.Current.Request));
        }

        internal static string GetIPAddress(HttpRequestBase request)
        {
            // handle standardized 'Forwarded' header
            string forwarded = request.Headers["Forwarded"];
            if (!String.IsNullOrEmpty(forwarded))
            {
                foreach (string segment in forwarded.Split(',')[0].Split(';'))
                {
                    string[] pair = segment.Trim().Split('=');
                    if (pair.Length == 2 && pair[0].Equals("for", StringComparison.OrdinalIgnoreCase))
                    {
                        string ip = pair[1].Trim('"');

                        // IPv6 addresses are always enclosed in square brackets
                        int left = ip.IndexOf('['), right = ip.IndexOf(']');
                        if (left == 0 && right > 0)
                        {
                            return ip.Substring(1, right - 1);
                        }

                        // strip port of IPv4 addresses
                        int colon = ip.IndexOf(':');
                        if (colon != -1)
                        {
                            return ip.Substring(0, colon);
                        }

                        // this will return IPv4, "unknown", and obfuscated addresses
                        return ip;
                    }
                }
            }

            // handle non-standardized 'X-Forwarded-For' header
            string xForwardedFor = request.Headers["X-Forwarded-For"];
            if (!String.IsNullOrEmpty(xForwardedFor))
            {
                return xForwardedFor.Split(',')[0];
            }

            return request.UserHostAddress;
        }
    }
}

以下是一些我用来验证解决方案的单元测试:

using System.Collections.Specialized;
using System.Web;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UtilTests
{
    [TestClass]
    public class IPTests
    {
        [TestMethod]
        public void TestForwardedObfuscated()
        {
            var request = new HttpRequestMock("for=\"_gazonk\"");
            Assert.AreEqual("_gazonk", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedIPv6()
        {
            var request = new HttpRequestMock("For=\"[2001:db8:cafe::17]:4711\"");
            Assert.AreEqual("2001:db8:cafe::17", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedIPv4()
        {
            var request = new HttpRequestMock("for=192.0.2.60;proto=http;by=203.0.113.43");
            Assert.AreEqual("192.0.2.60", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedIPv4WithPort()
        {
            var request = new HttpRequestMock("for=192.0.2.60:443;proto=http;by=203.0.113.43");
            Assert.AreEqual("192.0.2.60", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedMultiple()
        {
            var request = new HttpRequestMock("for=192.0.2.43, for=198.51.100.17");
            Assert.AreEqual("192.0.2.43", Util.IP.GetIPAddress(request));
        }
    }

    public class HttpRequestMock : HttpRequestBase
    {
        private NameValueCollection headers = new NameValueCollection();

        public HttpRequestMock(string forwarded)
        {
            headers["Forwarded"] = forwarded;
        }

        public override NameValueCollection Headers
        {
            get { return this.headers; }
        }
    }
}

9

如果您使用的是CloudFlare,则可以尝试以下扩展方法

public static class IPhelper
{
    public static string GetIPAddress(this HttpRequest Request)
    {
        if (Request.Headers["CF-CONNECTING-IP"] != null) return Request.Headers["CF-CONNECTING-IP"].ToString();

        if (Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null) return Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString();

        return Request.UserHostAddress;
    }
}

然后

string IPAddress = Request.GetIPAddress();

并使用F5或Palo Alto?
Kiquenet


7

您可以做的是存储用户的路由器IP以及转发的IP,并尝试使用IP [外部公共和内部私有]使其可靠。但是几天后,可能会再次从路由器为客户端分配新的内部IP,但这将更加可靠。


5

结合@Tony@mangokun的答案,我创建了以下扩展方法:

public static class RequestExtensions
{
    public static string GetIPAddress(this HttpRequest Request)
    {
        if (Request.Headers["CF-CONNECTING-IP"] != null) return Request.Headers["CF-CONNECTING-IP"].ToString();

        if (Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null)
        {
            string ipAddress = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

            if (!string.IsNullOrEmpty(ipAddress))
            {
                string[] addresses = ipAddress.Split(',');
                if (addresses.Length != 0)
                {
                    return addresses[0];
                }
            }
        }

        return Request.UserHostAddress;
    }
}

您为什么使用HTTP_X_FORWARDED_FOR但不使用X_FORWARDED_FOR?他们是一样的吗?
Igor Yalovoy

3

在ashx文件中使用

public string getIP(HttpContext c)
{
    string ips = c.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
    if (!string.IsNullOrEmpty(ips))
    {
        return ips.Split(',')[0];
    }
    return c.Request.ServerVariables["REMOTE_ADDR"];
}

2
public static class Utility
{
    public static string GetClientIP(this System.Web.UI.Page page)
    {
        string _ipList = page.Request.Headers["CF-CONNECTING-IP"].ToString();
        if (!string.IsNullOrWhiteSpace(_ipList))
        {
            return _ipList.Split(',')[0].Trim();
        }
        else
        {
            _ipList = page.Request.ServerVariables["HTTP_X_CLUSTER_CLIENT_IP"];
            if (!string.IsNullOrWhiteSpace(_ipList))
            {
                return _ipList.Split(',')[0].Trim();
            }
            else
            {
                _ipList = page.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
                if (!string.IsNullOrWhiteSpace(_ipList))
                {
                    return _ipList.Split(',')[0].Trim();
                }
                else
                {
                    return page.Request.ServerVariables["REMOTE_ADDR"].ToString().Trim();
                }
            }
        }
    }
}

采用;

string _ip = this.GetClientIP();

0

很简单,尝试一下:

var remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress;

只是它 :))


1
仅供参考,以上代码仅适用于Asp.Net Core,不适用于常规的Asp.Net 链接
Dobin

-1

大家好,您会发现大多数代码将返回您的服务器IP地址而不是客户端IP地址,但是此代码返回了正确的客户端IP地址,请尝试一下。有关更多信息,请检查此

https://www.youtube.com/watch?v=Nkf37DsxYjI

使用javascript获取本地IP地址,您可以使用将此代码放入脚本标签中

<script>
    var RTCPeerConnection = /*window.RTCPeerConnection ||*/
     window.webkitRTCPeerConnection || window.mozRTCPeerConnection;

         if (RTCPeerConnection) (function () {
             var rtc = new RTCPeerConnection({ iceServers: [] });
             if (1 || window.mozRTCPeerConnection) {      
                 rtc.createDataChannel('', { reliable: false });
             };

             rtc.onicecandidate = function (evt) {

                 if (evt.candidate)
                     grepSDP("a=" + evt.candidate.candidate);
             };
             rtc.createOffer(function (offerDesc) {
                 grepSDP(offerDesc.sdp);
                 rtc.setLocalDescription(offerDesc);
             }, function (e) { console.warn("offer failed", e); });


             var addrs = Object.create(null);
             addrs["0.0.0.0"] = false;
             function updateDisplay(newAddr) {
                 if (newAddr in addrs) return;
                 else addrs[newAddr] = true;
                 var displayAddrs = Object.keys(addrs).filter(function
(k) { return addrs[k]; });
                 document.getElementById('list').textContent =
displayAddrs.join(" or perhaps ") || "n/a";
             }

             function grepSDP(sdp) {
                 var hosts = [];
                 sdp.split('\r\n').forEach(function (line) { 
                     if (~line.indexOf("a=candidate")) {   
                         var parts = line.split(' '),   
                             addr = parts[4],
                             type = parts[7];
                         if (type === 'host') updateDisplay(addr);
                     } else if (~line.indexOf("c=")) {      
                         var parts = line.split(' '),
                             addr = parts[2];
                         updateDisplay(addr);
                     }
                 });
             }
         })(); else
         {
             document.getElementById('list').innerHTML = "<code>ifconfig| grep inet | grep -v inet6 | cut -d\" \" -f2 | tail -n1</code>";
             document.getElementById('list').nextSibling.textContent = "In Chrome and Firefox your IP should display automatically, by the power of WebRTCskull.";

         }




</script>
<body>
<div id="list"></div>
</body>

为了获得您的公共IP地址,您可以使用将此代码放入脚本标签中

  function getIP(json) {
    document.write("My public IP address is: ", json.ip);
  }


<script type="application/javascript" src="https://api.ipify.org?format=jsonp&callback=getIP"></script>


-7

尝试:

using System.Net;

public static string GetIpAddress()  // Get IP Address
{
    string ip = "";     
    IPHostEntry ipEntry = Dns.GetHostEntry(GetCompCode());
    IPAddress[] addr = ipEntry.AddressList;
    ip = addr[2].ToString();
    return ip;
}
public static string GetCompCode()  // Get Computer Name
{   
    string strHostName = "";
    strHostName = Dns.GetHostName();
    return strHostName;
}

这将返回服务器ip地址
Sreekumar P

关于asp.net,它是一个Web应用程序,在服务器上运行,而不是在用户计算机上运行。
doekman '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.