How to save cipher text to db if it has illegal characters? - mysql

I am working on encrypting text strings that contain sensitive data. I need to save these encrypted strings to a MySQL db. The strings are cipher text, and all character (printable ASCII to control characters to null are equally likely).
These strings are not long (< 40 characters). I am using Ruby 2.1 (no Rails) along with the encryptor gem with a custom salt and iv to do the encryption. The encryptor gem is a wrapper for Ruby's openssl.
For many strings this works fine. However, I have run into a small number of these strings that, once encrypted, contain illegal or improperly quoted characters. As a result, when the string is saved I get an error.
What is the best way to handle the encrypted value so it can be reliably saved to MySQL?
Here is my encryption command:
require 'encryptor'
encrypted_value = Encryptor.encrypt(#sensitive_string,
:key => #config["encryption"]["key"],
:iv = #config["encryption"]["iv"],
:salt => #config["encryption"]["salt"])
Here is the encrypted value:
encrypted_value: /:Z`߉Nc??"v'??\??؟??????Oa?jR
and a screenshot since some of the characters did not copy correctly:
MySQL update statement:
query = "UPDATE db.table
SET `key` = mysql_real_escape_string(#{encrypted_value})"
With the value in the query it looks like:
query UPDATE db.table
SET `key` = mysql_real_escape_string(/:Z`߉Nc??"v'??\??؟??????Oa?jR)
I have tried both the MySQL Quote and mysql_real_escape_string functions. I get the same error with both and also wrapping the encrypted_value in double and single quotes.
Then I get this error:
wrong number of arguments (0 for 2) (ArgumentError)
What is the best way to tackle this? Any advice is appreciated.

You can perform base64 encoding of encrypted string, and store the encoded value in DB.
When retrieving the value, you can decode it back to binary and decrypt it.
require "base64"
enc = Base64.encode64('Send reinforcements')
# -> "U2VuZCByZWluZm9yY2VtZW50cw==\n"
plain = Base64.decode64(enc)
# -> "Send reinforcements"
http://ruby-doc.org/stdlib-2.2.0/libdoc/base64/rdoc/Base64.html

Related

Store AESCipher object in mySQL

I'm encrypting some user data (no passwords) using Crypto.Cipher AES.
The return is a AESCipher of the form:
b'o\xab\xdd\x19\xaat\xfcIAN\xd2\x00\xe9'
sometimes it produces spaces and non hex representations.
b"N%?\x91\xe8'J\xc0\x10 p"
b'QV8>K\xd8\xfa\x9a\x05%\xe8LJp\xd0gf'
When I try to insert these values into the SQL table created as:
data_encrypted VARBINARY(40)
I get the following warning:
Warning: (1300, "Invalid utf8 character string: 'ABDD19'")
Seems like it's trimming the binary array, when I Query the inserted rows in the table, the row was effectively inserted but only the first bytes of the array, from b'o\xab\xdd\x19\xaat\xfcIAN\xd2\x00\xe9' it inserts only the 'o'.
Do I have to specify something else in the format?
Thanks
Adding a _binary solved the warning
self.cur.execute("INSERT IGNORE INTO members VALUES(%s,_binary %s...
And I'm also formatting the array to be hex represented, it looks better in the MySQL table and the decryption still works ok binascii.b2a_hex

MySQL statement fails due to encoded quotes

Following on from this question MySQL database contains quotes encoded and unencoded and it's breaking javascript
I am executing this MySQL query:
DELETE FROM `example` WHERE `name` = ''12345''
However it fails because the value in the database is '12345'. It seems that old data in the database has a mixture of encoded and unencoded quotes. Is it safe to to update all ' to ' in the database?
In most cases (yours included), store text without any "encoding". That is, do not store htmlentities, store the actual characters, do not store unicode 'codes', store the actual characters, etc.
Do likewise for anything you need to compare to what is in the database.
You will, however, have to escape strings when building SQL statements. Otherwise, you can't get quotes (in text) inside quotes (that are part of the SQL syntax.
That is, you will end up with this SQL when searching for that Irishman:
... WHERE `name` = 'O\'Brian'

String to Blob comparison in MySQL where clause

I have a table called messages with a column (BLOB) called message_text. I'm passing a value from the web app when a user sends a new message and I want to check if it's an exact duplicate message text.
SELECT count(message_id) FROM messages WHERE message_text = '$msgTxt' AND user_id = $userId
Where $msgTxt will be a formatted string like...
"Hello there. I don\'t know you.
I\'ve just made a new line. "
The problem is that the comparison isn't working and I'm never finding duplicates. Even if I literally copy/paste an existing value from the database and replace it with $msgTxt in my query I never get any results, and so I'm assuming there's something wrong with the way I'm comparing a blob to a string.
BLOB values are treated as binary strings (byte strings). They have the binary character set and collation, and comparison and sorting are based on the numeric values of the bytes in column values. String or Text values are treated as nonbinary strings (character strings). They have a character set other than binary, and values are sorted and compared based on the collation of the character set.
So, you have to convert either BLOB to String or String to BLOB and then compare both.
If you are using java,
Convert Blob to String
byte[] bdata = blob.getBytes(1, (int)blob.length());
String data1 = new String(bdata);
What API are you using to call MySQL? I see some backslashes, but need to verify that \ is not turning into \\, and that other escapings are not unnecessarily happening or not happening.
Which OS are you using? Windows, when reading stuff, likes to convert NL into CRLF, thereby making it so that it won't match.

How can I insert arbitrary binary data into a VARCHAR column?

I have a MySQL table with a VARCHAR(100) column, using the utf8_general_ci collation.
I can see rows where this column contains arbitrary byte sequences (i.e. data that contains invalid UTF8 character sequences), but I can't figure out how to write an UPDATE or INSERT statement that allows this type of data to be entered.
For example, I've tried the following:
UPDATE DataTable SET Data = CAST(BINARY(X'16d7a4fca7442dda3ad93c9a726597e4') AS CHAR(100)) WHERE Id = 1;
But I get the error:
Incorrect string value: '\xFC\xA7D-\xDA:...' for column 'Data' at row 1
How can I write an INSERT or UPDATE statement that bypasses the destination column's collation, allowing me to insert arbitrary byte sequences?
Have you considered using one of the Blob data types instead of varchar? I believe that this'd take a lot of the pain away from your use-case.
EDIT: Alternatively, there is the HEX and UNHEX functions, which MySQL supports. Hex takes either a str or a numeric argument and returns the hexadecimal representation of your argument as a string. Unhex does the inverse; taking a hexadecimal string and returning a binary string.
The short answer is that it shouldn't be possible to insert values with invalid UTF8 characters into VARCHAR column declared to use UTF8 characterset.
That's the design goal of MySQL, to disallow invalid values. When there's an attempt to do that, MySQL will return either an error or a warning, or (more leniently?) silently truncate the supplied value at the first invalid character encountered.
The more usual variety of characterset issues are with MySQL performing a characterset conversion when a characterset conversion isn't required.
But the issue you are reporting is that invalid characters were inserted into a UTF8 column. It's as if a latin1 (ISO-8859) encoding was supplied, and a characterset conversion was required, but was not performed.
As far as working around that... I believe it was possible in earlier versions of MySQL. I believe it was possible to cast a value to BINARY, and then warp that in CONVERT( ... USING UTF8), and MySQL wouldn't perform a validation of the characterset. I don't know if that's still possible with the current MySQL Connectors.
If it is possible, then that's (IMO) a bug in the Connector.
The only way I can think of getting around that characterset check/validation would be to get the MySQL sever to trust the client, and determine that no check of the characterset is required. (That would also mean the MySQL server wouldn't be doing a characterset conversion, the client lying to the server, the client telling the server that it's supplying valid UTF8 characters.
Basically, the client would be telling the server "Hey server, I'm going to be sending UTF8 character encodings".
And the server says "Okay. I'll not do any characterset conversion then, since we match. And I'll just trust that what you send is valid UTF8".
And then the client mischievously chuckles to itself, "Heh, heh, I lied. I'm actually sending character encodings that aren't valid UTF8".
And I think it's much more likely to be able to achieve such mischief using prepared statements with the old school MySQL C API (mysql_stmt_prepare, mysql_stmt_execute), supplying nvalid UTF8 encodings as values for string bind parameters. (The onus is really on the client to supply valid values for bind parameters.)
You should base64 encode your value beforehand so you can generate a valid SQL with it:
UPDATE DataTable SET Data = from_base64('mybase64-encoded-representation-of-my-value') WHERE Id = 1;

MD5 function returns empty result

I am trying to return an MD5 encoded string of a value from my database, but it just returns a blank result (not null, just blank).
I have tried just running this query and get the same result:
SELECT MD5('test');
I have tried restarting the MySQL server, MySQL Workbench, etc. But get the same result.
If I try running the same command on a different database/server, it returns the hash string just fine.
What am I doing wrong? Is there a setting I disabled on accident?
Prior to MySQL v5.5.3, MD5() returned a binary string.
By default, MySQL Workbench does not display binary strings (to avoid accidental misinterpretation); however it is possible to display binary string values in output grids: View > Edit > Preferences > SQL Editor > Treat BINARY/VARBINARY as nonbinary character string.
Alternatively, either upgrade your MySQL server or transcode the result to a non-binary character set:
SELECT CONVERT(MD5('test') USING utf8)