在 C# 中请求网页欺骗主机
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/359041/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Request Web Page in c# spoofing the Host
提问by Xetius
I need to create a request for a web page delivered to our web sites, but I need to be able to set the host header information too. I have tried this using HttpWebRequest, but the Header information is read only (Or at least the Host part of it is). I need to do this because we want to perform the initial request for a page before the user can. We have 10 web server which are load balanced, so we need to request the file from each of the web servers.
我需要为传送到我们网站的网页创建一个请求,但我也需要能够设置主机标头信息。我已经使用 HttpWebRequest 尝试过这个,但是 Header 信息是只读的(或者至少是它的 Host 部分)。我需要这样做是因为我们希望在用户执行之前执行对页面的初始请求。我们有 10 个负载均衡的 Web 服务器,因此我们需要从每个 Web 服务器请求文件。
I have tried the following:
我尝试了以下方法:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm");
request.Headers.Set("Host", "www.mywebsite.com");
WebResponse response = request.GetResponse();
Obviously this does not work, as I can't update the header, and I don't know if this is indeed the right way to do it.
显然这不起作用,因为我无法更新标题,我不知道这是否确实是正确的方法。
采纳答案by Xetius
I have managed to find out a more long winded route by using sockets. I found the answer in the MSDN page for IPEndPoint:
我已经设法通过使用套接字找到了一条更漫长的路线。我在 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();
回答by annakata
Alright, little bit of research turns up this:
好吧,一点点研究结果是这样的:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=384456
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=384456
Seems MS maydo something about this at some point.
似乎 MS可能会在某个时候对此做些什么。
回答by Strelok
The "Host" header is protected and cannot be modified programmatically. I suppose to work around this, you could try and bind via reflection to the private "InnerCollection" property of the WebRequest object and calling the "Set" ar "Add" method on it to modify the Host header. I haven't tried this, but from a quick look at the source code in Reflector, I think it's easily accomplished. But yeah, binding to private properties of framework objects is not the best way to accomplish things. :) Use only if you MUST.
“主机”标头受保护,不能以编程方式修改。我想解决这个问题,您可以尝试通过反射绑定到 WebRequest 对象的私有“InnerCollection”属性,并在其上调用“Set”和“Add”方法来修改 Host 标头。这个我没试过,但是从 Reflector 中的源代码快速浏览一下,我认为它很容易完成。但是,是的,绑定到框架对象的私有属性并不是完成任务的最佳方式。:) 仅在必须时使用。
edit: Or like the other guy mentions in the linked question, just open up a socket and do a quick "GET" manually. Should be a no brainer, if you don't need to tinker with other stuff, like cookies or whatever else niceties the HttpWebRequest provides.
编辑:或者像链接问题中提到的其他人一样,只需打开一个套接字并手动执行快速“获取”。如果您不需要修改其他东西,例如 cookie 或 HttpWebRequest 提供的任何其他细节,那么应该很简单。
回答by Fox
I know this is old, but I came across this same exact problem, and I found a better solution to this then using sockets or reflection...
我知道这是旧的,但我遇到了同样的问题,我找到了一个更好的解决方案,然后使用套接字或反射......
What I did was create a new class that durives from WebHeaderCollection and bypasses validation of what you stick inside it:
我所做的是创建一个来自 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); }
}
}
and here is how you use it:
这是你如何使用它:
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;
Hope this helps anyone looking for this!
希望这可以帮助任何寻找这个的人!
回答by thedrs
I had a problem where the URL dns I used had several different IP addresses, I wanted to call each address separately using the same dns name in the host - the solution is using a proxy:
我遇到了一个问题,我使用的 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;
Host header is set from 'url' automatically by .NET, and 'ip' contains the actual address of the web server you want to contact (you can use a dns name here too)
主机头由 .NET 自动从 'url' 设置,'ip' 包含您要联系的 Web 服务器的实际地址(您也可以在此处使用 dns 名称)
回答by L.B
Although this is a very late answer, maybe someone can get benefit of it
虽然这是一个很晚的答案,但也许有人可以从中受益
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"});
Reflection is your friend :)
反射是你的朋友:)
回答by user2225191
You can use my solution for this problem, it posted here :
您可以使用我的解决方案来解决这个问题,它发布在这里:
How to set custom "Host" header in HttpWebRequest?
如何在 HttpWebRequest 中设置自定义“Host”标头?
This can help you to edit host header, and avoid to using proxy and direct socket requests.
这可以帮助您编辑主机头,并避免使用代理和直接套接字请求。
回答by Will Munn
I know this is an old question, but these days, you can do.
我知道这是一个老问题,但现在,你可以做到。
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm");
request.Host = "www.mywebstite.com";
WebResponse response = request.GetResponse();
回答by Stefan Steiger
Necromancing.
For those still on .NET 2.0
It is in fact quite easy, if you know how.
死灵法术。
对于那些仍然使用 .NET 2.0 的人
来说,如果您知道如何操作,这实际上很容易。
Problem is, you can't set the host header, because the framework won't let you change the value at runtime. (.net framework 4.0+ will let you override host in a httpwebrequest).
问题是,您无法设置主机标头,因为框架不允许您在运行时更改该值。(.net framework 4.0+ 将让您覆盖 httpwebrequest 中的主机)。
Next attempt will be setting the header with reflection - as demonstrated in the top upvoted answer here - to get around it, which will let you change the header value. But at runtime, it will overwritethis value with the host part of the url, which means reflection will bring you nothing, which is why I don'tunderstand why people keep upvoting this.
下一次尝试将使用反射设置标头 - 如此处最受好评的答案所示 - 绕过它,这将让您更改标头值。但在运行时,它会覆盖与主机部分该值的URL,该装置反射会带给你什么都没有,这就是为什么我不明白为什么人们保持了投票这一点。
If the dns-name doesn't exist, which is quite frankly the only case in which you want to do this in the first place, you can't set it, because .NET can't resolve it, and you can't override the .NET DNS resolver.
如果 dns-name 不存在,坦率地说,这是您首先要执行此操作的唯一情况,则无法设置它,因为 .NET 无法解析它,并且您不能覆盖 .NET DNS 解析器。
But what you can do, is setting a webproxy with the exact same IP as the destination server.
但是您可以做的是设置一个与目标服务器 IP 完全相同的 webproxy。
So, if your server IP is 28.14.88.71:
因此,如果您的服务器 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
}
}
and voila, now
瞧,现在
myweb wc = new myweb();
string str = wc.DownloadString("http://redmine.netexistantdomain.com");
and you get the correct page back, if 28.14.88.71 is a webserver with virtual name-based hosting (based on http-host-header).
如果 28.14.88.71 是具有基于虚拟名称的托管(基于 http-host-header)的网络服务器,则您会返回正确的页面。
Now you have the correct answer to the original question, for both WebRequest and WebClient. I think using custom sockets to do this would be the wrong approach, particularly when SSL should be used, and when an actual solution is that simple...
现在您对 WebRequest 和 WebClient 的原始问题有了正确的答案。我认为使用自定义套接字来执行此操作将是错误的方法,尤其是在应该使用 SSL 时,并且当实际解决方案如此简单时......