【问题标题】:Request Web Page in c# spoofing the Host在 c# 中请求网页欺骗主机
【发布时间】:2010-09-26 10:05:17
【问题描述】:

我需要为发送到我们网站的网页创建一个请求,但我也需要能够设置主机标头信息。我已经尝试过使用 HttpWebRequest,但是标头信息是只读的(或者至少它的主机部分是只读的)。我需要这样做,因为我们想在用户之前执行页面的初始请求。我们有 10 个负载均衡的 Web 服务器,因此我们需要从每个 Web 服务器请求文件。

我尝试了以下方法:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm");
request.Headers.Set("Host", "www.mywebsite.com");
WebResponse response = request.GetResponse();

显然这不起作用,因为我无法更新标题,而且我不知道这是否确实是正确的方法。

【问题讨论】:

标签: c# http http-headers


【解决方案1】:

虽然这是一个很晚的答案,但也许有人可以从中受益

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://192.168.1.1"));
request.Headers.GetType().InvokeMember("ChangeInternal", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, request.Headers, new object[] {"Host","www.mysite.com"});

反思是你的朋友:)

【讨论】:

  • L.B - 这很酷,我是 C# 新手,不知道什么是反射以及你的答案对我有什么帮助
  • 注意:这将改变主机,但它会被您发送请求的 url 中的值覆盖。
【解决方案2】:

我已经设法通过使用套接字找到了一条更漫长的路线。我在 IPEndPoint 的 MSDN 页面中找到了答案:

string getString = "GET /path/mypage.htm HTTP/1.1\r\nHost: www.mysite.mobi\r\nConnection: Close\r\n\r\n";
Encoding ASCII = Encoding.ASCII;
Byte[] byteGetString = ASCII.GetBytes(getString);
Byte[] receiveByte = new Byte[256];
Socket socket = null;
String strPage = null;
try
{
    IPEndPoint ip = new IPEndPoint(IPAddress.Parse("10.23.1.93"), 80);
    socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
    socket.Connect(ip);
}
catch (SocketException ex)
{
    Console.WriteLine("Source:" + ex.Source);
    Console.WriteLine("Message:" + ex.Message);
}
socket.Send(byteGetString, byteGetString.Length, 0);
Int32 bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
strPage = strPage + ASCII.GetString(receiveByte, 0, bytes);

while (bytes > 0)
{
    bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
    strPage = strPage + ASCII.GetString(receiveByte, 0, bytes);
}
socket.Close();

