Project 04: Building a secure messaging system
In this project, I've extended the set of features for the secure login website to include messaging.
The website is being hosted on cmpsec.johnbojorquez.com.
The website supports single-recipient messages between users. All messages are sent over an SSL connection between the client and the server. Messages are also encrypted before they are stored in the database.
To create the application's database, messages table, users table, and initial users we can run
in the application directory.
The database initialization script in this project will create a
messages table. The scheme of the
messages table is as follows:
This project makes use of the MVC design pattern and new messages are inserted into the database as follows:
$m = new SecureMessage; $m->message = "Hello, world!"; $m->sender_id = 1; $m->receiver_id = 2; $m->save();
It's important to notice that the
SecureMessage::save() method will encrypt the message prior to inserting into
SecureMessage class also supports several static methods for retrieving users from the
users table in the form of a
Protecting the contents of the
The goal of this project is to devise a strategy for securely storing messages on the server. This is done by encrypting the messages before storing them in the
messages table. I have adopted the following strategy. When a user submits a message to be "sent" to another user, two 16-byte keys are generated:
$integrity_key = openssl_random_pseudo_bytes(16); $encryption_key = openssl_random_pseudo_bytes(16);
A 64-byte keyed hash is generated using the SHA256 hashing algorithm:
$message_hash = hash_hmac(Config::hash_algo, $this->message, $integrity_key);
This hash is stored in the database to protect the integrity of the message. That is, any unauthorized changes to the message itself can be easily detected. Next, we generate a random 16-byte IV and encrypt the message and its hash using the AES-128-CBC encryption algorithm.
$message_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(Config::encr_algo)); $ciphertext = openssl_encrypt($this->message . $message_hash, Config::encr_algo, $encryption_key, 0, $message_iv);
Lastly, the keys are encrypted using a secret 16-byte key stored on the server.
$keys_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(Config::encr_algo)); $enc_keys = openssl_encrypt($integrity_key . $encryption_key, Config::encr_algo, Config::secret_key, 0, $keys_iv);
To retrieve a message from the database, its keys are decrypted using the system key. Then the message itself is decrypted using its own key.
Detecting changes to the contents of a message
When retrieving entries from the
messages table, the hash is recomputed by hashing the message content using the integrity key, the produced hash is then compared with the hash that is stored in the database. If the hashes differ, the message has been altered and is thus discarded.
Protection against man-in-the-middle attacks
To ensure that the communication between the client and the server is protected, apache has been configured to used HTTPS (with the SSL protocol) for all communication. Using HTTPS requires the exchange of an SSL certificate. For the purpose of this project, a self-signed certificate was generated and stored on the server. This will ensure that all communication between the user's browser and the server is securely encrypted using public key encryption.
The downside of using a self-signed certificate is that the HTTPS connection, although encrypted, does not appear as a secure connection to the user's browser. This is because the certificate is not signed by a trusted authority. Although this still prevents against man-in-the-middle attacks with exchanging sensitive user-related information after the SSL handshake, this still opens the possibility of a man-in-the-middle attack during the handshake. There is no way for the user's browser to prevent attackers from providing their own certificate and tricking the user into giving the attacker their account login information. The solution to this problem is to purchase a signed certificate from a trusted authority.