Secure HttpWebRequest so I can send credentials possible? - c#

I have the following code which connects to my php server and retrieves data from it. The only thing is, I need to send the username and password securely from this webrequest to the PHP server. Looking at the docs for the webrequest class, there is a credentials property as well as a preauthenticate property. I'm assuming these are for the network credentials (all my users are in AD).
Is it possible to secure this post request with credentials or is this just a bad idea? I've also found SetBasicAuthHeader - I'll read up on this and see if it might help. All traffic will be on SSL from ASPX site to the PHP site
// variables to store parameter values
string url = "https://myphpserver.php";
// creates the post data for the POST request
string postData = "Username=" + username + "&Password=" + "&UID=" + UniqueRecID;
// create the POST request
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = postData.Length;
// POST the data
using (StreamWriter requestWriter2 = new StreamWriter(webRequest.GetRequestStream()))
{
requestWriter2.Write(postData);
}
// This actually does the request and gets the response back
HttpWebResponse resp = (HttpWebResponse)webRequest.GetResponse();
string responseData = string.Empty;
using (StreamReader responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream()))
{
// dumps the HTML from the response into a string variable
responseData = responseReader.ReadToEnd();
}

SetBasicAuthHeader is for HTTP Basic Access Authentication so won't help here as you're handling authentication at application level. Really, this is no more insecure than just going to the page in a browser. I see you're using SSL so your request will be encrypted anyway and you have nothing to worry about.
If you're concerned for some other reason (although I can't think why), it sounds like you have control over the PHP end so you could just encrypt the password and add an extra POST parameter so the server knows to decrypt it.

When using HTTPS your data is safe in the message and transport scope. It means no one can decode it or sniff the packets. I suggest you read this article HTTPS Wiki

Related

Can not Access a URL using Webrequest

I am trying to get access a URL using .Net but when I run my Program I get the Error The remote server returned an error: (403) Forbidden. Now, the issue is if I click the link http://thisIsMyUR, and enter the user name and password as in the below code. It totally works. I am not able to understand why this exception is coming? Please refer the code below.
Side Note: I am using this sample function below to fire Build of my project in Jenkins Server.
string url = "http://thisIsMyURL";
WebRequest webRequest = WebRequest.Create(url);
webRequest.Credentials = new NetworkCredential("admin", "pass");
WebResponse response = webRequest.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string responseText = reader.ReadToEnd();
Other than what was recommended by Bradley Uffner in the comments, you can try to provide an actual user agent during the request. I have seen certain servers which would not respond to requests without that header for some odd perceived security reason.
EDIT: as requested, i'll update with some more information.
Some servers may choose to ignore requests, giving some error code (or closing connection) when certain conditions are not met. A good way of checking if that is the case, is to actually send all the standard headers sent by your average web browser in the request. The "User agent" is one of those headers, and this example adds it to your request:
string url = "http://thisIsMyURL";
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.UserAgent = "USER AGENT VALUE";
HttpWebRequest.Credentials = new NetworkCredential("admin", "pass");
WebResponse response = webRequest.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string responseText = reader.ReadToEnd();
The cast to HttpWebRequest is required so you have access to the UserAgent header field. Other standard HTTP fields can be accessed this way.
You can check which headers are sent by your browser of choice by inspecting its requests using the browser developer tools (usually Right click on webpage -> Inspect elements -> Network).
I figured it out with the help of some articles. A basic authentication needs to be added. Below is a sample.
var userName = "admin;"
var password = "password";
var encodedAuthentication = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(userName + ":" + password));
webRequest.Headers.Add("Authorization", "Basic " + encodedAuthentication);

Stripe 401 - No valid API key provided - C#

I get following error: "The remote server returned an error: (401) Unauthorized" in this line:
using (HttpWebResponse httpResponse = request.GetResponse() as HttpWebResponse) {}
Here is the complete code:
string clientSecretKey = ConfigurationManager.AppSettings["ClientSecretKey"];
const string ChargeUrl = "https://api.stripe.com/v1/charges?amount={0}&currency={1}&source={2}&description={3}";
string requestUrl = HttpUtility.UrlPathEncode(
String.Format(ChargeUrl, 1000, "usd", "tok_19xLu8HN9aKw9vrkUsflNWOI", "Test charge to text#example.com") );
HttpWebRequest request = WebRequest.Create(requestUrl) as HttpWebRequest;
request.Headers.Add("Authorization", "sk_test_example");
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
using (HttpWebResponse httpResponse = request.GetResponse() as HttpWebResponse)
{/* some code */}
At the beginning I thought the error was caused because the token can't used more than one time, but I changed it and got the same error. I'm not sure what is causing the error.
The issue here is that you are passing the API key but not using Bearer authentication which is what Stripe's API expects. You need to change your Authorization header like this:
request.Headers.Add("Authorization", "Bearer sk_test_example");
I know you mentioned in the comments that you can't use a third-party library but I wanted to mention one just in case. Stripe.net lets you use Stripe's API in .Net easily without having to rewrite the logic yourself. Handling errors, encoding parameters and sub-hashes properly, managing authentication and JSON decoding, all of this will take a lot of time and trial and error to build from scratch while this library would handle all of this for you.
You're initializing a variable for the secret key but not using it. Try modifying the request url to start with "https://" + clientSecretKey + ":#api..."
This is, of course, assuming clientSecretKey is the Stripe key.
Be careful putting a secret key somewhere on your server that it isn't hidden from a user / client.

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
;

