Header Manipulation Cookies in asp.net - c#

Fortify tool is showing the Header manipulation cookies issue on the below code.
HttpCookie cookieexit= new HttpCookie("CheckUserNameExists");
cookieexit.Value = userName;
cookieexit.Secure = true;
Response.Cookies.Add(cookieexit);
Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, false));
Any help ?

It is not directly related to original question.
If you use FormAuthentication, it is not a good practice to create custom cookie name to keep track of logged-in username.
Instead, you want to use same cookie name FormsAuthentication.FormsCookieName that FormsAuthentication uses.
So, you replace 5 lines of codes with the following single line -
FormsAuthentication.RedirectFromLoginPage(userName, false);
Global.asax.cs
You then retrieve the username from cookie, and save it in both Principal Object and Current Thread.
public class Global : HttpApplication
{
private void Application_AuthenticateRequest(object sender, EventArgs e)
{
HttpCookie decryptedCookie =
Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt(decryptedCookie.Value);
var identity = new GenericIdentity(ticket.Name);
var principal = new GenericPrincipal(identity, null);
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = HttpContext.Current.User;
}
}
You can then retrieve username literally anywhere in your code as long as HttpContext.Current is available. For example,
string userName = User.Identity.Name;

Fortify is complaining because it thinks that userName could have dangerous characters, such as newlines, which could (maybe) allow header manipulation attacks in the scenario of social engineering/phishing attacks.
I'm not too sure of the practicality of such attacks, but I am pretty sure that if you validate that userName only contains a whitelisted set of characters that exclude any whitespace, then such an attack is prevented.

Related

How can I retrieve a user from a password reset token in ASP.NET Core?

