C# Network credentials not being passed to server? - c#

Edit: Using:
byte[] authBytes = System.Text.Encoding.UTF8.GetBytes(user + ":" + password);
wr.Headers["Authorization"] = "Basic " + Convert.ToBase64String(authBytes);
Seems to work fine.
I have an application that communicates with a CMS. I'm currently trying to get this client to be able to upload text/xml data to the CMS using a "POST" method.
I can pass this through using curl perfectly fine:
curl -u user:password -H "Content-Type:text/xml" -d "<element>myXML</element>" serverURL
However, trying to use the HttpWebRequest in C# I can't get the server to return what I want it to. So I fired up Wireshark and had a look at what was actually being passed through, and it's pretty much identical except for the fact that when using curl I can see:
Authorization: Basic <a bunch of hex>=\r\n
Credentials: user:password
In the HTTP header fields, while in the output from my client, these header fields are simply not present. ("Credentials:" isn't actually there in plain text, it's a subtree of "Authorization:" - so I'm not sure where it's getting it from, but the username and password are correct.)
The C# code I'm trying to use to set the credentials for the webrequest is something like this:
NetworkCredential myCred = new NetworkCredential(
user, password, serverURL);
CredentialCache myCache = new CredentialCache();
myCache.Add(new Uri(serverURL), "Basic", myCred);
HttpWebRequest wr = (HttpWebRequest) HttpWebRequest.Create(serverURL);
wr.Credentials = myCache;
I've tried just setting the credentials like this too (and without specifying serverURL):
wr.Credentials = new NetworkCredential(user,password,serverURL);
But that still doesn't make it show up in wireshark. Does anyone have any idea if:
A) That authorization information should actually be in the HTTP header for this to work, and
B) If it is - then how do I make C# put it in? I only seem to be able to find decent examples using the default credentials, which doesn't apply to what I'm doing.
Thanks in advance.