Consume Web Service with POST from a WCF Service

So this is my situation. I have to consume a third party web service (not wcf) from another WCF service that will serve as an intermediary between the first service and my web app. The problem is almost every example I have seen on doing this requieres you to Add Web/Service Reference to the app in order to generate the proxy, but I can't add the reference, it returns an error, possibly due to some authentication required.
This service can be consumed only by either GET or POST. I was successful in consuming the service by both GET and POST from an ajax call with jquery in a web page, but I don't know how to consume the service from inside a wcf service in c#.
An example GET request from the service is:
http://webservice.server.com/services/myservice?user=[username]&password=[password]&value1=[somevalue]&value2=[anothervalue]
The response is an xml with the status code of the operation and a status message, which I then proceed to save to a database.
How might I go about doing this?
Thank you for any help...
SOLUTION
Thanks to Sean for pointing me in the right direction. How I did it:
Reference article: How to use HttpWebRequest to send POST request to another web server
ASCIIEncoding encoding = new ASCIIEncoding();
string postData = "username=" + username;
postData += ("&password=" + password);
postData += ("&value1=" + val1);
postData += ("&value2=" + val2);
byte[] data = encoding.GetBytes(postData);
// Prepare POST web request...
HttpWebRequest myRequest =
(HttpWebRequest)WebRequest.Create(new Uri("http://webservice.server.com/services/myservice"));
myRequest.Method = "POST";
myRequest.ContentType = "application/x-www-form-urlencoded";
myRequest.ContentLength = data.Length;
Stream newStream = myRequest.GetRequestStream();
// Send the data.
newStream.Write(data, 0, data.Length);
newStream.Close();
// Get response
using (HttpWebResponse response = myRequest.GetResponse() as HttpWebResponse)
{
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream());
// Read the whole contents and return as a string
result = reader.ReadToEnd();
}
XDocument doc = XDocument.Parse(result);
// Read XML
Please if you have any comments on my solution, objections or improvements, all comments are welcomed.
I think you'd want to take a look at the HttpRequest class:
http://msdn.microsoft.com/en-us/library/system.web.httprequest.aspx
If you can't add the web service reference (I would investigate further why you can't do this first) I am afraid you'll have to do this manually issuing an HTTP Request manually using the WebClient class WebClient or the HttpReqest class as Sean suggests

Logging in to eBay using HttpWebRequest fails due to 'The browser you are using is rejecting cookies' response

I'm trying to log in to my eBay account using the following code:
string signInURL = "https://signin.ebay.com/ws/eBayISAPI.dll?co_partnerid=2&siteid=0&UsingSSL=1";
string postData = String.Format("MfcISAPICommand=SignInWelcome&userid={0}&pass={1}", "username", "password");
string contentType = "application/x-www-form-urlencoded";
string method = "POST";
string userAgent = "Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)";
CookieContainer cookieContainer = new CookieContainer();
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(signInURL);
req.CookieContainer = cookieContainer;
req.Method = method;
req.ContentType = contentType;
req.UserAgent = userAgent;
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] loginDataBytes = encoding.GetBytes(postData);
req.ContentLength = loginDataBytes.Length;
Stream stream = req.GetRequestStream();
stream.Write(loginDataBytes, 0, loginDataBytes.Length);
stream.Close();
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
StreamReader xsr = new StreamReader(res.GetResponseStream());
String responseText = xsr.ReadToEnd();
Obviously substituting my real username and password. When I look at the string responseText, I see that part of the response from eBay is
The browser you are using is rejecting cookies.
Any ideas what I'm doing wrong?
P.S. And yes, I am also using the eBay API, but this is for something slightly different than what I want to do with the API.
You're doing a direct http request. The Ebay site has functionality to talk to a browser (probably to store the session cookie). Unless you make the request code smart enough to use cookies correctly it won't work. You'll probably have to use the internet explorer object instead.
Before doing the POST you need to download the page with the form that you are submitting in your code, take the cookie they give you, put it in your CookieContainer (making sure you get the path right) and post it back up in your request.
To clarify, while you might be POSTing the correct data, you are not sending the cookie that needs to go with it. You will get this cookie from the login page.
You need to intercept the http traffic to see what exactly what had happened. I use Fiddler2. It is the good tools for debugging http. So I can know whos wrong, my application or the remote web server.
Using fiddler, you can see the request header, response header with its cookies as well as response content. It used in the middle of your app and the Ebay.
Based on my experience. I think it is because Ebay cookie sent to you is not send back to Ebay server. Fiddler will prove it whether yes or not.
Another thing, the response cookie you receive should be send back to next request by using the same CookieContainer.
You should notice that CookieContainer has a bug on .Add(Cookie) and .GetCookies(uri) method. You may not using it, but internal codes might use it.
See the details and fix here:
http://dot-net-expertise.blogspot.com/2009/10/cookiecontainer-domain-handling-bug-fix.html
CallMeLaNN

Categories