I have an ASP.NET Core 2.1 web application and am adding forgot password functionality. I have looked at several examples, and they seem to take one of two approaches. The first approach is to include either the user id or the user's email in the password reset url along with the password reset token. The second approach is to include only the password reset token in the password reset url and then require the user to enter identifying information (such as email) when attempting to change the password (Binary Intellect example). Is there a way to look up the user given just the password reset token?
My team lead has asked me to pass just the token in the password reset url and then look up the user. My initial research makes me believe that I would have to manually keep record of the user id and token relationship, but am hoping that there's something built in. I have reviewed the ASP.NET Core UserManager documentation, but did not find any methods for retrieving a user for a given token.
Here's some of the example code embedding the user id in the password reset URL (Microsoft Password Recovery Doc):
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
There is a way to get the UserId from the password reset token, but in my opinion it's tricky and a lot of work.
What are the defaults
If you have some codes like the following,
services.AddIdentity<AppUser, AppRole>(options =>
{
...
}
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
the last line .AddDefaultTokenProviders() adds 4 default token providers, which are used to generate tokens for reset passwords, change email and change phone number options, and for two factor authentication token generation, into the pipeline:
DataProtectorTokenProvider
PhoneNumberTokenProvider
EmailTokenProvider
AuthenticatorTokenProvider
The first one, DataProtectorTokenProvider, is what we're looking for. It uses data protection to serialize/encrypt those tokens.
And within the DataProtectorTokenProvider, its protector is default to the name of "DataProtectorTokenProvider".
How tokens are generated
If you look at GenerateAsync() method inside DataProtectorTokenProvider, you can kind of tell the token consists of:
Utc timestamp of the token creation (DateTimeOffset.UtcNow)
userId
Purpose string
Security stamp, if supported
The generate method concatenates all those, transform them to a byte array, and calls the protector inside to protect/encrypt the payload. Finally the payload is converted to a base 64 string.
How to get User Id
To get the userId from a token, you need to do the reverse engineering:
Convert the token from base 64 string back to the byte array
Call the protector inside to unprotect/decrypt the byte array
Read off the Utc timestamp
Read userId
The tricky part here is how to get the same DataProtector used to generate those token!
How to get the default Data Protector
Since the default DataProtectorTokenProvider is DIed into the pipeline, the only way I can think of to get the same DataProtector is to use the default DataProtectorTokenProvider to create a protector with the same default name, "DataProtectorTokenProvider", used to generate tokens!
public class GetResetPasswordViewModelHandler : IRequestHandler<...>
{
...
private readonly IDataProtector _dataProtector;
public GetResetPasswordViewModelHandler(...,
IDataProtectionProvider dataProtectionProvider)
{
...
_dataProtector = dataProtectionProvider.CreateProtector("DataProtectorTokenProvider");
// OR
// dataProtectionProvider.CreateProtector(new DataProtectionTokenProviderOptions().Name);
}
public async Task<ResetPasswordViewModel> Handle(GetResetPasswordViewModel query, ...)
{
// The password reset token comes from query.ResetToken
var resetTokenArray = Convert.FromBase64String(query.ResetToken);
var unprotectedResetTokenArray = _dataProtector.Unprotect(resetTokenArray);
using (var ms = new MemoryStream(unprotectedResetTokenArray))
{
using (var reader = new BinaryReader(ms))
{
// Read off the creation UTC timestamp
reader.ReadInt64();
// Then you can read the userId!
var userId = reader.ReadString();
...
}
}
...
}
}
Screenshot:
My 2 cents
It seems like it's a lot of work just try to read the userId off a password reset token. I understand your team lead probably doesn't want to expose the user id on the password reset link, or (s)he thinks it's redundant since the reset token has the userId.
If you're using integer to represent the userId and don't want to expose that to public, I would change it to GUID.
If you have to use integer as your userId, I would just create a column of the type unique_identifier off the user profile (I would call it PublicToken) and use that to identifier a user for all public matters.
var callbackUrl = Url.Action("resetPassword", "account", new
{
area = "",
rt = passwordResetToken, // reset token
ut = appUser.Id // user token, use GUID user id or appUser.PublicToken
}, protocol: Request.Scheme);
I believe there is no way you can do that you can pass user email then find it look for user in your code
public async Task<IActionResult> ResetPassword([FromBody]ResetPasswordViewModel model)
{
if (string.IsNullOrEmpty(model.Token) || string.IsNullOrEmpty(model.Email))
{
return RedirectToAction("Index", "Error", new { statusCode = AppStatusCode.NotFound });
}
var isResetTokenValid = await _userManager.CheckValidResetPasswordToken(model.Token, model.Email);
if (!isResetTokenValid || string.IsNullOrEmpty(model.Email))
{
return StatusCode(AppStatusCode.ResetPassTokenExpire);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
return Ok();
}
await _userManager.ResetPasswordAsync(user, model.Token, model.Password);
return Ok();
}
You can view the implementaion detail here
What I do in this case is I keep that new token in a cache or sql table with user id in it. That way you first query that table containing reset token, validate it if you need it and get user.

Why does the authorize attribute fail to authorize the action with valid user being logged in?

I am using authorize attribute over an action.
[Authorize(Users= "admin" )]
[HttpGet]
public JsonResult GetServices()
{
return Json(ServicesRepository.SelectServices(), JsonRequestBehavior.AllowGet);
}
While successfully logging in I am setting:
Session["Users"] = usersModels;
Session["UHTUserName"] = usersModels.UserName;
FormsAuthentication.SetAuthCookie(usersModels.UserName, LoginVM.RememberMe);
AuthorizeAttribute aattr = new AuthorizeAttribute();
aattr.Users = usersModels.UserName;
but still, it fails to authorize.
Based on the above code snippet, you are using form authentication with MVC.
When Forms authentication is being used, whenever the need for authentication arises, the ASP.NET framework checks with the current IPrinciple type object. The user ID and Role contained in this IPrinciple type object will determine whether the user is allowed access or not.
So far you have not written code to push your user's Role details in this principle object. To do that you need to override a method called FormsAuthentication_OnAuthenticate in global.asax. This method is called each time ASP.NET framework tries to check authentication and authorization with respect to the current Principle.
What you need to do now is to override this method. Check for the authentication ticket (since the user has already been validated and the ticket was created) and then supply this User/Role information in the IPrinciple type object. To keep it simple, you will simply create a GenericPriciple object and set user specific details into it, as follows:
protected void FormsAuthentication_OnAuthenticate(Object sender, FormsAuthenticationEventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//let us take out the username now
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string roles = string.Empty;
using (userDbEntities entities = new userDbEntities())
{
User user = entities.Users.SingleOrDefault(u => u.username == username);
roles = user.Roles;
}
//let us extract the roles from our own custom cookie
//Let us set the Pricipal with our user specific details
e.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles.Split(';'));
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Note: In MVC 4 and later versions, this event will not work. To make the custom forms authentication work in MVC 4 and later versions, we need to put this code in Application_PostAuthenticateRequest event in the Global.asax file.
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//let us take out the username now
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string roles = string.Empty;
using (userDbEntities entities = new userDbEntities())
{
User user = entities.Users.SingleOrDefault(u => u.username == username);
roles = user.Roles;
}
//let us extract the roles from our own custom cookie
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles.Split(';'));
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Reference: https://www.codeproject.com/Articles/578374/AplusBeginner-splusTutorialplusonplusCustomplusF
Did you set the settings in web.config for Forms Authentication
<system.web>
<authentication mode="Forms"></authentication>
<system.web>
And while login set the cookie as follows
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, //version
UserName, // user name
DateTime.Now, // create time
expiration, // expire time
RememberMe, // persistent
strUserData); // user data/role
HttpCookie objHttpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
objHttpCookie.Path = FormsAuthentication.FormsCookiePath;
Response.Cookies.Add(objHttpCookie);