.NET's WebRequest has an infuriating default behavior where it only sends credentials after receiving an HTTP 401 Not Authorized response.
Manually adding the credentials header (as you've done) seems to be the best solution available.
More details in this post

Related

CredentialCache and HttpWebRequest in .NET

I'm having difficulty understanding how web requests and credentials work in .NET.
I have the following method that is executing a request to a SOAP endpoint.
public WebResponse Execute(NetworkCredential Credentials)
{
HttpWebRequest webRequest = CreateWebRequest(_url, actionUrl);
webRequest.AllowAutoRedirect = true;
webRequest.PreAuthenticate = true;
webRequest.Credentials = Credentials;
// Add headers and content into the requestStream
asyncResult.AsyncWaitHandle.WaitOne();
return webRequest.EndGetResponse(asyncResult);
}
It works well enough. However, users of my applications may have to execute dozens of these requests in short succession. Hundreds over the course of the day. My goal is to implement some of the recommendations I've read about, namely using an HttpClient that exists for the entire lifetime of the application, and to use the CredentialCache to store user's credentials, instead of passing them in to each request.
So I'm starting with the CredentialCache.
Following the example linked above, I instantiated a CredentialCache and added my network credentials to it. Note that this is the exact same NetworkCredential object that I was passing to the request earlier.
NetworkCredential credential = new NetworkCredential();
credential.UserName = Name;
credential.Password = PW;
Program.CredCache.Add(new Uri("https://blah.com/"), "Basic", credential);
Then, when I go to send my HTTP request, I get the credentials from the cache, instead of providing the credentials object directly.
public WebResponse Execute(NetworkCredential Credentials)
{
HttpWebRequest webRequest = CreateWebRequest(_url, actionUrl);
webRequest.AllowAutoRedirect = true;
webRequest.PreAuthenticate = true;
webRequest.Credentials = Program.CredCache;
// more stuff down here
}
The request now fails with a 401 error.
I am failing to understand this on several levels. For starters, I can't seem to figure out whether or not the CredentialCache has indeed passed the proper credentials to the HTTP request.
I suspect part of the problem might be that I'm trying to use "Basic" authentication. I tried "Digest" as well just as a shot in the dark (which also failed), but I'm sure there must be a way to see what kind of authentication the server is expecting.
I have been combing StackOverflow and MDN trying to read up as much as possible about this, but I am having a difficult time separating the relevant information from the outdated and irrelevant information.
If anyone can help me solve the problem that would be most appreciated, but even links to proper educational resources would be helpful.
According to the documentation the CredentialCache class is only for SMTP, it explicitly says that it is not for HTTP or FTP requests:
https://msdn.microsoft.com/en-us/library/system.net.credentialcache(v=vs.110).aspx
Which directly contradicts the info in the later api docs. Which one is right I don't know.
You could try using the HttpClient class. The methods and return types are different, so you would need to tweak your other a code a bit, but it would look a bit like this:
public class CommsClass
{
private HttpClient _httpClient;
public CommsClass(NetworkCredential credentials)
{
var handler = new HttpClientHandler { Credentials = credentials };
_httpclient = new HttpClient(handler);
}
public HttpResponseMessage Execute(HttpRequestMessage message)
{
var response = _httpClient.SendAsync(message).Result;
return response;
}
}
You can do all sorts of other things with the handler, and the client like set request headers or set a base address.

Logging in to HTTPS interface of Serv-U server

I am trying to connect to a corporate ftp server via code (C#/VB). It has been set up to run in a browser using SolarWinds Serv-U software so that users can access it via a browser. The address is in the form:
https://ftp.example.com
From here they are presented with a login form (part of Serv-U) in which they enter their u/p and log in.
I have been trying to use HttpWebRequest class to log in, but each time I get an '401 Unauthorised - not logged in' error. In the web request I set the credentials:
Dim loginUri = New Uri("https://ftp.example.com")
Dim loginRequest As HttpWebRequest = DirectCast(WebRequest.Create(loginUri), HttpWebRequest)
With loginRequest
.Accept = "*/*"
.ContentType = "application/x-www-form-urlencoded"
.CookieContainer = New CookieContainer()
.Credentials = New NetworkCredential("user", "pass")
.Method = WebRequestMethods.Http.Get
End With
Dim loginResponse As HttpWebResponse = loginRequest.GetResponse()
I'm not even sure if this approach is possible; there are quite a number of cookies set by the browser during the login process which is not a desirable thing to replicate in code.
I've done a fair bit of searching on the subject and haven't found any definitive answers. Should I just push back on the sysadmin to set up a proper ftp server over SSL? It is a requirement that we use :443 as many firewalls block 21 (& 22).
Thanks - Z

HttpClient in .net issues 2 requests when providing username and password in NetworkCredentials

When using the HttpClient in .net 4.5 to do basic authentication I'm finding that it's issuing 2 requests.
The first fails with a HTTP/1.1 401 Unauthorized and then it resends the request to which we get a HTTP/1.1 200 OK.
Any ideas on how to stop it from doing this?
var credential = new NetworkCredential
{
UserName = username,
Password = password
}
var httpClientHandler = new System.Net.Http.HttpClientHandler
{
Credentials = credential
};
httpClient = new HttpClient(httpClientHandler, true)
{
BaseAddress = address
};
Try setting this:
httpClientHandler.PreAuthenticate = true;
I think the very first request will still get the initial 401, but subsequent request to the same URI up to the last forward slash should always send the authentication headers without the 401.
You could try setting HttpClientHandler.PreAuthenticate as per Tobberoth's answer, although the documentation for that suggests it will only help after the very first request:
With the exception of the first request, the PreAuthenticate property indicates whether to send authentication information with subsequent requests to a Uri that matches the specific Uri up to the last forward slash without waiting to be challenged by the server.
It won't help for the very first request, but it may help to reduce the number of round trips after that.
Another thing to try is including "Authorization" in HttpClient.DefaultRequestHeaders. I'd be slightly surprised if that worked, but it's worth trying, at least.
While looking for an answer to another problem I have with the NetworkCredentials I stumbled across two questions with answers that might be the ones you're looking for:
Here the answer says that at least WebRequest has the default behaviour of only sending the credentials after getting a 401. And here it seems that the solution might be using a CredentialCache.
I'm not sure if the CredentialCache works with the HttpClient, but if it doesn't you could switch to WebRequest or also WebClient, or maybe this information just brought you on the path to another solution.
The best to avoid the second call is to create and set Authorization header manually:
https://stackoverflow.com/a/4346843/1143824

what's the proper way to encrypt login credentials in an http request?

If I need to make an HttpRequest to a site that requires login credentials, I can use code similar to the following, but as you can see the username and password are just base64 encoded, which means that if someone were to intercept the http request all they have to do is search for the value associated with the "Authorization" header and they have my login information.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://...");
request.Headers.Add("Authorization",
string.Format("Basic {0}", Convert.ToBase64String(Encoding.Default.GetBytes(
string.Format("{0}:{1}", username, password)))));
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream s = response.GetResponseStream())
{
using (StreamReader r = new StreamReader(s))
{
DoSomething(r.ReadToEnd());
}
}
}
Is the following code a better alternative or does it also make http requests with the "Basic " Authorization header?
WebClient wc = new WebClient();
wc.Credentials = new NetworkCredential(username, password);
DoSomething(wc.DownloadString("https://..."));
If neither code results in http requests that "hide" the login credentials and if there is actually a way to "hide" them, what's the proper way to do it?
If you're making a webrequest to a server using HTTPS your entire message including headers are encrypted. So there is essentially no need for you to worry about encrypting your data. If you were to open Wireshark and try and sniff the packets, you'd see they are unreadable.
The .NET Framework will take care of the SSL encryption required under the hood based on the URL you're sending requests to.
More information can be found on MSDN: http://msdn.microsoft.com/en-us/library/ds8bxk2a.aspx
Both of these will use HTTP Basic Access Authentication, but being as you appear to be using HTTPS you won't have to worry about this as the entire request will be encrypted anyway.
I would recommend using WebClient not considering that it part of encryption i just think it a lot more cleaner approach and if you really care about login credentials you can use
SecureString testString = new SecureString();
http://blogs.msdn.com/b/fpintos/archive/2009/06/12/how-to-properly-convert-securestring-to-string.aspx
;

