mvc encoding/decoding the querystring - c#

I am developing an asp.net mvc 6 application, and as part of the application we will send out emails, which contain a link that a user can click on that sends them to a particular action method.
An example of an emailed link would be
http://identity.platform:7000/account/register?emailinvitation=true&email=yerg#test.com
Which would then go to the AccountController Register action method:
Register(string userName, bool emailInvitation=false, string email="" )
What I would like is to do a Base-64 encoding of the url, so the user is not then tempted to manually change any of the parameters, so then we have a link like
http://identity.platform:7000/account/register?url=ZW1haWxpbnZpdGF0aW9uPXRydWUmZW1haWw9eWVyZ0B0ZXN0LmNvbQ==
So the flow in my mvc application would be
receive the request
check if there is a url parameter that needs decoding
if so decode and send on to the appropriate controller/action method
My question is, whereabouts should I be intercepting the request and decoding it? Should this happen in the routing, or somewhere later? How do I then redirect to the action method with the appropriate parameters

On the server, generate a GUID for the specific invite, and send that in the email instead of the params.
You will also need an overload for the Register action method which accepts the GUID string instead.
Register(string guid) {
}
It will fetch the linked details (e.g. email address) from the data store and then proceed as per your normal process.
Unlike base64, there's no way for anyone to reverse it and discover the parameters, and it's hard for the user to guess another valid GUID. There's no need for you to worry about encoding and decoding them, and you can easily make them one-time-only tokens, which may be helpful to your business process. Another bonus is that you don't end up with sensitive data like email addresses in your server logs or user's browsing history, or transmitted in the clear over HTTP (as per your example URL).

Related

rest api can't receive encoded parameters in urls

We have some endpoints that receive a username, the username may contain special characters, for example:
POST /api/users/{userName}/cars/
if the username is "joe+doe", then we have a 404 error, we have enconded and send
POST /api/users/joe%2Bdoe/cars/
Still it doesn't work
We could either change to an id or change it into a format like "api/users/cars?username={username}" but we would like to follow the logic we have.
So the question is "is there a way to configure endpoints in rest api to accepts encoded chars"

How safe or dangerous it is to use "UserAgent" of the request to identify the origin of the request?

