Hiding user password field from being displayed using CreatedAtRoute - c#

I'm building an ASP.NET Web API and I'm currently implementing very basic user accounts. I have a User model which consists of just Email and Password fields, and I have a UserController class with the following action:
// POST: api/Users
[ResponseType(typeof(User))]
public IHttpActionResult PostUser(User user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Users.Add(user);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = user.Id }, user);
}
Everything works perfectly, except when I POST /api/Users I get the password field sent back in the response body:
{
"Id":2,
"Email":"dummy#test.com",
"Password":"$2a$12$jTACgOlm2eO/OYcV5wrDnO2dmsnbWVnsCRzX1WfQKGsY4sYvh16gm"
}
How do I go about making sure that sensitive fields such as user passwords never get output in a response? I'd prefer a method that does it on the model level so that I never accidentally forget to implement it in a controller.

One option would be to always use a Data Transfer Object (DTO) when communicating between a client and the server. The example given by this article from the Web Api team is very similar to your problem.
I would create a UserDTO which wouldn't contain any sensitive data that I wouldn't want to transfer between my clients and the server.
That is how most APIs work, take Facebook, for example, the User passed via this API call is not the User from their Domain, it is a representation of a User with just the needed information.
Using a DTO you could control exactly what gets transferred, lowering data size and preventing secure information from leaking.
UPDATE: If you go that road you'll probably want to use AutoMapper, it reduces the amount of Object to Object mapping you have to do considerably.

It's too much to just remove some values we need to create a separate model, better to clean the data before returning, say
db.SaveChanges();
user.Password = String.Empty;
user.anyproperty = null;
return CreatedAtRoute("DefaultApi", new { id = user.Id }, user);

John,
The CreatedAtRoute method is intended to return the URI of a newly created resource. For example, if you are creating a new product, you may see the return as api/products/1234. The method will also return the serialized object you provide as a third parameter. The method is not aware of the nature of the object you are returning therefore it does not recognize any of the fields as sensitive.
In your case, you may clear the password field from the user object or return a completely different object that does not include this field. You are not forced to return the same object you just created.
Best Regards,
Daniel

Related

How Can Get Session With SessionId?

I use ASP.NET MVC. I have a problem. I set my variables to the session and I request a web service that doesn't belong to me. Then the web service makes an HttpPost request to my server.
It doesn't send a cookie to my server so I lost my session.
I think I can save my sessionid to the DB and I can get back my session with this ID. But I can't get any solution.
What's your suggestion?
public ActionResult SomeAction(){
mySettingService.saveSessionIdToDb(someToken, Session.SessionID);
var myPaymentFormObj = FormInit.Create(request, options);
myPaymentFormObj.DoRequest(); //it's callback to my another action with a token
}
[HttpPost]
public ActionView MyCallBack(string someToken){
//here is our app generates new session id and i lost my session because other server doesn't send me session id.
//i need to read session id from db and i can get my session maybe.
var mySessionId = mySettingService.getSessionIdFromDb(someToken);
//how can i start session like this?
Session.SessionID = mySessionId;
}
It seems like the problem you described is about maintaining the distributed transaction.
To describe it better your app is a service A and the webServer is service B.
You can perform an action which saves some changes to the database A including the session stuff then you send a call to service B which also can saves some changes to its DB or perform a bunch of other actions but in this case you don't care how it works, you only care about what kind of responses you get back with a callback. There should be an option to be able to send some kind of unique thing like userEmail or a transactionId which you can get back in a callback method to be able to recognize the transaction.
What I would suggest you do is something like
[HttpPost]
public ActionResult SendBlah(BlahData data){
var transactionId = Guid.NetGuid();
_sessionService.Create(transactionId, dataYouWantToStore)
_webServiceB.SendBlah(transactionId, data, token);
//optionally return info about call status / do other stuff
}
//or this can be of type HttpGet
[HttpPost]
public ActionView MyCallBack(string someToken, string tranactionId){
var sessionData = _sessionService.Get(tranactionId)
//do other stuff
}
If it's needed and you are using e.g. JWT you can store the transactionId/emailAddress/etc. there instead and read it.
Btw. it's always safer to store the session in the database instead of using some cache objects or relaying on cookies or javascript objects etc.
Also, it's better to be careful with the amount of data you want to store in a Session table in your db. I'd personally focus on storing the Ids and stuff like Status of given item etc.