web service - 400 bad request

i want to consume a php Webservice from C# which is protected by htaccess.
i added the Service by adding a web reference in my VS 2010.
mywsfromwsdl myws = new mywsfromwsdl();
System.Net.CredentialCache myCredentials = new System.Net.CredentialCache();
NetworkCredential netCred = new NetworkCredential("user", "pass");
myCredentials.Add(new Uri(myws.Url), "Basic", netCred);
myws.Credentials = myCredentials;
myws.PreAuthenticate = true;
tbxIN.Text = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> "+
" <test> "+
" <parm1>4</parm1> "+
" <parm1>2</parm1> "+
" </test>";
tbxOUT.Text= myws.func1(tbxIN.Text.ToString());
The VS shows an error called 400 Bad REquest my last row.
If i delete the .htaccess File on the server, the pgm works fine, but i cant delete. because other PHP User use the Service.
Can anybody tell me how to send the Credentials correctly ?
By jo
Sometimes C# and Apache clash a bit: in this case, it might be that your client is expecting a 100 Continue response due to authentication being active, but the server doesn't send it.
This kind of behavior is toggled by this line:
ServicePointManager.Expect100Continue = false;
Add it before executing the request.
It's also worth pointing out that when 400 bad request happens, you might find some useful details in server's logs.
According to MSDN you shouldn't need the credential cache:
// Set the client-side credentials using the Credentials property.
ICredentials credentials = new NetworkCredential("Joe",SecurelyStoredPassword,"mydomain");
math.Credentials = credentials;
Have you tried this method instead of the cache object? More info can be found here.

Categories