I am developing an SMS service which sends SMS to the destination numbers using Twilio as an SMS provider. Twilio is suppose to send a POST request to my web service as and when the status of the message is updated (i.e., sent, delivered, etc).
In order to make sure that the POST request is not sent by anyone else than Twilio, I am validating UserAgent of the request as below.
If ((HttpRequest)request.OriginalRequest).UserAgent.StartsWith("TwilioProxy/"))
{
return true;
}
Currently I am getting "TwilioProxy/1.0" as User Agent in each of the POST action, where I believe the version number can be changed in future, so I have skipped it from validation.
Is it possible to receive a request with the same user agent (something starting to "TwilioProxy/") from any other origin than Twilio? Is it safe to rely on UserAgent for this type of verification?
Any inputs/ suggestions on this will be much helpful to me.
Thanks
Twilio developer evangelist here.
As the comments have mentioned, it is trivial to spoof a header and since the UserAgent header for Twilio is very simple, then it is unreliable to rely on it.
However, if you are interested in validating that requests are made by Twilio then you need to check out how we sign requests to ensure they are not malicious.
Here's how it works:
Turn on TLS on your server and configure your Twilio account to use HTTPS urls.
Twilio assembles its request to your application, including the final URL and any POST fields (if the request is a POST).
If your request is a POST, Twilio takes all the POST fields, sorts them by alphabetically by their name, and concatenates the parameter name and value to the end of the URL (with no delimiter).
Twilio takes the resulting string (the full URL with query string and all POST parameters) and signs it using HMAC-SHA1 and your AuthToken as the key.
Twilio sends this signature in an HTTP header called X-Twilio-Signature
Then to verify that this X-Twilio-Signature contains a valid signature, you need to do the following in your application:
Take the full URL of the request URL you specify for your phone number or app, from the protocol (https...) through the end of the query string (everything after the ?).
If the request is a POST, sort all of the POST parameters alphabetically (using Unix-style case-sensitive sorting order).
Iterate through the sorted list of POST parameters, and append the variable name and value (with no delimiters) to the end of the URL string.
Sign the resulting string with HMAC-SHA1 using your AuthToken as the key (remember, your AuthToken's case matters!).
Base64 encode the resulting hash value.
Compare your hash to ours, submitted in the X-Twilio-Signature header. If they match, then you're good to go.
Within our official libraries, we include a request validator that can do all of this for you. There is an example of doing this in C# in the documentation.
Let me know if this helps at all.

MVC HttpGet and HttpPost

Recently I have attended a training in mvc. The trainer said that - As per the security concerns we have to use HttpPost instead of HttpGet. Always use HttpPost.
Can anyone explain - what is the security issue when we use HttpGet?
When transmitting data over secure connection (https) body of the post request is encrypted and practically undreadable, you can only see address where data is going but not the data itself. Get on the other hand has no body and data has to be transmitted in either query string or as a path parameter. While it is true that query string does get encrypted as well, due to request logging on the server and browser it is possible to get hold of that data.
Anyone can insert image on public forum or stackoverflow with link to your web-site. Then happens next:
Browser looks at url in image tag
Browser find cookies corresponding to domain in url
Browser sends request to url with cookies of user
Your server performs action
Browser tries to parse response as image and fails
Browser renders error instead of image
But if you mark your action as Http Post only then this scenario isn't applicable for 90% of sites. But you should also consider that if hacker can create a form on other web-site then he still can make browser to perform request. So you need CSRF. Well, browsers made a lot to prevent cross-site requests, but it's still possible in some scenarios.

Pass data to a URL on a different web server without the QueryString

I've got an .ashx handler which, upon finishing processing will redirect to a success or error page, based on how the processing went. The handler is in my site, but the success or error pages might not be (this is something the user can configure).
Is there any way that I can pass the error details to the error page without putting it in the query string?
I've tried:
Adding a custom header that contains the error details, but since I'm using a Response.Redirect, the headers get cleared
Using Server.Transfer, instead of Response.Redirect, but this will not work for URLs not in my site
I know that I can pass data in the query string, but in some cases the data I need to pass might be too long for the query string. Do I have any other options?
Essentially, no. The only way to pass additional data in a GET request (i.e. a redirect) is to pass it in the query string.
The important thing to realise is that this is not a limitation of WebForms, this is just how HTTP works. If you're redirecting to another page that's outside of your site (and thus don't have the option of cookies/session data), you're going to have to send information directly in the request and that means using a query string.
Things like Server.Transfer and Response.Redirect are just abstractions over a simple HTTP request; no framework feature can defy how HTTP actually works.
You do, of course, have all kinds of options as to what you pass in the query string, but you're going to have to pass something. If you really want to shorten the URL, maybe you can pass an error code and expose an API that will let the receiving page fetch further information:
Store transaction information (or detailed error messages) in a database with an ID.
Pass the ID in the query string.
Expose a web method or similar API to allow the receiving page to request additional information.
There are plenty of hacky ways you could create the illusion of passing data in a redirect outside of a form post (such as returning a page containing a form and Javascript to immediately do a cross-domain form post) but the query string is the proper way of passing data in a GET request, so why try to hack around it?
If you must perform a redirect, you will need to pass some kind of information in the Query String, because that's how browser redirects work. You can be creative about how you pass it, though.
You could pass an error code, and have the consuming system know what various error codes mean.
You could pass a token, and have the consuming system know how to ask your system about the error information for the given token behind-the-scenes.
Also, if you have any flexibility around whether it's actually performing a redirect, you could use an AJAX request in the first place, and send back some kind of JSON object that the browser's javascript could interpret and send via a POST parameter or something like that.
A redirect is executed by most browsers as a GET, which means you'd have to put the data in the query string.
One trick (posted in two other answers) to do a "redirect" as a POST is to turn the response into a form that POSTs itself to the target site:
Response.Clear();
StringBuilder sb = new StringBuilder();
sb.Append("<html>");
sb.AppendFormat(#"<body onload='document.forms[""form""].submit()'>");
sb.AppendFormat("<form name='form' action='{0}' method='post'>",postbackUrl);
<!-- POST values go here -->
sb.AppendFormat("<input type='hidden' name='id' value='{0}'>", id);
sb.Append("</form>");
sb.Append("</body>");
sb.Append("</html>");
Response.Write(sb.ToString());
Response.End();
But I would read the comments on both to understand the limitations.
Basically there are two usual HTTP ways to send some data - GET and POST.
When you redirect to another URL with additional parameters, you make the client browser to send the GET request to the target server. Technically, your server responds to the browser with specific HTTP error code 307 + the URL to go (including the GET parameters).
Alternatively, you may want/need to make a POST request to the target URL. In that case you should respond with a simple HTML form, which consists of several hidden fields pre-filled with certain values. The form's action should point the target URL, method should be "POST", and of course your HTML should include javascript, which automatically submits the form once the document is loaded. This way the client browser would send the POST request instead of the GET one.

Secure WCF REST Webservice and headers

I'm writing a secure WCF REST webservice using C#.
My code is something like this:
public class MyServiceAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
base.CheckAccessCore(operationContext);
var ctx = WebOperationContext.Current;
var apikey = ctx.IncomingRequest.Headers[HttpRequestHeader.Authorization];
var hash = ctx.IncomingRequest.Headers["Hash"];
var datetime = ctx.IncomingRequest.Headers["DateTime"];
...
I use headers (Authorization,Hash,DateTime) to store informations about apikey, current datetime and the hashed request URL while request body contains only URL and webservice parameters.
Example:
http://127.0.0.1:8081/helloto/daniele
Is this the right way or I've to pass and retieve those parameters from URL like this:
http://127.0.0.1:8081/helloto/daniele&apikey=123&datetime=20120101&hash=ddjhgf764653ydhgdhgfjiutu56
are there differences between those two methods?
I think both methods would work for simple cases. However, if you want to make maximum use of native HTTP behaviours, you should go with the headers approach, not the URL query parameters one.
This will allow you to (for example) use HTTP response codes to indicate to client that a resource has been permanently moved (response code 301) so the client can automatically update links. If the URL included the authentication information, it is not clear to a client that two different URLs are actually referring to the same resource. In other redirect scenarios, the headers will be automatically included so you don't have to worry about appending parameters to redirect URLs.
Also, it should allow better caching behaviour on clients (if that is relevant in your scenario).
As another example, using headers would allow you to authenticate a request based just on the headers without requiring the client to send the message body. The idea is that you authenticate with the headers, then send the client an HTTP 100 Continue response. The client should not send the message body until it gets the 100. This could be an important optimisation if you are doing POSTs or PUTs with large message bodies.
There are other examples, but whether any given one is relevant depends on your scenarios and on the clients you expect to serve.
In summary, I would say it is better to make use of elements of the protocol as they were explicitly intended - this gives you the best chance of behaving as a client expects and should make your service more accessible, efficient and usable in the longer term.
Based on your implementation, your required parameters would have to be passed in the HTTP Headers of the request, which would most certainly not be on the query string.

Categories