Using Jasypt: The checkPassword method is returning false when the passwords should match. - sql

To give you some background, my team and I are creating a program that stores usernames and passwords in a database. We are using Java and interacting with the Database through java code.
We use Jasypt to encrypt the usernames and passwords. I am using the BasicPasswordEncryptor in Jasypt to encrypt both. The usernames encrypt fine and are stored in the database fine. However, when the login is checked and said BasicPasswordEncryptor attempts to check the plaintext username against the encrypted password, it always returns false. I have done a series of checks to focus down where the problem is occuring. As far as I know, it's a problem with Jasypt. Does anyone know what the problem is, a possible solution, or a more optimal method? Thank you. I will post the code.
Here is where the encryption occurs.
public void register(String userName, String passWord){
String encryptedUsername = e.encryptPassword(userName);
String encryptedPassword = e.encryptPassword(passWord);
System.out.println("Registered eU: " + encryptedUsername);
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/PandaBox", "root", "");
statement = con.prepareStatement("insert into Users (username, password, logged) values (?,?,?)");
statement.setString(1, encryptedUsername);
statement.setString(2, encryptedPassword);
statement.setInt(3, 0);
boolean x = statement.execute();
System.out.println("IT REGISTERED");
} catch (SQLException o) {
o.printStackTrace();
}
}
Where "e" is the BasicPasswordEncryptor object. Here is the login check.
public boolean checkLogin(String inputedUsername, String inputedPassword) {
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/PandaBox", "root", "");
statement = con.prepareStatement("select * from Users");
rs = statement.executeQuery();
System.out.println(inputedUsername + " / " + inputedPassword);
while(rs.next()){
String usernameInDatabase = rs.getString("username");
System.out.println(usernameInDatabase);
if (e.checkPassword(inputedUsername, usernameInDatabase)) {
System.out.println("Username correct.");
statement = con.prepareStatement("select password from Users where username = ?");
statement.setString(1, usernameInDatabase);
rs = statement.executeQuery();
String passwordInDatabase = rs.toString();
if(passwordIsCorrect(inputedPassword, passwordInDatabase)){
return true;
}
}
}
return false;
} catch (SQLException o) {
// TODO Auto-generated catch block
o.printStackTrace();
return false;
}
}

I'm jasypt's author.
From your message, it isn't clear to me whether you are observing this issue when matching the user name or the password --you say 'attempts to check the plaintext username against the encrypted password', which makes no sense--. Nevertheless, one of the most common reasons for problems like yours is that your database columns are not big enough for storing your hashed user names and/or passwords.
The size of the hashing result will depend on the algorithm and salt configuration being used, but for a BasicPasswordEncryptor, which uses MD5 and a salt size of 8 bytes, you should expect your hashes to be 16-byte (hash) plus 8 bytes (salt), plus 8 additional bytes because of textual Base64 encoding. A total of 32 bytes.
Also think that many DBMS measure varchar fields in chars and not bytes, so you should do the appropiate conversion depending on the character encoding being used at your table.
I always recommend to check column sizes first because many DBMS's do not raise an error if you try to store a varchar which is too long for a column --they simply truncate it. I don't know MySQL's behaviour, but Oracle does exactly this. And when you try to decrypt it back... it doesn't match.
So checking your column sizes could be a good starting point. And remember jasypt has a users forum at http://forum.jasypt.org
Oh, and by the way-- forgive me if this is just ad-hoc demo code, but just in case: you should make sure you close all your Statement and ResultSet objects in 'finally' blocks before reusing them... so you should use different 'statement' and 'rs' variables in the inner iteration block, and close them each time.
Regards.

Optimisation 1 : Use a WHERE clause.

Related

Do strings need to be escaped inside parametrized queries?

I'm discovering Express by creating a simple CRUD without ORM.
Issue is, I'm not able to find any record through the Model.findBy() function
model User {
static async findBy(payload) {
try {
let attr = Object.keys(payload)[0]
let value = Object.values(payload)[0]
let user = await pool.query(
`SELECT * from users WHERE $1::text = $2::text LIMIT 1;`,
[attr, value]
);
return user.rows; // empty :-(
} catch (err) {
throw err
}
}
}
User.findBy({ email: 'foo#bar.baz' }).then(console.log);
User.findBy({ name: 'Foo' }).then(console.log);
I've no issue using psql if I surround $2::text by single quote ' like:
SELECT * FROM users WHERE email = 'foo#bar.baz' LIMIT 1;
Though that's not possible inside parametrized queries. I've tried stuff like '($2::text)' (and escaped variations), but that looks far from what the documentation recommends.
I must be missing something. Is the emptiness of user.rows related to the way I fetch attr & value ? Or maybe, is some kind of escape required when passing string parameters ?
"Answer":
As stated in the comment section, issue isn't related to string escape, but to dynamic column names.
Column names are not identifiers, and therefore cannot be dynamically set using a query parameter.
See: https://stackoverflow.com/a/50813577/11509906

