Programming on the Server Side
42 Data Security
As a server-side programmer, you have a responsibility to keep your users’ data private. Information from the user, especially sensitive information like passwords and credit cards, should be kept hidden as much as possible, even from trusted members of your web development team. Passwords, credit card numbers, and other sensitive data should not be visible on the device, should not be visible in transmission, and should not even be visible in the database or anywhere else on the server.
Hiding Information on the Device
Hiding information on the device means making sure that people and surveillance devices in the user’s environment do not have a view of it. You should mask sensitive information and hide it in POST parameters to keep it away from prying eyes.
Masking
Masking information means blocking all or part of it when displaying it on the page.
Passwords can be masked using an input element with type=”password”. This shows the length of the password, but not its contents. JavaScript event listeners can also be used to capture inputs and change the value attribute, masking the length of the password as well.
Credit card numbers should also be masked, but in practice, it is frustrating for the user to not be able to see a long number while entering it. So, most apps do not mask during initial entry but do mask most of the credit card number when echoing it for confirmation.
POST Parameters
Sensitive information should always be sent using an HTTP Post Request. This does not keep the information safe from hackers, but it does stop the information from being viewed in the address bar of the device, and it stops it from being accidentally bookmarked, or copied and pasted.

Hiding Information in Transmission
HTTP Request and Response messages may pass through multiple servers before arriving at their destination. There are lots of opportunities for someone to intercept the messages along the transmission route. Hiding information in transmission means making sure that someone who intercepts the communications between the client and server will not be able to read passwords, credit card numbers, or other sensitive information. The only way to do this is by using a secure transmission protocol.
Never Use HTTP
HTTP (Hypertext Transfer Protocol) is ok for development servers that are not live, like your local XAMPP installation. But you should never use HTTP for a live web app on the open Internet. Any site that uses HTTP is sending plain text data over an unencrypted connection. This information might pass through dozens of servers on its way to its destination, where it may get copied, cached, and/or harvested by malicious users. Using HTTP Post Requests does not hide data during transmission – anything you can see by viewing a request in the Network tab of the Chrome Developer Tools can also be seen during transmission if the request uses HTTP.
Always Use HTTPS
The solution is to use the HTTPS protocol (Hypertext Transfer Protocol Secure) for live web apps. This forces the browser and the server to exchange their public encryption keys (very large integers, usually presented in hexadecimal) which are used to turn every message into unreadable encrypted code. Decoding requires private encryption keys, which the server and browser keep to themselves.
Messages encrypted using a public key are not uncrackable but cracking them requires figuring out the private key that goes with the public key. The sophisticated mathematics used to generate the keys ensure that solving this problem is intractable for most users. They could figure out the private key in theory, but the computations involved will take many years even on the fastest available machines.
Do it Yourself
Go to any valid HTTPS website on Chrome and click the “View Site Information” logo on the left side of the address bar. It looks like the image on the left.
Then click “Connection is Secure“, then “Certificate is Valid” to see the certificate and the public encryption key. At the time of writing, the public key for https://ecampusontario.pressbooks.pub was: 9a3df1f371d466307b74b8932e85814444620929d402c0c244a0d8fb1c274df8.
In order to use HTTPS for your web app, you have to do two things:
- Obtain an SSL Certificate containing a public key, issued by a trusted authority
- Redirect users from HTTP to HTTPS
Obtaining an SSL Certificate
An SSL Certificate is a data file that the server sends to the client for verification. It contains the server’s public key and domain name and is digitally signed by a trusted authority that issued the certificate. If the certificate is valid and the client trusts the authority that issued it, then it will initiate communications using HTTPS. Otherwise, the user may find themselves blocked with a security warning from their browser.
Most web hosting services will either include an SSL Certificate for free or charge a bit extra to obtain and install one for you.
Redirecting to HTTPS
Most servers support both HTTP and HTTPS, but users should only ever use HTTPS. On the off chance that a user arrives at your site using the unencrypted HTTP protocol, there are a number of ways to redirect them to HTTPS. At the server level, URL rewriting often does the job. On an Apache server (e.g. XAMPP) you can include URL rewriting rules in a special file called .htaccess in the public_html folder. Here is an example:
RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
The above code directs the server to check for HTTPS in the URL of every HTTP Request. That’s the RewriteCond (Rewrite Condition) line above. If HTTPS is not found (i.e. it’s “off”), the RewriteRule is executed. This rule instructs the server to send a response back to the browser including the HTTPS version of the URL and with a response status of 301, which means that this site has moved permanently. The browser then launches a new request to the HTTPS URL.
Do it Yourself
The site example.com supports both protocols and does not redirect. Try these links and note the difference in the address bar of Chrome:
Now open the Network tab of the Chrome Developer Tools and go to http://google.com. You should see an initial response with a status of 301 (Permanently Moved) followed by a new request using the HTTPS protocol. When you try this a second time, the browser will remember and will go straight for the HTTPS version.
(If you don’t see the 301 response, it may be because you’ve done this before and Chrome remembered the redirect. Try using the Chrome Incognito Browser with the Network tab open to see the redirect.)
Hiding Information on the Server
Hiding information on the server means making sure that people with server access (hackers, unauthorized users, or even authorized users) don’t see sensitive information. Masking information on the page and always using POST parameters and HTTPS prevents user data from being seen on the device and during transmission, but further steps must still be taken to make sure that the information is hidden from prying eyes on your servers.
Strong Server Passwords
Server passwords must be strong and kept secret. This includes database user passwords (for phpMyAdmin and connect.php) as well as FTP and remote login passwords. They are your first line of defense against hackers who would steal your users’ data.
PhpMyAdmin contains tools for generating secure passwords, as shown below. (You can find this in the User Accounts tab.)
Session Storage
Never store passwords or credit card numbers in the PHP $_SESSION object. By default, information that you store here will be cached in plain text session files on the server for later retrieval. Anyone with access to the server will be able to retrieve the information in these files. A few years ago, Facebook, still has a lot of back end code written in PHP, recently caught themselves in exactly this situation: https://newsroom.fb.com/news/2019/03/keeping-passwords-secure/
Do it Yourself
By default, XAMPP stores sessions in its tmp folder. There you will find plain text files that start with “sess“. If you open one of them using a text editor such as notepad, you will be able to read the keys and values of the $_SESSION object for that session. If a password or credit card number was stored here, you would be able to read it. If you don’t see “sess” files, run one of the Do it Yourself examples from the PHP Session Management chapter, then check again.
For example, I just ran the session_ajax example from the Full Stack Example Pack. This example was discussed in a Do it Yourself box in the PHP Session Management chapter. The session_ajax app stores an integer value in $_SESSION[“number”]. I’m on Windows, so my sessions are in the C:/xampp/tmp folder. In that folder I found this file:
sess_svlq6na249h2qtoubgu0a32t5v
When I open in notepad, I see these contents:
number|i:10;
This tells me that the session key “number” stores an integer value (“i”) with the value of 10.
Hashing and Salting Passwords
You should not store plaintext passwords in your database. Passwords should not be visible to intruders who may have broken into your system, but they should not even be visible to you or your team members. This can be done by hashing and salting user passwords before storing them.
Hashing is a kind of one-way encryption. A hash function turns a string into an unrecognizable series of characters. It’s easy and fast to do the encryption, but there is no known way to quickly compute the decryption. When the user registers, you hash their password and store the hashed version in your database. When they attempt to log in again, you hash the password they entered and check it against the hashed password stored in the database for a match. When passwords are handled in this way, no attacker or employee will ever see an unencrypted user password. Knowing the hashed password is not helpful because you need the original password to log in and you can’t get that from the hashed version without spending years of compute time on the decryption.
Although it’s not easy to compute the original password from its hashed version, it is possible to precompute hash codes for a list of common or known passwords and then look up hashed passwords to find the original. A table of passwords and their hashed versions is called a rainbow table. To combat this, hashing algorithms will also use a process called salting. Salting makes rainbow tables much more difficult by adding a random string of characters (the salt) to each password so that the hashed version in the database is not a direct hash of the original password, but of the salted version of the password.
Hashing and Salting in PHP
In PHP, you can hash a password for storage using the password_hash function as shown below:
$hash = password_hash($user_password, PASSWORD_DEFAULT);
In this example $user_password is the password entered by the user, and $hash is the hashed and salted version, created using the best available hashing algorithm (the default). Store $hash in the database, not $user_password.
When a user tries to log in, you can retrieve $user_password from a POST parameter, then compare it to the $hash you retrieve from the database, like this:
if ( password_verify($userpwd, $hash) ) { // granted } else { // denied }
Do it Yourself
Get the hashing folder from the Full Stack Example Pack to see some basic password hashing in action. Load hashing.html through a server, and enter “mypassword” to get the Access Granted message. Any other password should get Access Denied. In both cases, you will see the hashed version of the password you entered displayed on the page.
Check the hashing.php code to see how this was done. Note that in a real application, you would have a user table in the database storing userids and hashed passwords. To login, the user would enter a userid and a password. The app would retrieve the hashed password from the database using the userid.
Gotcha: Password Width
When storing hashed passwords in a database table, make sure the VARCHAR field size is large enough. If you make the field too small, storing will appear to be successful but will truncate the stored value. Then password matching will fail because you are not retrieving the entire hash. Currently, the hash size for PASSWORD_DEFAULT is 60 characters.