Am I going about this the wrong way? - asp.net

This is my first MVC/Linq to SQL Application. I'm using the out of the box SQL Membership with ASP.NET to track users through my system.
As most of you know, the UserId is a guid, great. However, to link other user-created tables in the system, I decided to go with username instead of userid. The reason I did this was because:
Username is unique anyway
It prevents me from having to make an extra call when handling db functions.
So for example: I don't have to do a look up on the userid based on username to create a new story; I simply insert User.Identity.Name into the story table.
Now I did run into some nasty complication, which seems to be related to this. It worked fine on my local machine, but not on the host. I continually got an error that went something like this:
"System.InvalidCastException: Specified cast is not valid. at System.Data.Linq.IdentityManager.StandardIdentityManager.SingleKeyManager"...
This happened whenever an insert on the db occurred on the host. If I understand correctly, this is a bug currently that happens when you link a non integer field (in my case username) to another table of a non integer field (username in aspnet_user). Although the bug reported seems a little bit different, maybe they are similar?
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=351358
In any case, MS bug or not - is storing the username instead of the userid in my tables a bad idea? If it is, why?
Update
I just wanted to add some more context here. A good point people are bringing up is that this is dangerous if I want to allow users to change their username in the future. Perfectly valid!
However, this application relies heavily on the username. Each user creates one and only one story. They then link to their story by using: mysite/username. Therefore, the application will never allow them to change their username. It would cause a potential nightmare for people who follow the link only to see it no longer exists.

Be careful regarding your comment regarding usernames are unique. The minute Anita Takeabath gets married to Seymour Butts suddenly atakebath wants to be abutts.
Just a thought!

I've used the same approach as you and it works. Do you have a relationship between your application table and the table from the membership db? If so, you may want to remove that relationship.

My only thought would be in order to future proof your application, the userid would offer flexibility in users changing their username, as the userid would remain constant (like SO for instance).
But that is something that has to fit your application requirements. Then again requirements often tend to change wihtout a developers control.

It's bad for the following reasons:
You mentioned avoiding extra database calls. However, by joining tables, there is no "extra" call to database. You can argue that joining is expensive than no joining at all. However, most likely, a store needs more user information than a user login name (note: user names are not unique, user login names are unique). So you need joining anyway for most database operations.
User login names have different length, it doesn't perform well when they are used in joining.
Edit: modified format. I am still learning how to make my post look better:-)

If the reason you're implementing this is for easier access to the User's GUID, I suggest having your FormsAuthentication.SetAuthCookie use the users's GUID as the name property and use User.Identity.Name throughout your application.
Using username as the unique identifier could have bad consequences in the future. Should you want to allow the user change their username in the future, you will have a hard time implementing that.

Related

Is it ok to use username as a foreign key in asp.net membership

I use asp membership in application.
I added UserProfile table and it has foreign key to Users(of asp membership).
As a foreign key I use Username because username is email and it's unique.
And anywhere where I need to reference user I use Username as foreign key.
From application when I need to get profile for example I pass Username to stored procedure to get data.
I just wonder if this is the good way to do this. Is there some potentional security issue here?
The main issue that I see here is that you spend a lot of "data space" for a foreign key and this will make it slow and eat database space for your tables. Also you database table will connect making string compare - database take care and make hash for this strings and behind is make a number compare, but have a little small overhead on that.
Just make the UserName unique and use a number foreign key to connect it with the rest table.
The second issue here is when a user need to change their email, or give it wrong for any reason. In this case you need to update all the connections on the database and make sure that there is not other similar email.
And one more issue is that the email and the foreign key can be case sensitive or not. If for any reason you make it case sensitive then you make a mess.
About security issues, you always need to open and ask your database using parameters. This is the same for a number key or for a string key, so this make no different at this case.
I would say no for one simple reason: many systems allow users to change their usernames. In your case, you link this to an email address, which users should be allowed to change.
If you use it as a foreign key, you have to run updates to keep your data in sync, and that is bad.
This is an old natural vs. surrogate key discussion. There are "fans" of either approach, but the simple truth is that both have pros and cons, and you'll have to make your own decision that best fits your particular situation.
For the specific case of e-mail as PK, you might want to take a look at this discussion.

