Laravel use secure SSL connection while using AWS RDS
Amazon Relational Database Service Amazon RDS is a cloud based web service that makes things easier to set up, operate, and scale a relational databases on the cloud. It has become one of the popular choices when setting up laravel database infrastructure.
- Quick question :
If you are using AWS RDS in your laravel application, is your connection encrypted in transit? Or so ask other way around, is your laravel application connecting to AWS RDS using a secure SSL connection?
If you think that RDS comes up with secure encryption in transit, you are right, it implements SSL. However, is it turned on somehow by default or is it there when we use it directly? Not really.. I had a perception that my laravel app is secured in transit by SSL encryption until I found out its not.
- Let's find out :
Connect to the environment/server/container where laravel is hosted and run tinker :
php artisan tinker
Once, the tinker prompt is open, run following :
>>> DB::select("SHOW STATUS LIKE 'Ssl_cipher'")
If it gives output like following :
=> [
{
+"Variable_name": "Ssl_cipher",
+"Value": "DHE-RSA-AES128-SHA",
},
]
Then laravel application is connecting to AWS RDS via a secure SSL connection.
However, if the output is like this :
=> [
{
+"Variable_name": "Ssl_cipher",
+"Value": "",
},
]
Then the connection is not sure. There are number of variables which you can help us get more information about the SSL connection paramaters. We checked Ssl_cipher
above, you can also check Ssl_version
which might give you blank or something like TLSv1
if SSL is working.
To get all information about SSL connection run following in tinker prompt :
>>> DB::select("SHOW STATUS LIKE '%Ssl%'")
- Next steps to secure the connection :
If you found out that the laravel application connection is not using SSL while connecting to AWS RDS, you can follow below steps to enable the same.
Firstly, let us understand how it works. When you connect to AWS RDS normally via mysql cli, you do :
mysql -h myinstance.c9akciq32.rds-us-east-1.amazonaws.com -u username -p
You can pass SSL certificate using --ssl-ca
option in above command like below :
mysql -h myinstance.c9akciq32.rds-us-east-1.amazonaws.com --ssl-ca=/path/to/certificate-authority-file.pem -u username -p
Optionally, you can pass -ssl-mode
and --ssl-verify-server-cert
. For more details about this please refer mysql's official documentation.
Now let's get back to the original problem which we are here to solve. How we are going to do this in Laravel?
Step 1 : Downloading the certificate authority file
. AWS RDS has a commonly published pem file called rds-combined-ca-bundle.pem
which you can download directly from here. It is an officially published pem file which will work in all default RDS SSL connections.
Step 2 : Save the downloaded file from step 1 inside a new directory called RDSCerts
inside laravel root. Quick note that in this step itself, I would add this inside gitignore because there is no need to add pem and cert files inside the version control.
Step 3 : Laravel's database configurations are inside config/database.php
file. It already has a mysql
section. Let's not change that, lets copy that entirely into a new configuration section called mysql_ssl
where we will also add the certification authority file in options like below :
'mysql_ssl' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => false,
'engine' => null,
'options' => [
PDO::MYSQL_ATTR_SSL_CA => base_path('RDSCerts/rds-combined-ca-bundle.pem')
],
],
Important note, You might be thinking that when we need to pass --ssl-verify-server-cert
option somehow from laravel's configuration as well. Don't worry it's enabled by default. If you want to disable it then you can pass PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT = false
which I would not suggest.
There are more options about SSL which you can check in the official PDO documentation.
Once you follow above 3 steps, you should be good to go. Cross check by running DB::select("SHOW STATUS LIKE '%Ssl%'")
in tinker as we did earlier in this article. You should see ciphers and ssl version mentioned in the connection.