Clarifications and peer review regarding authentication and roles of my web application

I am trying to learn basic security and access limitations on ASP MVC.
So far, i have read/watched tutorials but all of them seems different from one another. If i will search something, it will lead me to another implementation which is totally different from what i have.
I implemented Authentication and custom role provider and i have some questions regarding how things work. Majority of explanations that i found from the internet seems overly complicated or outdated.
This is how i implemented my authentication.
login controller:
[HttpGet]
[ActionName("login")]
public ActionResult login_load()
{
return View();
}
[HttpPost]
[ActionName("login")]
public ActionResult login_post(string uname,string pword)
{
using (EmployeeContext emp = new EmployeeContext())
{
int success = emp.login.Where(x => x.username == uname && x.password == pword).Count();
if (success == 1)
{
FormsAuthentication.SetAuthCookie(uname, false);
return RedirectToAction("Details", "Enrollment");
}
return View();
}
}
Then i protected most of my controllers with [Authorize]
Question #1
What's the purpose of FormsAuthentication.SetAuthCookie(uname, false); and what should i typicalfly use it for? would it be alright to store the username. Do i need it for comparison later on?(further security?). It says here that Authentication ticket will be given to the username. Are those the ones with random letters?
--
After that, i decided to dive deeper and implemented a custom role provider
from roleprovider.cs(I only implemented 2 methods so far)
public override string[] GetRolesForUser(string username)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
return null;
}
var cacheKey = username;
if (HttpRuntime.Cache[cacheKey] != null)
{
return (string[])HttpRuntime.Cache[cacheKey];
}
string[] roles = new string[] { };
using (MvcApplication6.Models.EmployeeContext emp = new MvcApplication6.Models.EmployeeContext())
{
roles = (from a in emp.login
join b in emp.roles on a.role equals b.id
where a.username.Equals(username)
select b.role).ToArray<string>();
if (roles.Count() > 0)
{
HttpRuntime.Cache.Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration);
}
}
return roles;
}
Question #2
I am kinda confused here and i need a deep clarification: so what is basically the purpose of the cacheKey and from my example, i just made it equal to uname since i have no idea what's going on.
Question #3
Why is it returned (string[])HttpRuntime.Cache[cacheKey]; if the value is null? when is it returned and who is receiving it?
Question #4
After getting the value the list of roles from the database, this function will be called HttpRuntime.Cache.Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration);. So from what i see, the roles are being inserted into the cache? is it for checking the login type later on?
Question #5
from this lines of code:
public override bool IsUserInRole(string uname, string roleName)
{
var userRoles = GetRolesForUser(uname);
return userRoles.Contains(roleName);
}
When are they exactly triggered and who provides the parameters? is the roleName from the cache?
I am having a hard time visualizing what's happening under the hood. Explanations/Referrals will be very helpful.
What's the purpose of FormsAuthentication.SetAuthCookie()?
This is ASP.NET FormsAuthentication's built-in method for dealing with authentication cookies.
How does cookie based authentication work?
Explained: Forms Authentication in ASP.NET 2.0
Basically, it's doing the hard work for you; creating a cookie for a specific user, giving it to them and then using it to recognise the same user in the future. You want to use this function to log a user in (if they enter correct credentials).
The string parameter is for a username. Yes, you can use username.
The bool parameter is for if you want the cookie to be persistent. That is, keep them logged in even if they close the browser (whether or not to use a session).
By using FormsAuthentication in this way, ASP.NET will automatically detect the user again when they visit subsequent pages.
What is basically the purpose of the cacheKey?
The Cache component of the HttpRuntime is for managing a "box" of objects that you might retrieve frequently but don't want to be hitting the database all the time for.
The Cache is implemented as a kind of Key-Value Pair. The cacheKey in your example is a key in the Key-Value collection. You can think of it like other similar data structures used in other languages.
{
"carlobrew": {
"roles": {
"Name": "Administrator"
}
}
}
So you're basically "saving" the roles of the user carlobrew in a container so that you can get them again later. The key in a Key-Value Pair is used to refer back to the data that you put in there. The key you are using to refer back to the saved information is the uname; that is, the username.
The key in Key-Value Pairs is unique, so you cannot have two keys called carlobrew.
Why is it returned (string[])HttpRuntime.Cache[cacheKey]; if the value is null?
There are two steps to using a typical "cache box" like this.
If we find the key (such as the user carlobrew) then we can simply return the data straight away. It's not if the value is null. It's if the value is not null. That's why the code is if (HttpRuntime.Cache[cacheKey] != null).
If the key cannot be found (that is, we don't have the key for carlobrew), well then we have to add it ourselves, and then return it.
Since it's a cache, ASP.NET MVC will automatically delete things from the cache when the timer expires. That's why you need to check to see if the data is null, and re-create it if it is.
The "who is receiving it" is whichever object is responsible for calling the GetRolesForUser() method in the first place.
So from what i see, the roles are being inserted into the cache?
Yes.
Basically, if the data isn't in the cache, we need to grab it from the database and put it in there ourselves, so we can easily get it back if we call the same method soon.
Let's break it down. We have:
Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration);
Insert is the method. We're calling this.
cacheKey is the key part of the Key-Value Pair. The username.
roles is the object that we want to store in cache. The object can be anything we want.
DateTime.Now.AddMinutes(_cacheTimeoutInMinute) is telling ASP.NET MVC when we want this data to expire. It can be any amount of time that we want. I'm not sure what the variable _cacheTimeoutInMinute maybe it's 5 or 15 minutes.
Cache.NoSlidingExpiration is a special flag. We're telling ASP.NET that, when we access this data, don't reset the expiration timer back to its full. For example, if our timer was 15 mins, and the timer was about to expire with 1 minute to go, if we were using a sliding expiration and tried to access the data, the timer would reset back to 15 minutes and not expire the data.
Not sure what you mean by "is it for checking the login type later on". But no, there isn't any checking of login type here.
IsUserInRole
You would probably call this when the user is trying to do something. For example, if the user goes to /Admin/Index page, then you could check to see if the user is in the Administrator role. If they aren't, you'd return a 401 Unauthorized response and tell you the user they aren't allowed to access that page.
public Controller Admin
{
public ActionResult Index()
{
if (!IsUserInRole("Administrator"))
{
// redirect "not allowed"
}
return View();
}
}

