I am working on a side project(student, not homework, just holiday curiosity), which would be my simple personal password manager/vault. It will be a windows app. I want it to support more than just one user. Now that i have my login screen and other functionalities, i struggle to find a simple and effective way to check if login and password of the user are correct. I store the information in a simple SQL table called Users(picture below). Now, i want to get the bool result, whether are these two strings (username,password) in database. i will include some code and screenshots below. Any help appreciated!
P.S.: I am pretty familiar with mySQL(Oracle), i just canĀ“t fin a good way to do this without being ridiculous. :)
namespace KeyLocker.Database {
[Serializable]
public class UserDatabase {
public void AddUser(string username,string password,string question,string answer) {
using(var db = new KeyLockerContext()) {
db.Add(new User { Username = username,Password = password,SecurityQuestion = question,SecurityAnswer = answer });
db.SaveChanges();
}
}
public bool CheckUser(string username,string password) {
using(var db = new KeyLockerContext()) {
/*here i need to find out if username is present in database
and if it is, i need to find out if the password is correct*/
}
}
}
}
Table Users ScreenShot
Table SQL detail
At first glance, it seems like the easiest code to execute your idea would be:
public bool CheckUser(string username,string password) {
using(var db = new KeyLockerContext()) {
// Check if that combination already exists in DB.
var result = db.Users.Where(x => x.Username == username && x.Password == password).SingleOrDefault();
// See if result has a value, SingleOrDefault() returns null if no match
return (result == null);
}
}
EDIT/Disclaimer: Since there seems to be some confusion, this is an idea for how to quickly check if an object w/ certain values already exists in the DB - not a complete authentication library. You should never store cleartext passwords.
Related
So I'm currently setting up a small database for testing purposes and I'm trying to follow some general safety guidelines such as trying to prevent a SQL attack by using parameterized queries. When using EF Core to register a user I would usually do something along the lines of.
public IActionResult Register(UserModel userModel)
{
using (var ctx = new APIDbContext())
{
if (!ctx.Users.Any(x => x.Username.ToLower() == userModel.Username.ToLower()))
{
ctx.Users.Add(new UserModel
{
Username = userModel.Username,
Password = userModel.Password,
});
return Ok();
}
}
return NotFound("Username already taken.");
}
Besides storing the password in plain text, is there anything super bad going on here? Can you SQL inject something like this and is there anything I should think of?
[HttpPost] // Use the right http verb.
// You dont want to submit credentials in the url, but the body.
// Put validations on the Model properties.
1. You should have your own password policy.
2. Whitelist the username characters to prevent any malicious
characters that can be compile somehow into scripts
3. Have an limit of length on every request input that will go
to the database.
// Performance Tip: Make your action async.
// async Task<IActionResult> and await on user creation function
public IActionResult Register(UserModel userModel)
{
// Add captcha to guard the application from malicious automation tools.
// It would be better to take the users email,
// for example in order to reset the password.
// If the username is his password, then ignore this point.
// Optional: Add a central or localized logging capability for security reasons.
using (var ctx = new APIDbContext())
{
if (!ctx.Users.Any(x => x.Username.ToLower() == userModel.Username.ToLower()))
{
ctx.Users.Add(new UserModel
{
Username = userModel.Username,
Password = userModel.Password, // This needs to be hashed
});
// await ctx.SaveChangesAsync()
return Ok();
}
}
return NotFound("Username already taken.");
}
I have provided you with guidelines in the comments at your code.
As long as you use linq queries, they are not vulnerable to traditional SQL injection attacks.In this way Entity Framework passes the data via SQL parameters. Also When passwords are stored in plain text, anyone who knows the database password, can get them and log into any user profile. So they needed to be hashed in the database.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
How to save history of user last login and display right after user login in login screen.
I have shared the code in which I have implemented for the user session login. I am checking for any help with explanation will be appreciated.
User Login and Validation Code:
public ActionResult Login(LoginViewModel login)
{
if (ModelState.IsValid)
{
if (new UserEntity().isValidUser(login.EmailId, login.Password))
{
Session["login"] = login;
//Redirect to Employee Controller after Validation
return RedirectToAction("Index", "Employee");
}
else
{
ViewBag.InvalidUser = "Invalid User Name or
Password";
return View(login);
}
}
return View(login);
}
public Boolean isValidUser(string emailId, string password)
{
Boolean isValid = false;
try
{
sqlConnection = new SqlConnection(ConnectionString);
string query = #"Select * from UserProfile
where EmailID='" + emailId + "' and
Password = '"+password+"'";
cmd = new SqlCommand(query, sqlConnection);
sqlConnection.Open();
SqlDataReader dataReader = cmd.ExecuteReader();
if (dataReader.Read())
{
isValid = true;
}
}
catch (Exception exp)
{
//exception logging
}
return isValid;
}
This is just an attempt to try and (hopefully) guide you further in the right direction. I am not sure that there is a built-in feature that holds this information, so we may need to store the data manually. Maybe it could be something like this:
public ActionResult Login(LoginViewModel login)
{
if (ModelState.IsValid)
{
SaveLoginTimeToDatabase(); //here, we call a method that we define ourselves
... //rest of your original code
}
return View(login);
}
public void SaveLoginTimeToDatabase()
{
DateTime loginTime = new DateTime().Now;
sqlConnection = new SqlConnection(ConnectionString);
string query = #"Insert* into UserLoginTimeTable
VALUES (User.Email, loginTime)";
//So, we need to create a new database table, UserLoginTimeTable, for this info
//... Rest of code: open SQL connection, execute etc.
}
However, we also need to display the login time when user logs in, so we need a method for that. To do that, we probably a field on your Razor login-page, where that information can be displayed.
Then, in the login method, we call a new method to fetch that data
public ActionResult Login(LoginViewModel login)
{
if (ModelState.IsValid)
{
var lastLogin = FetchLastLoginTime(login.EmailId); //new method for retrieving the data
// you need to call it before SaveLoginTime...(), so that you dont get the current login, that will be saved in the next line
SaveLoginTimeToDatabase();
//you may need to include lastLogin in your loginViewModel:
login.LastLogin = lastLogin;
... //rest of your original code
}
return View(login);
}
public DateTime FetchLastLoginTime(string emailId)
{
sqlConnection = new SqlConnection(ConnectionString);
string query = #"Select TOP 1 from UserLoginTimeTable
Where EmailID = ' emailId"
//Open sql connection, execute etc
//Now, you should have access to the last time that user logged in. Return it to the original method and display it
var lastLogin = //get the logintime from the query
return lastLogin;
}
This is not a definitive answer and may have incorrect syntax, but just an overall attempt to show how you maybe can go about it. You don't need to accept it as answer, it's just a suggestion that you hopefully can make work. Since you would need multiple queries to the database like this, you may need to implement some asynchronous behaviour with await/async, unit of work/transactions or similar, but hopefully it just works.
So to sum up, with this approach you need a new database table, queries and additional logic. You are always welcome to tag me/write if you have more questions, you think I can help with (hopefully you can use some of this), best of luck
I have an API that I've created. I've (finally) managed to both GET and POST with it. Now I want to check the POST before it gets submitted.
I have a class with properties (is that the right word? I'm still learning the lingo) of id, name, city, state, and country.
I have a form with a button, and the button has a click event method that looks like this:
protected void submitButton_Click(object sender, EventArgs e)
{
void Add_Site(string n, string ci, string s, string co)
{
using (var client = new HttpClient())
{
site a = new site
{
name = n,
city = ci,
state = s,
country = co
};
var response = client.PostAsJsonAsync("api/site", a).Result;
if (response.IsSuccessStatusCode)
{
Console.Write("Success");
}
else
Console.Write("Error");
}
}
Add_Site(nameText.Text, cityText.Text, stateText.Text, countryText.Text);
}
Now, at this point, it's working as expected. However, I'd like to limit it.
What I want to do is to have it look at the nameText.Text value. Before it runs the POST statement, I want it to look at the other values in the GET of the API and make sure that name doesn't already exist.
While I know that I could probably update the database to make the name field unique, I'd rather do it programatically in C#.
Is this something that's possible within my C# code? If so, what function would I use and how would I get it to return the Site.Name attribute to compare my nameText.Text value?
EDIT:
Here is the code for the POST as requested in one of the comments. Note: This was auto-generated by Visual Studio when I added the controller.
// POST: api/site
[ResponseType(typeof(site))]
public IHttpActionResult Postsite(site site)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.site.Add(site);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = site.id }, site);
}
I wouldn't have any idea where to even start with adding an "If the name already exists, throw an error," so some kind of walkthrough is plenty.
Here's code for looking in the database if any sites have the provided name (site.Name):
if (db.site.Any(o => o.Name == site.Name))
return BadRequest("Name already exists.");
I eventually determined that I should modify the POST as said in the comments. The solution I used ended up in this question: How to limit POST in web API
Users of my site have experienced some strange behaviour yesterday (first time I've seen this issue), and unfortunately I don't have much in the way of error logs to try to figure out what's going on. The site had a higher-than-normal number of people online at once, albeit not a large number in the grand scheme of things (maybe 50 to 100 users all trying to perform similar functions). I can't recreate the issue in my development environment, haven't seen it before, and don't really know why it is happening.
The crux of the problem is that users can register or log on successfully, but a small number of them could see other users' data.
The site is ASP.NET MVC 3.
Users are logging on and I set an authentication cookie - here's the LogOn action:
[HttpPost]
public ActionResult LogOn(AccountLogOnViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (!Membership.ValidateUser(model.UserName, model.Password))
{
ModelState.AddModelError("login-message", "Incorrect username or password");
}
}
if (ModelState.IsValid)
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
Session.Remove("MenuItems");
return Redirect(returnUrl ?? Url.Action("Index", "Home"));
}
else
{
model.ReturnUrl = returnUrl;
return View(model);
}
}
AccountLogOnViewModel is a simple object with two string properties, UserName and Password.
From what I can gather, this is fine - if you log in as NickW then doing something like User.Identity.Name correctly gives you "NickW" (when users were seeing other users' data, they reported that that "Welcome, NickW" text on screen was showing them the correct value - this is written out using User.Identity.Name)
The site also uses a custom membership provider. It overrides the ValidateLogin method, and the GetUser method. ValidateLogin appears to be working just fine so I'm not concerned about it.
The overridden GetUser method is as follows:
public override MembershipUser GetUser(string username, bool userIsOnline)
{
User user = _userRepository.Users.FirstOrDefault(u => u.UserName == username);
MembershipUser membershipUser = null;
if (user == null)
return membershipUser;
membershipUser = new MembershipUser(this.Name,
user.UserName,
user.Id,
user.Email,
null,
user.Comments,
user.IsActivated,
user.IsLockedOut,
user.CreatedDate,
user.LastLoginDate,
user.LastLoginDate,
user.LastModifiedDate,
Convert.ToDateTime(user.LastLockedOutDate));
return membershipUser;
}
So I'm attempting to retrieve a User object from my database, and using that to create a new MembershipUser object. My database User table has additional columns on top of those required by the membership provider - e.g. name, address, phone number etc.
At various points in the rest of the website (for example if you go to the Profile page), I retrieve a user object from the database and use it to populate the screen. The line I use to retrieve the User object is:
User user = userRepository.Users.FirstOrDefault(u => u.UserName == Membership.GetUser().UserName);
Here is a cut down version of the userRepository (i.e. just removing unrelated code).
public class SqlUserRepository : IUserRepository
{
private Table<User> usersTable;
private string _connectionString;
public SqlUserRepository(string connectionString)
{
_connectionString = connectionString;
usersTable = (new DataContext(connectionString)).GetTable<User>();
}
public IQueryable<User> Users
{
get { return usersTable; }
}
public void CreateUser(AccountRegisterViewModel user)
{
User newUser = new User();
newUser.UserName = user.UserName;
newUser.Salutation = user.Salutation;
newUser.PhoneNumber = user.PhoneNumber;
newUser.SecondaryPhoneNumber = user.SecondaryPhoneNumber;
newUser.FirstName = user.FirstName;
newUser.LastName = user.LastName;
newUser.PasswordSalt = CreateSalt();
newUser.Password = CreatePasswordHash(user.Password, newUser.PasswordSalt);
newUser.Email = user.Email;
newUser.CreatedDate = DateTime.UtcNow;
newUser.Comments = "Created from web registration";
newUser.LastModifiedDate = DateTime.UtcNow;
newUser.LastLoginDate = DateTime.UtcNow;
newUser.IsActivated = true;
newUser.IsLockedOut = false;
newUser.MayContact = user.MayContact;
usersTable.InsertOnSubmit(newUser);
usersTable.Context.SubmitChanges();
}
}
So it appears to me as if the auth cookie I'm setting is fine, but either:
When I first go in to the membership provider's GetUser() method, it retrieves the wrong record from the database and therefore sets up a MembershipUser object with the wrong username; subsequently when I look in the database for "this" user I'm actually looking for the wrong username.
Or: Intermittently when I do userRepository.FirstOrDefault(x => x.UserName == Membership.GetUser().Name) it retrieves the wrong record.
Or: something else is going wrong that I haven't thought of.
As I say, this seems to be a problem when the site was under load, so I'm wondering if it's some sort of caching issue somewhere? But I really don't know.
One thought I had was to change the way I retrieve the user in case the problem lies with the membership provider, and use this instead:
userRepository.FirstOrDefault(x => x.UserName == User.Identity.Name)
// or HttpContext.Current.User.Identity.Name if not within a controller
But really I'm not even sure what's going on so have no idea whether this will resolve the issue. Could it be a caching problem somewhere? It appears (but I can't be 100% certain) that when user A could see user B's details, it was always the case that user B was also active in the system (or had been within the previous 20 minutes).
I know it's a long shot, but does anyone have any idea how this could happen? Obviously it's a major concern and needs to be fixed urgently, but without knowing why it's happening I can't fix it!
Thanks in advance for any help,
Nick
Some things to consider:
Instead of using FirstOrDefault, use SingleOrDefault. FirstOrDefault assumes there will be more than 1 record of data matching your query. Since you are querying by username, there should only be 1 matching row, correct? In that case, use SingleOrDefault instead. When there are multiple rows that match the query, SingleOrDefault will throw an exception.
To get the username, instead of invoking Membership.GetUser().UserName, use User.Identity.Name. The User property on an MVC controller references an IPrincipal that should match the user's forms authentication cookie value. Since you have a custom membership provider, this should help eliminate its methods as a source of the problem.
There could be a caching issue if you have caching set up for the MVC project. Do you use the OutputCacheAttribute ([OutputCache]) on any controllers or action methods? Do you have it set up as a global filter in the global.asax file? Or do you think there may be some kind of SQL-based caching going on?
Looking at your overridden GetUser method, I see it should take 2 parameters: string username and bool isOnline. However, when you invoke it with Membership.GetUser().UserName, you are passing no parameters. Do you have another overridden overload of this method that also takes no parameters? What does it look like? Does it use System.Threading.CurrentPrincipal.Identity.Name to sniff out the current username when none is passed?
I wrote the following code to log in to my application using C# and LINQ. It connected to a SQL service oriented database that I have created in Visual Studio. The problem that I am having is one that I do not understand and am hoping that someone can help me about here. I have created two message boxed to try to see the output of my code buy I am not getting anything from it.
If anyone could help that would be great!
public bool UserLogin(string User, string Pass)
{
var Database = new ExampleDataSet();
var query = from Employee in Database.Employee
where (Employee.EmployeeID.ToString() == Employee.ToLower() && Employee.Password == Pass)
select Employee;
if (query.Count() != 0)
{
return true;
MessageBox.Show("You are logged in");
}
return false;
MessageBox.Show("You are not logged in");
}
private void cmdLogin_Click(object sender, EventArgs e)
{
string User = (txtUser.Text);
string Pass = (txtPass.Text);
UserLogin(User, Pass);
}
From the code you wrote, it seems that the problem is that you compare the string representation of an Employee with its EmployeeId property Employee.EmployeeID.ToString() == Employee.ToLower(). This line will always return false unless you override ToString() method of Employee class to return the property EmployeeId (which I presume you didn't).
Try this instead (assuming that parameter User contains the name of the user):
using(var dataSet = new ExampleDataSet())
{
var loggedIn = dataSet.Employee.Any(e=>e.UserName == User && e.Password == Pass);
var message = loggedIn ? "You are logged in" : "You are not logged in";
MessageBox.Show(message);
return loggedIn;
}
When you return, the function execution will stop, because it's done and will return the value to what it was called from. So anything after your return won't happen. Try putting MessageBox.Show before return:
MessageBox.Show("You are logged in");
return true;
And the same for the false version.
The Message boxes wont show as they are after the return statements - move your message boxes to before the returns to see them.
I have made changes in your code....Your if-else part is not valid......You are comparign employee.ID with employee itself..Try out with following code.....
public bool UserLogin(string User, string Pass)
{
var Database = new ExampleDataSet();
var query = from Employee in Database.Employee
where (Employee.EmployeeID.ToString().ToLower().Equals(User.ToLower())&& Employee.Password.ToString().ToLower().Equals(Pass.ToLower())
select Employee;
if (query.Count() != 0)
{
MessageBox.Show("You are logged in");
return true;
}
else
{
MessageBox.Show("You are not logged in");
return false;
}
}
private void cmdLogin_Click(object sender, EventArgs e)
{
string User = (txtUser.Text);
string Pass = (txtPass.Text);
UserLogin(User, Pass);
}
If you mean user = employeeId
Employee.EmployeeID.ToString() == user.ToLower()
if not :
Employee.EmployeeName.ToLower() == user.ToLower()
About the message box, you should know any statement after return will not be executed
Writing your own user authentication is not recommended. It is very difficult to do it right, and in most cases you can use one that is already provided by Windows or the dot net framework.
For example, your application should not store the users passwords in plain text. If your application or database is compromised, the attacker will not only gain full access to your application, but a list of passwords which it is likely the users have also used elsewhere.
If you need to store the users credentials, they should first be salted, and then hashed with a secure hashing algorithm. This prevents anyone who gains access to the database from learning users passwords.
To verify the password, you look up the salt in the database, append the password to it, hash the result, and then compare that with the stored value. If they are the same, then the user entered the correct password.
If you are writing an application that will be used in a windows domain, you can use Active Directory groups to control access to your application. On the simplest level, you can store your application in a folder which is only accessible by the authorised users.
You can also use groups to control access to the database the application connects to. If you are using SQL server, each group should be placed in a SQL database role, which is in turn granted the necessary permissions for that role.
From within the application, you can look up the users group membership, and use it to determine which forms / menu options to show, or exit the application if they are not authorised.
If you are writing an ASP.Net application, consider using MemberShip and Roles which are built in to the framework.