Autosuggestion on encrypted Data(AES-256 GCM Mode)

For PII purposes, we are encrypting the database fields like email etc.
Now for exact match queries we are also keeping a hashed form(HMAC) for the fields.
But how to run the autosuggestion from Solr / like queries from MySQL.
My encryption code is
public String encrypt(byte[] plaintext, byte[] dataKey, String version) throws Exception {
long startTime = System.currentTimeMillis();
// Generate Initialization Vector
byte[] IV = generateIV();
// Get Cipher Instance
Cipher cipher = getCipher();
// Store Version
byte[] versionArr = new byte[3];
versionArr = version.getBytes();
// Generate Key
SecretKeySpec keySpec = new SecretKeySpec(dataKey, "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE_BYTES * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
int capacity = 3 + GCM_IV_SIZE_BYTES + plaintext.length + GCM_TAG_SIZE_BYTES;
// Create ByteBuffer & add IV & CipherText
ByteBuffer buffer = ByteBuffer.allocate(capacity);
buffer.put(versionArr);
buffer.put(IV);
buffer.put(cipherText);
long endTime = System.currentTimeMillis();
// return the final encrypted cipher txt
return Base64.getEncoder().encodeToString(buffer.array());
}
private static byte[] generateIV() {
final Random r = new SecureRandom();
byte[] IV = new byte[GCM_IV_SIZE_BYTES];
r.nextBytes(IV);
return IV;
}
private static Cipher getCipher() {
try {
return Cipher.getInstance("AES/GCM/NoPadding");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
return null;
}
Short answer, it’s possible, but quite difficult.
Long answer:
One of the basics of hashes is that the hash changes a lot, by changing the input only a little, so there is no way to know if a hash is close to matching the input value.
Now you might think inputting the hash into the search engine might work and with some customization to the tokenizer (the thing that takes the search input and splits it into small parts for the engine to match), this would actually work. However, you did just make it possible to reverse the hash, let me explain:
For autocomplete of a single field, the tokenizer would use an edge n-gram. What this does is splitting a single string into multiple tokens that can be exactly matched, for example john#gmail.com would be split into:
j, jo, joh, john, john#, …, john#gmail.c, john#gmail.co, john#gmail.com
Now the search engine can search for exact matches on all the tokens and recommend the closest matches as possible values.
If you would customize the tokenizer, to hash everything before it is stored, and then hased the input upon search, this would absolutely work, but now lets look at what happens when an attacker gets the hashed tokenized data.
As in the example with john#gmail.com the first stored value would be the hash of j, which by brute force would take a fraction of a second to reverse. Since you now know the first letter, the second letter would go just as fast and so on. Rendering the whole point of using the hash in the first case obsolete.
ps. A secure hashing algorithm with a salt might work, but the tokenization is almost always done by the search engine, so the compute part of the search engine has to be "secure"
Sources:
https://en.wikipedia.org/wiki/N-gram

Checking if a user and a password introduced in a form exist on SQL database

First post here so, first of all, thanks for the existence of this site since it has helped me lots so far.
Right now I'm making a project (C#) that requires a user to login. The profiles are stored on an SQL base and I need to check if the credentials (user/pass) are correct to allow the login.
I understand very little of SQL, so bare with my errors, please.
The form is working quite nicely. What doesn't work is the data check on my database. So, when I press login the form calls the following function:
public bool ValidacaoLogin(string user, string pass)
cn.Open();
cmd = new SqlCommand("select * from BDPerfil left join BDPerfilTecnico on BDPerfil.USER = BDPerfilTecnico.USER where BDPerfil.USER = #USER;", cn);
cmd = new SqlCommand("select * from BDPerfil where NIF = '" + user + "' AND PASSWORD = '" + pass + "';", cn);
cmd.Parameters.AddWithValue("#USER", user);
cmd.Parameters.AddWithValue("#PASSWORD", pass);
dr = cmd.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read())
{
if ("#USER" == user)
{
if ("#PASSWORD" == pass)
dr.Close();
cn.Close();
return true;
}
}
}
dr.Close();
cn.Close();
return false;
}
I have 2 tables to store different profiles: BDPerfilTecnico and BDPerfilAssist so I want my program to check both (through join) although right now I don't need to use BDPerfilAssist since all my test profiles are on BDPerfilTecnico.
Help?
Thanks in advance.
At the very least, you need the parameters in the query. I would expectd:
cmd = new SqlCommand("select * from BDPerfil where NIF = #USER AND PASSWORD = #PASSWORD'", cn);
You are onto a good path using parameters for the queries. You just need them in the query itself.
Note: There may be other issues in the code as well.
The results back will be in the dr variable and will depend on what the columns are in the table. Instead of * you should probably list the table columns that you want i.e. User,password. Then you can use the dr variable to get these values with statements like dr.getString(0) to get user and dr.getString(1) for password. The variables user and password will not get changed by the query in your code. Also the literal strings "#user" and "#password" won't come out of the query so I don't know why you would compare to them. Instead you want to do something like. If (dr.getString(0)==user)
Please please encrypt the password in the database. You can encrypt what the user types and compare encrypted password to encrypted password.
Good luck