Inconsistent display names of user account in ASP.NET MVC4 SimpleMembership

I am building a ASP.NET MVC4 Internet website using Visual Studio 2012.
VS2012 generated a template website by default, with SimpleMembership implemented.
The SimpleMembership feature is quite convinient, expect that there is one thing very confusing to me:
For example, I create a user account, with the user name say "Miles", and then login using the name "Miles", everthing is fine. Then I logout and login using the name "miles"(all lower case), the login is also sucessful, however, the user name reads "miles". To be more specific, the value of User.Identity.Name is "miles", instead of "Miles" in the database.
Likewise, I can use "miLes", "mILes", "MILES", etc. to login, and the user name will be the same. The common sense is that if the authentication is case-insensitve, the user name should be exactly the same as the one in database, in my case "Miles", not as what I type in the login textbox.
Does anyone know how to solve this problem? Thanks!
Reason
In your AccountController you probablay see something like this by default:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
WebSecurity.Login() is called (passing in the exact string on the model that you typed in your form).
If you look at the source code of WebSecurity.cs you'll see that FormsAuthentication.SetAuthCookie() is called (again, still passing the exact typed string). Before this methods actually sets the cookie in the response, it executes GetAuthCookie(), still passing in the exact string you typed when logging in.
This method actually builds out the authentication HttpCookie object once again using the data you typed in.
Solution
If you need the name to match exactly what's in your database, then you need to query for the record from the database and pass that value into WebSecurity.Login().

