I am using database with a list of username/passwords, and a simple web form that allows for users to enter their username/password.
When they submit the page, I simply do a stored procedure check to authenticate. If they are authorised, then their user details (e.g. username, dob, address, company address, other important info) are stored in a custom User object and then in a session. This custom User object that I created is used throughout the web application, and also in a sub-site (session sharing).
My question/problems are:
Is my method of authentication the correct way to do things?
I find users complaining that their session have expired although they "were not idle", possibly due the app pool recycling? They type large amounts of text and find that their session had expired and thus lose all the text typed in. I am uncertain whether the session does really reset sporadically but will Forms Authentication using cookies/cookiless resolve the issue?
Alternatively should I build and store the User Object in a session, cookie or something else instead in order to be more "correct" and avoid cases like in point #2.
If I go down the Forms Authentication route, I believe I cannot store my custom User object in a Forms Authentication cookie so does it mean I would store the UserID and then recreate the user object on every page? Would this not be a huge increase on the server load?
Advice and answers much appreciated.
L
It doesn't really care whether you use your own authentication system, or the default membership providers when using such a simple scenario.
You should avoid using the InProc session state when the app might recycle some times a day. Rather store your session into a database (SqlSessionState) or use a StateServer. Then the application pool can recycle all day without interferring with your sessions. Setting the session timeout to 60 minutes or something, will solve the remaining issues. Never use cookieless sessions (unless you know what you're doing), as they make it way too easy to steal one's session.
Just store it into the session (or profile if you use the default membership provider). Not only is a cookie easily readible, it is also limited to 4 KB.
No, you will have a profile where all the user information is stored. It doesn't really matter whether you use forms authentication or a custom system that stores it's data into SqlSessionState. The membership provider will store the Profile ID into a cookie, same as the session state will save the Session ID into a cookie.
You can use ASP.NET Membership, Roles, Forms Authentication, and Security Resources
I will give an example using c#
For reference Forms Authentication in ASP.NET 2.0
//code for checking user name & password
protected void btnlogin_Click(object sender, EventArgs e)
{
try
{
if (txtUserName.Text.Trim() != "" && txtPassword.Text.Trim() != "")
{
User obj = objUser.UserAuthenticate(txtUserName.Text.Trim(), txtPassword.Text.Trim());
if (obj != null)
{
//To set AuthenticationCookie of usertype "User"
SetAuthenticationCookie("User", obj.UserID.ToString(), obj.DisplayName);
HttpCookie usercookie = new HttpCookie("LoginTime");
usercookie.Value = DateTime.Now.ToString();
Response.Cookies.Add(usercookie);
HttpCookie namecookie = new HttpCookie("LoginName");
namecookie.Value = obj.DisplayName;
Response.Cookies.Add(namecookie);
}
else
{
lblMsg.Text = "Invalid Username or Password.";
}
}
else
{
//lblMsg.Visible = true;
}
}
catch (Exception ex)
{
//lblMsg.Visible = true;
throw ex;
}
}
private void SetAuthenticationCookie(string role, string userid, string name)
{
string userdata = "logintype=user|role=" + role + "|email=" + txtUserName.Text.Trim() + "|name=" + name;
FormsAuthenticationTicket faTicket = new FormsAuthenticationTicket(1, userid,
DateTime.Now, DateTime.Now.AddHours(+1),
false, userdata);
HttpCookie authCookie = new HttpCookie( FormsAuthentication.FormsCookieName,//"martinfernandez#ispg.in",
FormsAuthentication.Encrypt(faTicket));
authCookie.Expires = faTicket.Expiration;
Response.Cookies.Add(authCookie);
}
//code inside global.asax.cs
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.User != null && Context.User.Identity is FormsIdentity && Context.User.Identity.IsAuthenticated)
{
FormsAuthenticationTicket faTicket = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value);
string[] userdata = faTicket.UserData.Split("|".ToCharArray());
string logintype = "";
string email = "";
string uname = "";
string roleString = "";
foreach (string s in userdata)
{
string keyname = s.Split("=".ToCharArray())[0];
if (keyname == "logintype")
{
logintype = s.Split("=".ToCharArray())[1];
}
else if (keyname == "role")
{
roleString = s.Split("=".ToCharArray())[1];
}
}
string[] rolesArray = roleString.Split(";".ToCharArray());
Context.User = new System.Security.Principal.GenericPrincipal(new FormsIdentity(faTicket), rolesArray);
}
}
Just to touch on point #4 - it would be more efficient memory-wise to not store everyone's "User" object in memory, and re-create the object each HTTP request. This way you also re-verify the login details-- what if someone's account is compromised and the actual user changes their password to try and protect their account, but the "bad user" has already logged in? Under your security mechanism, the "bad user" can keep browsing away since the user data is cached and not re-verified each postback.
Here are the some general security measures that beginner and Advance Web Developer must follow.
#15 Steps To Secure Your Website
1 : Prevent Image Hotlinking (IMP)
Image hotlinking is the process of using someone else’s Image URL in our Website and using their bandwidth. In order to prevent this mystery, we can prevent access for external server by adding following line in code.
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yourdomain.com [NC]
RewriteRule \.(jpg|jpeg|png|gif)$ - [NC,F,L]
2 : Prevent CSRF (Cross Site Request Forgery) Attacks
In order to prevent CSRF attacks on your GET and POST requests for form submission, you can use following 2 techniques.
The first it to include a random token with each request, this is a unique string that is generated for each session.
The second method is to use random name for each form field.
3 : Prevent Directory Access / Disable Indexing
Add the following line to your .htaccess file.
Options -Indexes
4 : Secure your Private and CMS Login with IP restrictions
IP restriction is a bit way advanced yet effective method to stop the unauthorized personnel to access a particular area of your website.
Here is an example htaccess code to IP restrict the access to a particular location.
ALLOW USER BY IP
<Limit GET POST>
order deny,allow
deny from all
allow from 1.2.3.4
</Limit>
5 : Protect your .htaccess file
You can write this below piece of code in your htaccess file which do not let any others access your htaccess file.
<Files ~ “^.*.([Hh][Tt][Aa])”>
order allow,deny
deny from all
satisfy all
</Files>
6 : Function access rule
By adding "_" as a prefix for Function name, we can prevent function to be called from The Web publicly. This is the best practice when we need some specific function to be accessed via AJAX only.
7 : Lock down your directory and file permissions
File permissions define who can do what to a file.
"Read" = 4 : View the file contents.
"Write" = 2 : Change the file contents.
"Execute" = 1 : Run the program file or script.
8 : Prevent Cron Job to be run from Web Browser
By adding, following line of code in your page, you can protect your page to be accessed from a web browser.
if( ! $this->input->is_cli_request() ) {
die("Only CLI Requests Allowed");
}
9 : Hide admin pages to be crawled by Google
You do not want your admin pages to be indexed by search engines, so you should use the robots_txt file to discourage search engines from listing them.
10 : Disable Right Click on Page if not require
Disabling “right-click” as a way to view your website source code by inspect element to secure Website content for general users.
11 : Use Strong Password for CMS
Keep practice to set random Password with the special character only.
12 : Make Admin Directory Tough to guess
It may happen that Hackers can use scripts that scan all the directories on your web server for giveaway names like ‘admin’ or ‘login’ etc. and your significant stuff may get leaked.
13 : Change your database table prefix
Add a prefix (the mixture of project name and year) which would be hard to presume for a secured side.
To illustrate,
A) BPM Supreme => bpm14_download
B) Glickin => gk15_admin
C) TravelWorthy => tw16_user
14 : Prevent User's Password, that's as important as yours
Regarding Password Encryption algorithm, Use sha1 algorithm instead of tradition algorithm Md5 which is the very old way and becoming less secure nowadays as per sources.
Reference : http://php.net/manual/en/function.sha1.php
15 : Hide Error Log
During development Mode, keep error reporting "ALL" and once we go LIVE change it to "0" without forgetting. Over here
Reference : http://php.net/manual/en/function.error-reporting.php
My advice would be to use asp.net membership and roles (Microsoft). It is a very good security solution - login security, roles (permissions) and is stored in a SQLServer database (not sure if it can be stored elsewhere).
I use it on my site and you can use membership controls straight out of the box (login forms, change password, etc.) or you can roll your own.
The only tricky bit I found was setting up the membership tables, views and stored procs in my dB (you download a dB script), but really it was fairly straightforward to implement.
Here's a link to asp.net membership and roles
Related
I have an asp.net web application with forms authentication and users (credentials) are checked against active directory, username is actually samAccountName attribute from AD.
Now I need to enable users to get access to some files which are located on file share, where each user has his own folder.
First proof of concept works like this:
appPool in IIS is configured to run under some domain user, and this user was given R/W access to file share and all user folders
when the user logs into web app only content of the folder on the path "\\myFileServer\username" is visible to him. And same when uploading files they get stored to "\\myFileServer\username".
While this works, doesn't seem to be secure at all. First issue is that user under which application pool runs has access to folders from all users. And even bigger concern is that only username determines to which folder you have access.
So my question is what is the correct/better way to doing this ? I was reading about impersonating the user, but this is not advised anymore if I understood correctly ? And I don't have Windows authentications since the web application must be accessible from internet.
I recommend not running the application under a user account, but creating an application specific account under which it runs with the proper R/W rights, and separate the person who gives these rights from the development team.
Within the application's authentication: after you receive a GET/POST request, you can verify the path to which the current user would read/write data, and cross-reference this with the path the user is authorized to read/write from. If these are incorrect, return a 401 NOT AUTHORIZED response, else, carry on the operation as you do now.
If your endpoints are protected properly, and the application runs under its own account, I don't see any harm in the setup itself. This still however gives the developers a way, through the application, to indirectly access other user's files. Based on how tight these checks must be, you could add additional controls, (like only allowing the application to connect from the production server, and only allowing server transport in a controlled way).
From the Description of your Problem i think Custom HttpHandlers are the right choice for you. You didn't mention what type of files will be present in your Folder , for brevity i will answer by assuming it will be having PDF files.
As you were mentioning that your application will be having different users so for this you need to use .NET built-in authentication manager and role provider. With a simple security framework setup, we'll place a PDF file in the web application, behind a web.config protected folder.then create a custom HTTP handler to restrict access on the static document to only those users who should be allowed to view it.
A sample HTTP Handler:
public class FileProtectionHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
switch (context.Request.HttpMethod)
{
case "GET":
{
// Is the user logged-in?
if (!context.User.Identity.IsAuthenticated)
{
FormsAuthentication.RedirectToLoginPage();
return;
}
string requestedFile =
context.Server.MapPath(context.Request.FilePath);
// Verify the user has access to the User role.
if (context.User.IsInRole("User"))
{
SendContentTypeAndFile(context, requestedFile);
}
else
{
// Deny access, redirect to error page or back to login
//page.
context.Response.Redirect("~/User/AccessDenied.aspx");
}
break;
}
}
}
Method SendContentTypeAndFile :
private HttpContext SendContentTypeAndFile(HttpContext context, String strFile)
{
context.Response.ContentType = GetContentType(strFile);
context.Response.TransmitFile(strFile);
context.Response.End();
return context;
}
private string GetContentType(string filename)
{
// used to set the encoding for the reponse stream
string res = null;
FileInfo fileinfo = new FileInfo(filename);
if (fileinfo.Exists)
{
switch (fileinfo.Extension.Remove(0, 1).ToLower())
{
case "pdf":
{
res = "application/pdf";
break;
}
}
return res;
}
return null;
}
Last step is that you need to configure this HTTP Handler in the webconfig ,
and You can see the more info here
Here is the complete Source Code
You're architecture (and assumptions) seem good for a low/mid security level, but if the nature of your data is very sensitive (medical, etc) my biggest concern about security would be controlling the user sessions.
If you're using forms authentication then you're storing the authenticated identity in a cookie or in a token (or if you're using sticky sessions then you're sending the session Id, but for the case it's the same). The problem arises if user B has phisical access to the machine where user A works. If user A leaves it's workplace (for a while or forever) and he doesn't explicitly close it's session in your web app, then his identity has been left around, at least until his cookie/token expires, and user B can use it since the identity system of ASP.NET hasn't performed a SignOut. The problem is even worse if you use tokens for authorization, because in all the infamous Microsoft implementations of the Identity System you're responsible of providing a way to invalidate such tokens (and make them dissapear from the client machine) when the user signs out, since they would stay valid until it's expiration. This can be addressed (but no completely thus not very satisfactorily for high security requirements) issuing short living refresh tokens, but that's another story, and I don't know if it's your case. If you're going with cookies then when user A signs out it's cookie is invalidated and removed from the request/response cicle, so this problem is mitigated. Anyway you should ensure that your users close their sessions in your web app or/and configure the cookies with short lives or short sliding expirations.
Other security concerns may be related with CSRF, wich you can prevent using the Antiforgery Token infrastructure of ASP.NET, but these kind of attacks are methods that are very far away from the tipical user (I don't know anything about the nature of your user and if your app is exposed to public on internet or it's only accesible on an intranet), but If you worry for such specialised attacks and have so sensitive data, maybe you should go with something more complex than forms authentication (two factor, biometrical, etc)
I have an ASP.NET 3.5 application that I recently extended with multiple membership and role providers to "attach" a second application within this application. I do not have direct access to the IIS configuration, so I can't break this off into a separate application directory.
That said, I have successfully separated the logins; however, after I login, I am able to verify the groups the user belongs to through custom role routines, and I am capable of having identical usernames with different passwords for both "applications."
The problem that I am running into is when I create a user with an identical username to the other membership (which uses web.config roles on directories), I am able to switch URLs manually to the other application, and it picks up the username, and loads the roles for that application. Obviously, this is bad, as it allows a user to create a username of someone who has access to the other application, and cross into the other application with the roles of the other user.
How can I mitigate this? If I am limited to one application to work with, with multiple role and membership providers, and the auth cookie stores the username that is apparently transferable, is there anything I can do?
I realize the situation is not ideal, but these are the imposed limitations at the moment.
Example Authentication (upon validation):
FormsAuthentication.SetAuthCookie(usr.UserName, false);
This cookie needs to be based on the user token I suspect, rather than UserName in order to separate the two providers? Is that possible?
Have you tried specifying the applicationName attribute in your membership connection string?
https://msdn.microsoft.com/en-us/library/6e9y4s5t.aspx?f=255&MSPPError=-2147217396
Perhaps not the answer I'd prefer to go with, but I was able to separate the two by having one application use the username for the auth cookie, and the other use the ProviderUserKey (guid). This way the auth cookie would not be recognized from one "application" to the other.
FormsAuthentication.SetAuthCookie(user.ProviderUserKey.ToString(), false);
This required me to handle things a little oddly, but it simply came down to adding some extension methods, and handling a lot of membership utilities through my own class (which I was doing anyhow).
ex. Extension Method:
public static string GetUserName(this IPrincipal ip)
{
return MNMember.MNMembership.GetUser(new Guid(ip.Identity.Name), false).UserName;
}
Where MNMember is a static class, MNMembership is returning the secondary membership provider, and GetUser is the standard function of membership providers.
var validRoles = new List<string>() { "MNExpired", "MNAdmins", "MNUsers" };
var isValidRole = validRoles.Intersect(uroles).Any();
if (isValidRole)
{
var userIsAdmin = uroles.Contains("MNAdmins");
if (isAdmin && !userIsAdmin)
{
Response.Redirect("/MNLogin.aspx");
}
else if (!userIsAdmin && !uroles.Contains("MNUsers"))
{
Response.Redirect("/MNLogin.aspx");
}...
Where isAdmin is checking to see if a subdirectory shows up in the path.
Seems hacky, but also seems to work.
Edit:Now that I'm not using the username as the token, I should be able to go back to using the web.config for directory security, which means the master page hack should be able to be removed. (theoretically?)
Edit 2:Nope - asp.net uses the username auth cookie to resolve the roles specified in the web.config.
I have mvc 4 project with default Account Controller and UsersContext as DbContext. When a user, Suppose User1 logged in with REMEMBER ME checked on mobile. And next day User1 login in Laptop and changed its password, now User1 opened his mobile it is already logged in even when password is changed.
Is there any way by which i can force that User1 to Logout all the Devices,
without storing any session id in database?
I imagine you could store a "last password change date" in the cookie. On authentication, if the user has changed his password since the stored date, then do not authenticate the user.
You should probably store a hash of the last password change date, so that it can't be tampered with from the client side.
Have a flag in the database that checks users on Session_Start that invalidates their session if that flag is set. May not necessarily use a boolean, you can use a DateTime value and invalidate all sessions that started prior to that time. This could be done by checking a value stored in a cookie upon login.
There is nothing built into ASP.NET MVC (that I know of) that provides this functionality
I know this is an old topic but I ran across this today. The cause was slightly different, but I needed to do the same thing - log off a user if 'remember me' had been chosen.
The cause in my case was a change of database. I changed the database name in my connection string (from a 'test' DB to a 'dev' DB), and I will still logged in after running the project, which caused a problem because the user did not actually exist in this new DB.
So in Global.asax I did the following:
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
protected void Session_Start()
{
if (User.Identity.IsAuthenticated)
{
var user = UserManager.FindByName(User.Identity.Name);
if (user == null)
{
HttpContext.Current.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
Response.Redirect("/", true);
}
}
}
You need to redirect and terminate execution of the current page (by passing true) because (as I understand it) the sign out does not affect the current request, so the page that was loading will still load as if the user is logged in. Anyone feel free to correct, clarify or expand upon this.
Is there any good reason why ASP.NET's session state cookie and the Forms Authentication cookie are two separate cookies? What if I want to "tie" them to each other? Is it possible in an elegant way?
Right now, I am stuck with the following solution, which works, but is still ugly:
[Authorize]
public ActionResult SomeAction(SomeModel model)
{
// The following four lines must be included in *every* controller action
// that requires the user to be authenticated, defeating the purpose of
// having the Authorize attribute.
if (SomeStaticClass.WasSessionStateLost/*?*/) {
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account");
}
// ...
}
#RPM1984: This is what happens:
[HttpPost]
public ActionResult Login(LoginModel loginModel)
{
if (/* user ok */)
{
// ...
Session["UserID"] = loginModel.UserID;
Session["Password"] = loginModel.Password;
// ...
}
else
{
return View();
}
}
And it doesn't take much guessing to know what WasSessionStateLost does.
Session != Authentication
The session state cookie tracks the user's activity during a browser session.
The forms authentication cookie tracks the user's authenticated activity during a given time period, specified by the expiration date of the ticket and whether or not you have created a persistent cookie (e.g "Remember Me" checkbox).
You shouldn't be touching the session cookie itself, and all it contains is an identifier to tie the client session (browser) to the server.
If you need to access the session, use HttpContext.Current.Session.
What exactly are you trying to "tie" together?
What does SomeStaticClass.WasSessionStateLost do?
I'll start with a solution, then an explanation followed by a recommendation.
Create a custom authorization attribute:
Since your application defines Authorized as follows:
Logged in
Must have values in Session["UserID"] and Session["Password"]
you need to define your own AuthorizationAttribute
public class AuthorizedWithSessionAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if(httpContext.Request.IsAuthenticated &&
Session["UserID"] != null && Session["Password"] != null)
return true;
// sign them out so they can log back in with the Password
if(httpContext.Request.IsAuthenticated)
FormsAuthentication.SignOut();
return false;
}
}
Replace all your [Authorize] attributes with [AuthorizedWithSession] and you shouldn't need to put session check code in your controllers.
I don't know enough about your application, but saving passwords in session (even worse in plain text) is not a secure thing to do.
In addition, as RPM1984 said, the session cookie and authentication cookie are separate.
Explanation:
Think of the session as a bucket of info (on the server side) with your name on it. ASP.NET can take and put stuff in that bucket. ASP.NET gives you a name, your session id, and puts it on the bucket so it can know which one is yours.
The authentication cookie tells ASP.NET that you're authenticated and stores your authentication name in it. The authentication name is usually set by the developer of the application and is usually a unique key (think primary key in a DB) to separate you from the other users.
Recommendation to be more secure:
Encrypt the passwords before your store them. This is not total security, but it beats storing passwords in plain text and of course, if someone were to get a hold of the encryption key, they can crack the passwords.
Rather than using session, which is short lived you could cache in the System.Web.Cache. With this you can add events that are called before an entry is removed and decide accordingly if the cache should be cleared. You can set a higher time-out value on that, with the added bonus that you're not storing the clear text password in a file or database anywhere. Another bonus is you won't be vulnerable to session hijacking.
Of course if the application pool recycles the cache is gone, and as it's in memory load balanced machines will be out of sync, but Velocity or another distributed, out of process cache system would solve that.
It's not perfect though, entries may be dumped due to pressure on the cache, and of course you know this is all a bad idea anyway, so I'll skip that lecture.
I have an app with multiple subdomains, subone.parent.com, subtwo.parent.com.
I have a logon page at parent.com/login. When a user logs in I redirect them to the proper domain based on which one they are a member of. This works fine.
FormsAuthenticationTicket ticket = new FormsAuth...
string encTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
cookie.Domain = subone.parent.com
Response.Cookies.Add(cookie)
This properly authenticates the user for subone.parent.com and not subtwo.parent.com. However I would like to do the following.
If the user goes back to parent.com, I would like to know that they are logged in and redirect them back to subone.parent.com.
Is there a best practice for accomplishing this? Or do I have to set another cookie for parent.com?
I'm working in asp.net mvc if it matters.
THanks!
You can share cookies across domains like you are trying to do, but its not straight forward, example here.
Another options is to set the cookie to be ".parent.com" rather than specifying the sub-domain explicitly and use the cookie store the details of the sub-domain. Then you can access the cookie from any of your sub-domains (and parent assuming its www.parent.com).
If your using MVC, you can pretty easily create a custom filter and add to the www.parent.com controllers to check for the existence of the cookie, and if so redirect to the sub domain the cookie specifies. Further details of filters here.
I would set the cookie for the explicit domain as you have there because that maintains any security information within that specific domain's cookie. You can also add a non-encrypted cookie at the *.parent.com level that holds information about which domains have been authenticated. There is no real way to tie this together though without using maybe timestamps and having a logical connection between the applications (ie - sub2 has a session timeout of 20 minutes so if the domain + valid timestamp occur in the parent cookie it would be valid, however this is business logic).
I'm not sure the reasoning behind the disconnect between domains, but you might actually prefer to have a single cookie that has encrypted text behind encrypted text. EG:
1) Sub1 logs in, sets the parent.com cookie as valid. Sends a piece of user data to an authentication web service.
2) The authentication service recognizes sub1 as the sender, encrypts the user data, and adds it to a custom cookie object.
3) The custom cookie object constructs a composite string on a unique split character (or sequence) and makes it available to the service method.
4) The service, using the forms encryption, encrypts the entire ticket and sends it back to the original login.
That way each server would be able to unencrypt the global ticket, but each piece of data would be encrypted using a common algorithm but a server based salt. So if sub2 attempts to read the cookie data from sub1, it gets the encrypted version rather than raw data.
you could share the same session on all subdomains. That is the code we use to accomplish that :-)
void MasterPage_Unload(object sender, EventArgs e)
{
///ASP.NET uses one cookie per subdomain/domain,
///we need one cookie for _all_ subdomains.
if (Context.Response.Cookies["ASP.NET_SessionId"] == null)
return;
var sessionCookie = new HttpCookie("ASP.NET_SessionId", Context.Session.SessionID);
sessionCookie.Domain = ".yourdomain.com" ;
Context.Response.SetCookie(sessionCookie);
}
inside the Page_Load method is:
Unload += MasterPage_Unload;
it works great :-)
robert