How to get recently authenticated user?

I am working with MVC 3 and I have just implemented a wrapper for the FormsAuthenticationService.
Something similar to the following.
public void SignIn(string username, bool createPersistantCookie)
{
if (string.IsNullOrEmpty(username))
throw new ArgumentException("Value Cannot be null or empty", "username");
FormsAuthentication.SetAuthCookie(username, createPersistantCookie);
}
Reluctantly, I have gotten this to work, but now I am not quite sure how to get the information that I have stored.
Once the user is in my system, how can I now safely retrieve this information if I need to grab their UserID out of the database?
Based on the additional information provided, you want to store additional data with the FormsAuthentication ticket. To do so, you need first create a custom FormsAuthentication ticket:
Storing Data
Grab the current HttpContext (not worrying about testability)
var httpContext = HttpContext.Current;
Determine when the ticket should expire:
var expires = isPersistent
? DateTime.Now.Add(FormsAuthentication.Timeout)
: NoPersistenceExpiryDate; // NoPersistenceExpiryDate = DateTime.MinValue
Create a new FormsAuthentication ticket to hold your custom data.
var authenticationTicket = new FormsAuthenticationTicket(
1,
username,
DateTime.Now,
DateTime.Now.Add(FormsAuthentication.Timeout),
isPersistent,
"My Custom Data String"); //Limit to about 1200 bytes max
Create your HTTP cookie
new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(authenticationTicket))
{
Path = FormsAuthentication.FormsCookiePath,
Domain = FormsAuthentication.CookieDomain,
Secure = FormsAuthentication.RequireSSL,
Expires = expires,
HttpOnly = true
};
And finally add to the response
httpContext.Response.Cookies.Add(cookie);
Retrieving Data
Then you can retrieve your data on subsequent requests by parsing the stored authentication ticket...
Again, grab current HttpContext
var httpContext = HttpContext.Current
Check to see if the request has been authenticated (call in Application_AuthenticateRequest or OnAuthorize)
if (!httpContext.Request.IsAuthenticated)
return false;
Check to see if you have a FormsAuthentication ticket available and that it has not expired:
var formsCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (formsCookie == null)
return false;
Retrieve the FormsAuthentication ticket:
var authenticationTicket = FormsAuthentication.Decrypt(formsCookie.Value);
if (authenticationTicket.Expired)
return false;
And finally retrieve your data:
var data = authenticationTicket.UserData;
You haven't actually stored a user id in the database. All the code that you've written does is store an authentication cookie on the users computer, either as a session cookie (not persistent) or as a persistent one.
When your page refreshes, it will get the cookie automatically, decode it, and populate the IPrincipal object which you access from the User.Current property of your controller.