How to persist keeping the info in the Session["Stuff"]?

When the user makes selection and clicks a button, I call to:
public ActionResult Storage(String data)
{
Session["Stuff"] = data;
return null;
}
Then, I redirect them to another page where the data is accessed by
#Session["Stuff"]
This far, I'm happy. What I do next is that upon a click on a button on the new page, I perform a call to:
public ActionResult Pdfy()
{
Client client = new Client();
byte[] pdf = client.GetPdf("http://localhost:1234/Controller/SecondPage");
client.Close();
return File(pdf, "application/pdf", "File.pdf");
}
Please note that the PDFization itself works perfectly well. The problem is that when I access the second page a second time (it's beeing seen by the user and looks great both in original and on reload), it turns out that Session["Stuff"] suddenly is null!
Have I started a new session by the recall?
How do I persistently retain data stored in Session["Stuff"] before?
If you're simply storing string data (as would be indicated by your method signature) in an MVC application, don't.
It's far easier to pass the data as a query parameter to each method that needs it. It's far easier to manage and doesn't rely on Session sticky-ness.
To generate the appropriate links, you can pass data to your views and use Html.ActionLink to generate your links with the appropriate parameter data.
Here's several reasons why the session variable could return null:
null is passed into Storage
Some other code sets Session["Stuff"] to null
The session times out
Something calls Session.Clear() (or Session.Abandon())
The underlying AppPool is restarted on the server
Your web server is farmed and session state is not distributed properly
The first two can be discovered by debugging.

Pass data to another view

If the user login is valid, i will redirect the view to another view as shown below. The following method is in a Controller class.
if (loginSuccess(email,pwd))
{
FormsAuthentication.SetAuthCookie(email, false);
return RedirectToAction("Index", "SuccessPage");
}
Now i want to display the username on the redirected view (which is /SuccessPage/Index). How can i do this ?
You can pass parameters in the redirect.
if (loginSuccess(email,pwd))
{
string name = User.Identity.Name;
return RedirectToAction("Index", "SuccessPage"), new {username = name };
}
(Obviously, the controller action needs to have access to the username attribute before it can pass the value)
Note: The example above is useful for understanding how to pass additional parameters in a redirect, but in this case it's overkill since User.Identity.Name is available in the session once a user has authenticated and you can access it from any controller or view, so no need to pass (see Josh's response).
When a user is authenticated, you can use the HttpContext to get basic user information. For example, you can use the following to get the username:
string userName = HttpContext.User.Identity.Name;
You don't need to worry about passing the information between controllers. The information will be available as long as the user is autheticated.
In the SuccessPage.Index action, get the user's name and pass it into the view via the model.
To get the user's name, you either get it from a saved Session variable, or you can use controller.HttpContext.User.Identity.Name.
You have 4 options if we're just discussing passing data. (If we're talking about just the username then you want Josh's response)
Passing parameters in the redirect (as Jack suggested)
return RedirectToAction("Index", "SuccessPage"), new {username = name };
Storing it in temp data (only works for 1 request)
TempData[Key] = name;
return RedirectToAction("Index", "SuccessPage");
Storing it in session (lasts as long as the session lasts)
Session[Key] = name;
return RedirectToAction("Index", "SuccessPage");
Storing it in the database and linking that data via their session id.
/* databasey code here */
return RedirectToAction("Index", "SuccessPage");
That's your full set of options from simplest to most complex. I'd suggest in your case you just pass the values in the URL (first one) as your system expands and grows you may want to consider trying out the other options.
It's worth noting that TempData doesn't last across a page refresh.

Categories