[Sql-Server]what data type to use for password salt and hash values and what length?

I am generating salt and hash values from my passwords by using,
string salt = CreateSalt(TxtPassword.Text.Length);
string hash = CreatePasswordHash(TxtPassword.Text, salt);
private static string CreateSalt(int size)
{
//Generate a cryptographic random number.
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[size];
rng.GetBytes(buff);
// Return a Base64 string representation of the random number.
return Convert.ToBase64String(buff);
}
private static string CreatePasswordHash(string pwd, string salt)
{
string saltAndPwd = String.Concat(pwd, salt);
string hashedPwd =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPwd, "sha1");
return hashedPwd;
}
What datatype you would suggest for storing these values in sql server? Any suggestion...
Salt:9GsPWpFD
Hash:E778AF0DC5F2953A00B35B35D80F6262CDBB8567
ASPNET_DB says this - can't go wrong.
Password nvarchar(128) NOT NULL,
PasswordSalt nvarchar(128) NOT NULL,
while 128 may seem like a lot, various types of encryption can result in larger strings than you started out with. There is absolutely no reason not to follow the lead of the very smart people who have spend thousands of man hours developing the asp.net membership system.
We store our passwords as a binary SHA512 hash

figuring out reason for maxClauseCount is set to 1024 error

I've two sets of search indexes.
TestIndex (used in our test environment) and ProdIndex(used in PRODUCTION environment).
Lucene search query: +date:[20090410184806 TO 20091007184806] works fine for test index but gives this error message for Prod index.
"maxClauseCount is set to 1024"
If I execute following line just before executing search query, then I do not get this error.
BooleanQuery.SetMaxClauseCount(Int16.MaxValue);
searcher.Search(myQuery, collector);
Am I missing something here? Why am not getting this error in test index?The schema for two indexes are same.They only differ wrt to number of records/data.PROD index has got higher number of records(around 1300) than those in test one (around 950).
The range query essentially gets transformed to a boolean query with one clause for every possible value, ORed together.
For example, the query +price:[10 to 13] is tranformed to a boolean query
+(price:10 price:11 price:12 price:13)
assuming all the values 10-13 exist in the index.
I suppose, all of your 1300 values fall in the range you have given. So, boolean query has 1300 clauses, which is higher than the default value of 1024. In the test index, the limit of 1024 is not reached as there are only 950 values.
I had the same problem. My solution was to catch BooleanQuery.TooManyClauses and dynamically increase maxClauseCount.
Here is some code that is similar to what I have in production.
private static Hits searchIndex(Searcher searcher, Query query) throws IOException
{
boolean retry = true;
while (retry)
{
try
{
retry = false;
Hits myHits = searcher.search(query);
return myHits;
}
catch (BooleanQuery.TooManyClauses e)
{
// Double the number of boolean queries allowed.
// The default is in org.apache.lucene.search.BooleanQuery and is 1024.
String defaultQueries = Integer.toString(BooleanQuery.getMaxClauseCount());
int oldQueries = Integer.parseInt(System.getProperty("org.apache.lucene.maxClauseCount", defaultQueries));
int newQueries = oldQueries * 2;
log.error("Too many hits for query: " + oldQueries + ". Increasing to " + newQueries, e);
System.setProperty("org.apache.lucene.maxClauseCount", Integer.toString(newQueries));
BooleanQuery.setMaxClauseCount(newQueries);
retry = true;
}
}
}
I had this same issue in C# code running with the Sitecore web content management system. I used Randy's answer above, but was not able to use the System get and set property functionality. Instead I retrieved the current count, incremented it, and set it back. Worked great!
catch (BooleanQuery.TooManyClauses e)
{
// Increment the number of boolean queries allowed.
// The default is 1024.
var currMaxClause = BooleanQuery.GetMaxClauseCount();
var newMaxClause = currMaxClause + 1024;
BooleanQuery.SetMaxClauseCount(newMaxClause);
retry = true;
}
Add This Code
#using Lucene.Net.Search;
#BooleanQuery.SetMaxClauseCount(2048);
Just put, BooleanQuery.setMaxClauseCount( Integer.MAX_VALUE );and that's it.