I'm making a simple news feed where i enter a new item from a form hidden on a url that i manually need to type in (no account functionality). But i wanted a additional line of defense if the form is found so i added a password field so if the password match the preset i have then the form saves the data in to a xml file.
Now the question is, whats the best practice here to make that validation and where do i put the password?
At the moment my code looks like this:
[HttpPost]
[ValidateAntiForgeryToken()]
public ActionResult AddPost(AddPostModel model)
{
if (ModelState.IsValid && model.Password == "MyPassword")
{
AddPostModel.AddPostToXML(model);
}
return RedirectToAction("Index");
}
The thing is that its a small site and at worst they add news item that should not be there. So do i need to take additional precautions or is it secure enough for what its supposed to protect?
Since i'm quite new i don't have a lot of experience in security so any guidelines or what to keep in mind would also be much appreciated.
Thanks!
After some discussion i settled on having a hashed password in the web.config that i then check against to see if the password is the right one. Then during the check i just hash the entered password with the same function and check if its a match.
Here is the class i built if any one else is looking for something similar. =)
public class Security
{
public static bool ValidatePassword(string password)
{
string hashValue = HashPassword(password);
if (hashValue == ConfigurationManager.AppSettings["password"])
{
return true;
}
return false;
}
private static string HashPassword(string passwordToHash)
{
HashAlgorithm hash = new SHA256Managed();
byte[] plainTextBytes = System.Text.Encoding.UTF8.GetBytes(passwordToHash);
byte[] hashBytes = hash.ComputeHash(plainTextBytes);
//in this string you got the encrypted password
return Convert.ToBase64String(hashBytes);
}
}
I found useful link that might help you to have an idea about customising the security level http://www.c-sharpcorner.com/uploadfile/jitendra1987/password-validator-in-C-Sharp/
Have you looked at the Ajax toolkits!? They have good mechanisms to setup your first line of security defence i.e. length of password, adding complexity and other features. Please have a look at: http://www.ajaxcontroltoolkit.com/PasswordStrength/PasswordStrength.aspx
Related
I am new to C# and I am trying to add more than one element in the dictionary using user input. I tried different combinations, but still I can only see 1 entry when I test the program. You can find below my registration method which is a part of a 3 option menu. I am using the dictionary as a public class and created a property so I can use it in different methods. I don't know if my logic is not correct, but I would appreciate some feedback.
private static void Register()
{
userData = new Dictionary<string, string>();
//input for user to add username and password
string userName = "";
string password = "";
SystemMessage("Enter a Username :");
userName = Console.ReadLine();
SystemMessage("Enter a Password :");
password = Console.ReadLine();
//add username and password in to dictionary
userData.Add(userName, password);
//check that username is not the same aas the password for better security
if (userName.Equals(password))
{
ErrorMesssage("Username and Password cannot be the same");
}
if (userData.Equals(userName))
{
ErrorMesssage($"Username: {userName} already exist!");
}
else
{
//get count of key/value items
Console.WriteLine(userData.Count);
}
}
A quick approach would be to make you dictionary a static like
private static Dictionary<string, string> userData = new Dictionary<string, string>();
In you current implementation every time that you call the Register method you create a new Dictionary that is of course empty.
General feedback
You process your data in wrong order
userData.Add(userName, password);
//check that username is not the same aas the password for better security
if (userName.Equals(password))
{
ErrorMesssage("Username and Password cannot be the same");
}
if (userData.Equals(userName))
{
ErrorMesssage($"Username: {userName} already exist!");
}
You first make the validation checks (username same as password and key exists) and then add to distionary.
In order to check if you have an entry in your dictionary you do not use Equal method but userData.ContainsKey(userName)
So the code should look like
//check that username is not the same aas the password for better security
if (userName.Equals(password, StringComparison.OrdinalIgnoreCase))
{
ErrorMesssage("Username and Password cannot be the same");
}
if (userData.ContainsKey(userName))
{
ErrorMesssage($"Username: {userName} already exist!");
}
else
{
//get count of key/value items
Console.WriteLine(userData.Count);
}
userData.Add(userName, password);
Since it is a password, even for a uni project it might be a good idea to use some hashing at least so it is not plane test before you persist it somewhere
You are redefining the dictionary every time you call Register()
Redefine the userName Dictionary outside of the method, either in main() or outside of any method globally. This will sort out your issue.
I'm trying to implement signed URLs for short lived access to static files.
The idea is:
generate an URL with an expiration timestamp (e.g. https://example.com/file.png?download=false&expires=1586852158)
sign it with HMACSHA256 and a shared secret and append the signature at the end of URL (e.g. https://example.com/file.png?download=false&expires=1586852158&signature=6635ea14baeeaaffe71333cf6c7fa1f0af9f6cd1a17abb4e75ca275dec5906d1
When i receive the request on the server, I take out the signature parameter and verify that the rest of the URL signed with HMACSHA256 and the same shared secret results in the same signature.
The implementation is as follows:
public static class URLSigner
{
private static string GetSignatureForUri(string uri, byte[] key)
{
var hmac = new HMACSHA256(key);
var signature = hmac.ComputeHash(Encoding.UTF8.GetBytes(uri));
var hexSignature = BitConverter.ToString(signature).Replace("-", string.Empty).ToLowerInvariant();
return hexSignature;
}
public static string SignUri(string uri, byte[] key)
{
var hexSignature = GetSignatureForUri(uri, key);
return QueryHelpers.AddQueryString(uri, new Dictionary<string, string> { { "signature", hexSignature }});
}
public static bool VerifyUri(string uri, byte[] key)
{
var signatureRegex = "[\\?&]signature=([a-z0-9]+)$";
var signatureMatch = Regex.Match(uri, signatureRegex);
if (!signatureMatch.Success || signatureMatch.Groups.Count != 2)
return false;
var parsedSignature = signatureMatch.Groups[1].Value;
var originalUri = Regex.Replace(uri, signatureRegex, "");
var hexSignature = GetSignatureForUri(originalUri, key);
return hexSignature == parsedSignature;
}
}
and it's used like so:
var signedUri = URLSigner.SignUri("https://example.com/file.png?download=false", secretKey);
var isVerified = URLSigner.VerifyUri(signedUri, secretKey);
Is this implementation of signed URLs reasonably secure?
Your implementation seems to be missing the verification of the expiration time, so any one key would currently work indefinitely.
Otherwise, I don't see anything wrong with this approach in general. You may want to add in a key beyond just the timestamp for identifying the user or request in some way though.
Here's a good article on how the general approach is used for one time passwords which is essentially what you are doing.
https://www.freecodecamp.org/news/how-time-based-one-time-passwords-work-and-why-you-should-use-them-in-your-app-fdd2b9ed43c3/
Yes, it is secure, as long as the key is treated properly. The hash should be able to ensure data integrity (data in URL are not modified by other people).
Perhaps, one little improvement is to dispose the HMACSHA256 object (maybe by using), but that may not be related to security.
I have one concern. You are saying you want to use HMACSHA256 and a private key, but in security terminology what you're passing to the HMAC is not a private key, it's a shared secret.
If you have to had a public, private key for your sign and verify authentication, I would suggest using the RSACryptoServiceProvider. With RSA you have two keys, public key and private key.
Your client creates a private key and keep it and give its public key to the server. So only client can sign and anyone with public key can verify it.
On another note, no matter what algorithm you ended up using, I would suggest to add the signature to a authorization header instead of query string. This is more common and you don't need to match a regex in each request.
I have model like below:
public class User
{
private string password;
public string Password
{
get { return Decrypt(password); }
set { password = Encrypt(value); }
}
}
I want from user code to use just insert and select password easily and see in clear text and want the logic layer to handle the encryption and decryption.
But when selecting, EF will set the value again so the encrypted password gotten from database will be encrypted again for me to get from the client code which is a problem. I also cannot distinguish between an Insert and Select to set conditionals.
I can very well do this:
//Insert
user.Password = Encrypt("123"); //Encrypt or Hash does not matter
context.Users.Add(user);
//Select
var hashedpassword = context.Users.Find(1).Select(u => u.Password).Single();
var unhashed = Decrypt(hashedpassword);
Instead I would like it not to be apparent from client code:
//Insert
user.Password = "123"; //Inserts encrypted password in database
context.Users.Add(user);
//Select
var cleartextpassword = context.Users.Find(1).Select(u => u.Password).Single();
Hope I was able to explain the problem and someone can point me to the right direction. If it is even possible or not.
The better solution is indeed to use a Hash.
But a general pattern for injecting some logic:
public class User
{
// the mapped-to-column property
protected virtual string PasswordStored
{
get ;
set ;
}
[NotMapped]
public string Password
{
get { return Decrypt(PasswordStored); }
set { PasswordStored = Encrypt(value); }
}
}
Instead of [NotMapped] you can also use the fluent API to keep EF from storing it directly.
Read and write your entities only from a repository, i.e., through a data access class that you write:
var myEntities = userRepository.GetUserById(id);
then your GetUserById() method can perform the decryption in-place. Then when you do
userRepository.UpdateUser(myUser);
you can encrypt the field again in the UpdateUser() method.
Please find following method which will get you a password hash:
public static string GetPasswordHash(this string password)
{
using (var sha1 = new SHA1Managed())
{
var hash = Encoding.UTF8.GetBytes(password);
var generatedHash = sha1.ComputeHash(hash);
var generatedHashString = Convert.ToBase64String(generatedHash);
return generatedHashString;
}
}
I want apply the RSA encryption to my project, but encountered some troubles:
First, I have download the JavaScripts library from
http://www.ohdave.com/rsa/ ,and add reference to my project;
Second, I have define the RSA object and code to initialize that:
internal RSACryptoServiceProvider Rsa
{
get
{
if (HttpContext.Cache["Rsa"] != null)
{
RSACryptoServiceProvider encryptKeys = (RSACryptoServiceProvider)HttpContext.Cache["Rsa"];
return encryptKeys;
}
else
{
return new RSACryptoServiceProvider(1024);
}
}
set
{
HttpContext.Cache.Remove("Rsa");
HttpContext.Cache.Insert("Rsa", value);
}
}
public ActionResult SignUp()
{
this.Rsa = Security.GetRsa();
RSAParameters param= this.Rsa.ExportParameters(true);
//this will bind to view
TempData["exponent"] = Util.BytesToHexString(param.Exponent);
TempData["key"] = Util.BytesToHexString(param.Modulus);
UserInfo user = new UserInfo();
user.Birthday = DateTime.Now.Date;
return View(user);
}
private RSACryptoServiceProvider GetRsa()
{
RSACryptoServiceProvider Rsa = new RSACryptoServiceProvider(1024);
return Rsa;
}
3.then, on JavaScript side , I have code, it encrypt the password user input and the bind it control:
var hash = document.getElementById("Pwd").value;
var exponent = document.getElementById("exponent").innerHTML;
var rsa_n = document.getElementById("key").innerHTML;
setMaxDigits(131);
var key = new RSAKeyPair(exponent, "", rsa_n);
hash = encryptedString(key, "111");
document.getElementById("Pwd").value = hash;
document.getElementById("Pwd2").value = hash;
document.getElementById("error").innerHTML = "";
document.getElementById("submit").click();
4.when user click submit, my C# code get the encrypted pwd string and try to decrypt it but failed with exception: bad data:
[HttpPost]
public ActionResult SignUp(UserInfo user)
{
user.UserId = user.UserId.ToLower(); //ignore case
user.UserGUID = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.IsEnabled = false;
user.Pwd = Convert.ToBase64String(Rsa.Decrypt(Util.HexStringToBytes(user.Pwd), false));//Exception:Rsa.Decrypt throw bad data exception
who do you know how to fix it? thank you in advance.
I had a very similar problem in that most of the JavaScript based RSA encryption solutions wasn't "compatible" with .NET's implementation.
Almost all the implementations I found online had one or both of the following items causing the incompatibility with .NET's implementation.
The byte order encoding in JavaScript is different to the byte order that .NET used. This is a biggie as for example a string is represented with a different order of bytes in JS than it is in .NET so you'll need to convert before encrypting and after decrypting. I believe it's enough to just reverse the byte order to match .NET, but don't quote me on that.
Padding was different: .NET uses OAEP padding by default for RSA so the JS implementation of RSA should support the same padding too. I believe OAEP padding is also called PKCS#1 v2.0 padding, but don't quote me on that either.
Aside: I found an amazing JS library, called JavaScript.NET (from jocys.com) that mirrors tons of the .NET BCL functionality, including the RSA implementation, such that I could even use similar classes, properties and methods. Have a look at this. I can confirm it works with .NET RSA implementation. Give it a go - here are some links for it:
Jocys JS.NET Code Project demo
Jocys JS.NET Download
Hth
I am using:
http://mvcrecaptcha.codeplex.com/
My problem is very simple!
bool captchaValid
always returns false, no matter what I do.
Here is my code:
[CaptchaValidator]
[HttpPost]
public ActionResult ViewWidget(int id, TagwallViewModel model, bool captchaValid)
{
model.TagwallCollection = new TagWallCollection() { Id = id };
if (!captchaValid)
{
ModelState.AddModelError("_FORM", "You did not type the verification word correctly. Please try again.");
}
else
It shows no errors..
Things i have done differently, but i think have no influence:
The cs files downloaded from codeplex is not in the same folders.
I registered on https://www.google.com/recaptcha/admin/create to get my two keys with a online domain, but i'm testing it on localhost.
That was my problem, sorry for troubleing you! Having a bad code day.
I am using Razor.