Documents/links on preventing HTML form fiddling?

I'm using ASP.Net but my question is a little more general than that. I'm interested in reading about strategies to prevent users from fooling with their HTML form values and links in an attempt to update records that don't belong to them.
For instance, if my application dealt with used cars and had links to add/remove inventory, which included as part of the URL the userid, what can I do to intercept attempts to munge the link and put someone else's ID in there? In this limited instance I can always run a check at the server to ensure that userid XYZ actually has rights to car ABC, but I was curious what other strategies are out there to keep the clever at bay. (Doing a checksum of the page, perhaps? Not sure.)
Thanks for your input.
The following that you are describing is a vulnerability called "Insecure Direct Object References" And it is recognized by A4 in the The OWASP top 10 for 2010.
what can I do to intercept attempts to
munge the link and put someone else's
ID in there?
There are a few ways that this vulnerability can be addressed. The first is to store the User's primary key in a session variable so you don't have to worry about it being manipulated by an attacker. For all future requests, especially ones that update user information like password, make sure to check this session variable.
Here is an example of the security system i am describing:
"update users set password='new_pass_hash' where user_id='"&Session("user_id")&"'";
Edit:
Another approach is a Hashed Message Authentication Code. This approach is much less secure than using Session as it introduces a new attack pattern of brute force instead of avoiding the problem all togather. An hmac allows you to see if a message has been modified by someone who doesn't have the secret key. The hmac value could be calculated as follows on the server side and then stored as a hidden variable.
hmac_value=hash('secret'&user_name&user_id&todays_date)
The idea is that if the user trys to change his username or userid then the hmac_value will not be valid unless the attacker can obtain the 'secret', which can be brute forced. Again you should avoid this security system at all costs. Although sometimes you don't have a choice (You do have a choice in your example vulnerability).
You want to find out how to use a session.
Sessions on tiztag.
If you keep track of the user session you don't need to keep looking at the URL to find out who is making a request/post.

is there a way to get all profiles in ASP.NET membership

Is there an API in the ASP.NET membership, implementation to get all user profiles at once. If not, is there another good way to get all the names (first + last) of all the users. I'm trying to avoid the many SQL requests generated by getting the user profiles one at a time.
ProfileProvider.GetAllProfiles().
I'd still recommend just adding first and last names to the MembershipUser though. You'll need to cast your provider to the concrete type, which is brittle if you ever want to change it.
Update:
A challenge with the way profile data is stored is that the property names and values are packed and stored in two columns in the Profile database. If you run the aspnet_Profile_GetProperties sproc you will see that.
There is no out-of-the-box sproc that gets profile data for all users. A quick modification to the aspnet_Profile_GetProperties would do that for you though.

Query String Parameters make my app at risk?