Unable to logout after specifying "domain" parameter in "authentication" of web.config

I have logout handler which used to work fine:
public void ProcessRequest(HttpContext context)
{
//// Sign out
System.Web.Security.FormsAuthentication.SignOut();
//// Clear Session
if (context.Session != null)
{
context.Session.Clear();
}
/// Expire all the cookies so browser visits us as a brand new user
List<string> cookiesToClear = new List<string>();
foreach (string cookieName in context.Request.Cookies)
{
HttpCookie cookie = context.Request.Cookies[cookieName];
cookiesToClear.Add(cookie.Name);
}
foreach (string name in cookiesToClear)
{
HttpCookie cookie = new HttpCookie(name, string.Empty);
cookie.Expires = DateTime.Today.AddYears(-1);
context.Response.Cookies.Set(cookie);
}
context.Response.Redirect("~/default.aspx");
}
}
Once I added "domain" parameter to the authentication section of web.config:
<forms timeout="50000000"
loginUrl="~/login"
domain='mysite.com'/>
... it is no longer logging the user out - after it redirects to "~/default.aspx" I can still see the user logged in (I put a breakpoint to Load event of that page and check HttpContext.Current.User.Identity.IsAuthenticated, and its still = true).
Then I remove "domain='mysite.com'" and it logs the user out without problems.
I do need to specify the domain because I added a subdomain with its own application but I want it to share authentication cookie.
Any ideas are highly appreciated!
When I recreate cookies to expire, I need to specify the domain:
cookie.Domain = FormsAuthentication.CookieDomain;
That solves the problem.
Please specify domain =".mysite.com"

Membership provider class

I want to know how I can implement membership provider class to have ability to remember users who signed in.
I have Membership provider class and I need functionality of "Remember Me" checkbox but I don't know how I can implement some methods
In order to implement this functionality you must create a persistent cookie with some expiration date on the users computer. So if the user checks the Remember me checkbox you issue the following cookie:
var cookie = new HttpCookie("_some_cookie_name_", "username")
{
Expires = DateTime.Now.AddDays(15) // Remember user for 15 days
};
Response.Cookies.Add(cookie);
And then upon showing the login screen you could check if the cookie is present and prefill the username:
var cookie = Request.Cookies["_some_cookie_name_"];
if (cookie != null)
{
usernameTextBox.Text = cookie.Value;
}
I would use a Hashtable if it's in C#, keyed by the user id. Something like this (where lsdfjk is just whatever string the user ID corresponds to, and assuming that there is a class UserInfo defined, with a constructor taking string userID as an argument):
string userID = "lsdfjk";
UserInfo userInfo = null;
Hashtable htMembers = new Hashtable();
if (htMembers.ContainsKey(userID))
{
userInfo = (UserInfo)htMembers[userID];
}
else
{
//It's a new member
userInfo = new UserInfo(userID);
}
"Remember Me" doesn't have anything to do with a Membership Provider really. Basically it is just a function of Forms Authentication, where you set a persistent cookie so that when people show up at the website, it can log them in automatically.
You can do this automatically using the RedirectFromLoginPage() method.
FormsAuthentication.RedirectFromLoginPage(username, true);
The second parameter, "true", means "set a persistent cookie". The user will be logged in until the cookie expires or they clear their cookies.
If you need more control over it, you can manually set a cookie by manipulating the cookies collection directly.

Categories