【讨论】:

    【解决方案3】:

    我有一个问题,我使用的 URL dns 有几个不同的 IP 地址,我想在主机中使用相同的 dns 名称分别调用每个地址 - 解决方案是使用代理:

    string retVal = "";
                // Can't change the 'Host' header property because .NET protects it
                // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                // request.Headers.Set(HttpRequestHeader.Host, DEPLOYER_HOST);
                // so we must use a workaround
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.Proxy = new WebProxy(ip);
                using (WebResponse response = request.GetResponse())
                {
                    using (TextReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        string line;
                        while ((line = reader.ReadLine()) != null)
                            retVal += line;
                    }
                }
                return retVal;
    

    主机标头由 .NET 自动从“url”设置,“ip”包含您要联系的 Web 服务器的实际地址(您也可以在此处使用 dns 名称)

    【讨论】:

    • 当我们实际需要使用代理时,这显然行不通:)
    【解决方案4】:

    我知道这是旧的,但我遇到了同样的问题,我找到了一个更好的解决方案,然后使用套接字或反射......

    我所做的是创建一个来自 WebHeaderCollection 的新类,并绕过对您在其中粘贴的内容的验证:

    public class MyHeaderCollection:WebHeaderCollection
    {
        public new void Set(string name, string value)
        {
            AddWithoutValidate(name, value);
        }
        //or
        public new string this[string name]
        {
            get { return base[name]; }
            set { AddWithoutValidate(name, value); }
        }
    }
    

    这是你如何使用它的:

        var http = WebRequest.Create("http://example.com/");
        var headers = new MyHeaderCollection();
        http.Headers = headers;
    
        //Now you can add/override anything you like without validation:
        headers.Set("Host", http.RequestUri.Host);
        //or
        headers["Host"] = http.RequestUri.Host;
    

    希望这对寻找这个的人有所帮助!

    【讨论】:

    • 这在 3.5SP1 中不起作用 - 传递给 Headers 的集合不会被保留,它会被复制到新的 WebHeaderCollection 中,因此所有标题都会重新验证。
    【解决方案5】:

    我知道这是一个老问题,但现在,你可以做到。

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm");
    request.Host = "www.mywebstite.com";
    WebResponse response = request.GetResponse();
    

    【讨论】:

      【解决方案6】:

      “主机”标头受保护,不能以编程方式修改。我想解决这个问题,您可以尝试通过反射绑定到 WebRequest 对象的私有“InnerCollection”属性,并在其上调用“Set”ar“Add”方法来修改 Host 标头。我还没有尝试过,但是通过快速查看 Reflector 中的源代码,我认为它很容易完成。但是,是的,绑定到框架对象的私有属性并不是完成事情的最佳方式。 :) 仅在必须时使用。

      编辑:或者像链接问题中提到的其他人一样,只需打开一个套接字并手动执行快速“GET”。如果您不需要修改其他东西,比如 cookie 或 HttpWebRequest 提供的任何其他细节,那么应该是不费吹灰之力。

      【讨论】:

        【解决方案7】:

        好吧,稍微研究一下:

        https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=384456

        似乎 MS 可能会在某个时候对此采取一些措施。

        【讨论】:

        • 我得到异常“ArgumentException : The 'Host' header cannot be modified directly. Parameter name: name”。我认为它在 HttpWebRequest 类中是只读的
        • 创建一个新答案而不是取消删除并完全重写旧答案不是更好吗?
        • 嗯,可能 - 这里的礼仪不清楚(指点任何人?),但我倾向于保留历史而不是重新开始。我确实想知道我是否应该保留失败的答案以明显显示它不起作用。
        • 麻烦的是,仍然连接到这个答案的 cmets 和 votes 不再有意义。因此,在我看来,一个新的答案会更好。
        【解决方案8】:

        你可以使用我的解决方案来解决这个问题,它贴在这里:

        How to set custom "Host" header in HttpWebRequest?

        这可以帮助您编辑主机头,避免使用代理和直接套接字请求。

        【讨论】:

          【解决方案9】:

          死灵术。
          对于仍在使用 .NET 2.0 的用户
          其实很简单,如果你知道怎么做的话。

          问题是,您不能设置主机标头,因为框架不允许您在运行时更改值。 (.net framework 4.0+ 将允许您在 httpwebrequest 中覆盖主机)。

          下一次尝试将设置带有反射的标题 - 如此处最受好评的答案所示 - 绕过它,这将让您更改标题值。但是在运行时,它会用url的host部分覆盖这个值,这意味着反射不会给你带来任何东西,这就是为什么我不 了解人们为什么一直支持投票。

          如果 dns 名称不存在,坦率地说,这是您首先要执行此操作的唯一情况,您无法设置它,因为 .NET 无法解析它,并且您无法覆盖 .NET DNS 解析器。

          但是您可以做的是设置一个与目标服务器具有完全相同 IP 的网络代理。

          所以,如果你的服务器 IP 是 28.14.88.71:

          public class myweb : System.Net.WebClient
          {
              protected override System.Net.WebRequest GetWebRequest(System.Uri address)
              {
                  System.Net.WebRequest request = (System.Net.WebRequest)base.GetWebRequest(address);
                  //string host = "redmine.nonexistantdomain.com";
          
                  //request.Headers.GetType().InvokeMember("ChangeInternal",
                  //    System.Reflection.BindingFlags.NonPublic |
                  //    System.Reflection.BindingFlags.Instance |
                  //    System.Reflection.BindingFlags.InvokeMethod, null,
                  //    request.Headers, new object[] { "Host", host }
                  //);
          
                  //server IP and port
                  request.Proxy = new System.Net.WebProxy("http://28.14.88.71:80");
          
                  // .NET 4.0 only
                  System.Net.HttpWebRequest foo = (System.Net.HttpWebRequest)request;
                  //foo.Host = host;
          
                  // The below reflection-based operation is not necessary, 
                  // if the server speaks HTTP 1.1 correctly
                  // and the firewall doesn't interfere
                  // https://yoursunny.com/t/2009/HttpWebRequest-IP/
                  System.Reflection.FieldInfo horribleProxyServicePoint = (typeof(System.Net.ServicePoint))
                      .GetField("m_ProxyServicePoint", System.Reflection.BindingFlags.NonPublic |
                      System.Reflection.BindingFlags.Instance);
          
                  horribleProxyServicePoint.SetValue(foo.ServicePoint, false);
                  return foo; // or return request; if you don't neet this
              }
          
          
              }
          

          瞧,现在

          myweb wc = new myweb();
          string str = wc.DownloadString("http://redmine.netexistantdomain.com");
          

          如果 28.14.88.71 是具有基于虚拟名称的托管(基于 http-host-header)的网络服务器,那么您会返回正确的页面。

          现在您对于 WebRequest 和 WebClient 都得到了原始问题的正确答案。我认为使用自定义套接字来执行此操作是错误的方法,尤其是在应该使用 SSL 时,以及当实际解决方案如此简单时......

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-05-03
            • 1970-01-01
            • 2016-03-07
            • 2010-11-13
            • 1970-01-01
            • 1970-01-01
            • 2017-04-08
            • 2014-01-18
            相关资源
            最近更新 更多