I'm writing an Asp.Net WebForms app where I am calling an edit page an passing in the data about the record to be edited using query string parameters in the URL.
Like:
http://myapp.path/QuoteItemEdit.aspx?PK=1234&DeviceType=12&Mode=Edit
On a previous page in the app, I have presented the user with a GridView of screened items he can edit based on his account privileges, and I call the edit page with these above parameter list, and the page know what to do. I do NOT do any additional checking on the target page to validate whether the user has access to the passed in PK record value as I planned to rely on the previous page to filter the list down and I would be fine.
However, it is clear the user can now type in a URL to a different PK and get access to edit that record. (Or, he may have access to Mode=View, but not Mode=Edit or Mode=Delete. Basically, I was hoping to avoid validating the record and access rights on the target page.
I have also tested the same workflow using Session variables to store PK, DeviceType, and Mode before calling the target page, and then reading them from Session in the target page. So there are no query string paramaters involved. This would take control away from the user.
So, I'm looking for feedback on these two approaches so that I choose an accepted/standard way of dealing with this, as it seems like a very common app design pattern for CRUD apps.
Agreed, you'll want to validate permissions on the target page, it's the only way to be absolutely sure. When it comes to security, redundancy isn't a bad thing. Secure your database as if you don't trust the business layer, secure your business layer as if you don't trust the UI, and secure the UI as well.
You should always validate before the real execution of the action, especially if passing the parameters by query string. For the second page that does the execution you might not need as much feedback for the user since you do not have to be nice to the user if he tries to cirumvent your security, so error handling should be a lot easier.
Passing the variables per session is acceptable but imho you should still validate the values.
We always use querystrings so records can be bookmarked easily, however always validate in both places, if you write you access control code nicely it should just be a case of re-using the existing code...
I believe the common practice is to do what you're avoiding: On the original page, you need to check to see what the user should have capabilities to do, and display their options appropriately. Then on the actual work page, you need to check the user again to verify they are allowed to be there, with access to that specific task.
From a usability standpoint, this is what the user would want (keeps it simple, allows them to bookmark certain pages, etc), and security on both pages is the only way to do this.
If you really don't want to check access rights on the target page:
You could hash the PK with the UserID and then add the hash value to the query string.
string hash = hashFunction(PK.toString() + UserID.toString());
Then you have to make sure the hash in the queryString equals the hash value calculated before loading the page.
Assuming this is an internal organization Web application.
Session variables can be manipulated as well, although not as easily. Whatever authentication you're using throughout your site, you should definitely use on your target page as well. Otherwise, you'll be open to exposing data you may not want as you have found out.
You could do the following to make your URLs a bit more secure:
-Use Guids for Primary Keys so users cant guess other record ID's
-The Mode couls be implicit: Guid = Edit, no Guid = New
and..
-Server-side validation is the only way to go.

Should I use the username, or the user's ID to reference authenticated users in ASP.NET

So in my simple learning website, I use the built in ASP.NET authentication system.
I am adding now a user table to save stuff like his zip, DOB etc. My question is:
In the new table, should the key be the user name (the string) or the user ID which is that GUID looking number they use in the asp_ tables.
If the best practice is to use that ugly guid, does anyone know how to get it? it seems to not be accessible as easily as the name (System.Web.HttpContext.Current.User.Identity.Name)
If you suggest I use neither (not the guid nor the userName fields provided by ASP.NET authentication) then how do I do it with ASP.NET authentication? One option I like is to use the email address of the user as login, but how to I make ASP.NET authentication system use an email address instead of a user name? (or there is nothing to do there, it is just me deciding I "know" userName is actually an email address?
Please note:
I am not asking on how get a GUID in .NET, I am just referring to the userID column in the asp_ tables as guid.
The user name is unique in ASP.NET authentication.
You should use some unique ID, either the GUID you mention or some other auto generated key. However, this number should never be visible to the user.
A huge benefit of this is that all your code can work on the user ID, but the user's name is not really tied to it. Then, the user can change their name (which I've found useful on sites). This is especially useful if you use email address as the user's login... which is very convenient for users (then they don't have to remember 20 IDs in case their common user ID is a popular one).
You should use the UserID.
It's the ProviderUserKey property of MembershipUser.
Guid UserID = new Guid(Membership.GetUser(User.Identity.Name).ProviderUserKey.ToString());
I would suggest using the username as the primary key in the table if the username is going to be unique, there are a few good reasons to do this:
The primary key will be a clustered index and thus search for a users details via their username will be very quick.
It will stop duplicate usernames from appearing
You don't have to worry about using two different peices of information (username or guid)
It will make writing code much easier because of not having to lookup two bits of information.
I would use a userid. If you want to use an user name, you are going to make the "change the username" feature very expensive.
I would say use the UserID so Usernames can still be changed without affecting the primary key. I would also set the username column to be unique to stop duplicate usernames.
If you'll mainly be searching on username rather than UserID then make Username a clustered index and set the Primary key to be non clustered. This will give you the fastest access when searching for usernames, if however you will be mainly searching for UserIds then leave this as the clustered index.
Edit : This will also fit better with the current ASP.Net membership tables as they also use the UserID as the primary key.
I agree with Palmsey,
Though there seems to be a little error in his code:
Guid UserID = new Guid(Membership.GetUser(User.Identity.Name)).ProviderUserKey.ToString());
should be
Guid UserID = new Guid(Membership.GetUser(User.Identity.Name).ProviderUserKey.ToString());
This is old but I just want people who find this to note a few things:
The aspnet membership database IS optimized when it comes to accessing user records. The clustered index seek (optimal) in sql server is used when a record is searched for using loweredusername and applicationid. This makes a lot of sense as we only have the supplied username to go on when the user first sends their credentials.
The guid userid will give a larger index size than an int but this is not really significant because we often only retrieve 1 record (user) at a time and in terms of fragmentation, the number of reads usually greately outweighs the number of writes and edits to a users table - people simply don't update that info all that often.
the regsql script that creates the aspnet membership tables can be edited so that instead of using NEWID as the default for userid, it can use NEWSEQUENTIALID() which delivers better performance (I have profiled this).
Profile. Someone creating a "new learning website" should not try to reinvent the wheel. One of the websites I have worked for used an out of the box version of the aspnet membership tables (excluding the horrible profile system) and the users table contained nearly 2 million user records. Even with such a high number of records, selects were still fast because, as I said to begin with, the database indexes focus on loweredusername+applicationid to peform clustered index seek for these records and generally speaking, if sql is doing a clustered index seek to find 1 record, you don't have any problems, even with huge numbers of records provided that you dont add columns to the tables and start pulling back too much data.
Worrying about a guid in this system, to me, based on actual performance and experience of the system, is premature optimization. If you have an int for your userid but the system performs sub-optimal queries because of your custom index design etc. the system won't scale well. The Microsoft guys did a generally good job with the aspnet membership db and there are many more productive things to focus on than changing userId to int.
I would use an auto incrementing number usually an int.
You want to keep the size of the key as small as possible. This keeps your index small and benefits any foreign keys as well. Additonally you are not tightly coupling the data design to external user data (this holds true for the aspnet GUID as well).
Generally GUIDs don't make good primary keys as they are large and inserts can happen at potentially any data page within the table rather than at the last data page. The main exception to this is if you are running mutilple replicated databases. GUIDs are very useful for keys in this scenario, but I am guessing you only have one database so this is not a problem.
If you're going to be using LinqToSql for development, I would recommend using an Int as a primary key. I've had many issues when I had relationships built off of non-Int fields, even when the nvarchar(x) field had constraints to make it a unique field.
I'm not sure if this is a known bug in LinqToSql or what, but I've had issues with it on a current project and I had to swap out PKs and FKs on several tables.
I agree with Mike Stone. I would also suggest only using a GUID in the event you are going to be tracking an enormous amount of data. Otherwise, a simple auto incrementing integer (Id) column will suffice.
If you do need the GUID, .NET is lovely enough that you can get one by a simple...
Dim guidProduct As Guid = Guid.NewGuid()
or
Guid guidProduct = Guid.NewGuid();
I'm agreeing with Mike Stone also. My company recently implemented a new user table for outside clients (as opposed to internal users who authenticate through LDAP). For the external users, we chose to store the GUID as the primary key, and store the username as varchar with unique constraints on the username field.
Also, if you are going to store the password field, I highly recommend storing the password as a salted, hashed binary in the database. This way, if someone were to hack your database, they would not have access to your customer's passwords.
I would use the guid in my code and as already mentioned an email address as username. It is, after all, already unique and memorable for the user. Maybe even ditch the guid (v. debateable).
Someone mentioned using a clustered index on the GUID if this was being used in your code. I would avoid this, especially if INSERTs are high; the index will be rebuilt every time you INSERT a record. Clustered indexes work well on auto increment IDs though because new records are appended only.

Resources