© Christos Karayiannis 2019
Christos KarayiannisWeb-Based Projects that Rock the Classhttps://doi.org/10.1007/978-1-4842-4463-0_9

9. Running Your Site with a Certificate from a Certificate Authority

Christos Karayiannis1 
(1)
Karditsa, Thessaloniki, Greece
 

In this chapter, you will upgrade your site, which so far implements HTTPS with a self-signed certificate, by obtaining an SSL certificate from a certificate authority (CA). As a result, the web browser warnings about an insecure site will not appear, and instead a padlock icon, indicating secure communication, will appear on the left of the URL in the address bar of your browser. To obtain an SSL certificate, you usually need to own a second-level domain (SLD), like httpsserver in httpsserver.eu, as opposed to a name with the second-level domain owned by a DDNS company (like the names used in the previous chapters with the SLD ddns.net ). For this reason, in this chapter, you’ll learn how to obtain a domain name, and I’ll discuss the process to obtain both the domain name and the SSL certificate. The cost at the time of this writing is about $8/year for the domain name and $19/year for the SSL certificate. However, it is not required that you register to run the projects. The source code for the projects used in this and the following chapter can run with the old server configuration; you will just continue to get the browser warnings.

By obtaining an SSL certificate from a CA, you will create a site that provides a login connection. For a system requiring a user login, implementing SSL is an indispensable option because the password and all other sensitive data is transmitted encrypted. In this project, you will also learn how to encrypt the user password when stored in the database.

The user enters the username and the password, and by utilizing PHP sessions, the user will stay connected and can view user-specific data while browsing all the web pages of this site until a logout is issued.

Obtaining Your Own Domain Name

Plenty of companies offer domain name registration. You first have to search the company’s site to see whether the name you want is available. An SLD name such as httpsserver may be available for certain top-level domains (TLDs) such as for eu but be unavailable for another TLD like com. Usually the prices for the domain names are different for the various TLDs.

Visit the Domains link at Dynu.​com, the provider used in this example. In the textbox, enter your preferred domain name, e.g., httpsserver.eu, and click the Search button. Figure 9-1 displays the results indicating that httpsserver.eu is available for purchase.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig1_HTML.jpg
Figure 9-1

Searching the availability of a domain name

By clicking the Purchase button corresponding to the TLD of your choice, e.g., eu, you can start the registration process at Dynu.​com, which includes the payment process for obtaining the domain name. When you’re finished, the domain is included in the list of your domain names as a link, as displayed in Figure 9-2. You can further manage your domain name by clicking this link.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig2_HTML.jpg
Figure 9-2

The newly registered domain name is added to the list of your domain names

The next step for running a secure site is to acquire an SSL certificate from a certificate authority for your new domain name.

Obtaining a CA SSL Certificate for Your Domain Name

To go to the SSL certificates web page at Dynu.​com, click the gears icon and then click the SSL Certificates link. Make a choice from the available SSL certificate offers and proceed with the payment. Figure 9-3 shows an SSL certificate from Comodo being used. The status of the SSL certificate is Awaiting CSR.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig3_HTML.jpg
Figure 9-3

By purchasing an SSL certificate, your domain name status turns to Awaiting CSR

A certificate-signing request (CSR) is an encoded file with your application form for the CA. It is usually generated on the web server and includes information such as your location, your e-mail, your organization, etc., and basically contains your public key that will be included in the SSL certificate. You can generate your key pair, consisting of the CSR file (that includes your public key) and also your private key, by using the following openssl req command at the Linux terminal:
$ sudo openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr
In this command, the following options are used:
  • new: Generates a new certificate request

  • newkey rsa:2048: Generates a new private key, an RSA key that is 2,048 bits in size, as its argument indicates

  • nodes: Specifies that the private key created will not be encrypted

  • keyout server.key: Specifies server.key as the file name to write the newly created private key to

  • out server.csr: Specifies server.csr as the file name of the CSR file

OpenSSL asks about the following information:
  • Country name (two-letter code)

  • State or province name (full name)

  • Locality name (e.g., city)

  • Organization name (e.g., company)

  • Organizational unit name (e.g., section)

  • Common name (e.g., server FQDN or your name)

  • E-mail address

  • A challenge password

  • An optional company name

Most of the previous information is self-explanatory. In the Common Name field, enter your domain name, e.g., httpsserver.eu. If you don’t represent any company or organization, just enter your name because many CAs require that all the fields be completed. When you fill in all the fields, the openssl command terminates, and the two files server.key and server.csr are created in your working directory. Use the following command to view them:
$ ls –l
The command’s output is as follows:
total 8
-rw-r--r-- 1 root root 1058 Feb 27 19:49 server.csr
-rw------- 1 root root 1704 Feb 27 19:49 server.key
Change the server.csr file user rights so that only root has access to this file. File server.key has the user rights already set.
$ sudo chmod 0600 server.csr
Use the ls –l another time:
$ ls –l
The command’s output is as follows:
total 8
-rw------- 1 root root 1058 Feb 27 19:49 server.csr
-rw------- 1 root root 1704 Feb 27 19:49 server.key
Of the two files generated, your private key (server.key) is the one that you keep secret. The other one (server.csr) is the CSR file that includes your public key, and it can be available to anyone. This is the file you have to submit to the CA. Open the CSR file with a text editor, copy the contents, and switch back to your browser. In the Actions column of the Dynu.​com web page accessed previously, click the Manage button (the blue pencil) to submit your CSR file. Paste your CSR text in the Certificate Signing Request (CSR) window, as viewed in Figure 9-4, and click the Save button.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig4_HTML.jpg
Figure 9-4

Providing the CSR file contents to the CA

In the Web Server Type list, select Apache SSL. Also, in the Approved Email list, select [email protected]. Unless you run a mail server, you probably do not have an e-mail address that uses your domain name. Such an e-mail address is, however, required by CAs for the verification process. SSL certification providers like Dynu.​com create a temporary e-mail account for you to receive e-mail from the CA, via the Dynu.​com webmail interface. The username and the password for the Dynu.​com webmail site are provided by Dynu.​com so that you can reply to the CA’s e-mail and thus complete the SSL certificate registration process.

You’ll receive an e-mail from Dynu.​com that includes your certificate (e.g., httpsserver.eu_2018.cer) and a bundle file (e.g., httpsserver-eu.ca-bundle) with all the intermediate certificates. For security reasons, the CA uses a chain of trust, where one intermediate certificate signs the next. At one end of the chain is the root certificate, which is the CA identity, and at the other end is your domain certificate, which is the certificate you purchase from a CA.

Hint!

Sometimes the SSL certificate provider sends the root and intermediate certificates instead of a bundle file. You have to copy and paste the contents of the files in the order suggested by the SSL certificate provider into the .ca-bundle file. You can skip this and ask your SSL provider to send you a ready-to-use bundle file instead.

Since the SSL certificate is already provided, the status of the SSL Certificates web page of Dynu.​com changes to Complete, as displayed in Figure 9-5.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig5_HTML.jpg
Figure 9-5

With the SSL certificate granted, the status of the domain name on the SSL Certificates web page becomes Complete

In the following section, you’ll install the SSL certificate on the Apache and Lighttpd web servers so you can have a secure communication for the client login project.

Configuring SSL on the Web Servers

For both Apache and Lighttpd, three files are required for using HTTPS.
  • The server’s private key

  • The SSL certificate for the domain name

  • The bundle file that includes the root certificate and the intermediate certificates

Download the files sent with the e-mail from the SSL certificate provider and rename the certificate file using the .crt file extension.
$ cp httpsserver.eu_2018.cer ssl.crt
Next the installation process for Apache and then for the Lighttpd web servers are discussed.

Installing the CA Certificate on the Apache Web Server

Create a new directory named ca in /etc/apache2 and copy the certificates and your private key into it. In the following cp commands, the files are assumed to be copied from the home directory. Use sudo su to avoid including sudo in the rest of the commands.
$ sudo su
# mkdir /etc/apache2/ca
# cp ~/server.key /etc/apache2/ca
# cp ~/ssl.crt /etc/apache2/ca
# cp ~/httpsserver_eu.ca-bundle /etc/apache2/ca
Edit the default Apache SSL configuration file.
# gedit default-ssl.conf
In the configuration file, make sure the following directives are added:
        ServerName httpsserver.eu
        SSLCertificateFile      /etc/apache2/ca/ssl.crt
        SSLCertificateKeyFile   /etc/apache2/ca/server.key
        SSLCertificateChainFile /etc/apache2/ca/httpsserver_eu.ca-bundle
The whole file should look like the following:
<IfModule mod_ssl.c>
    <VirtualHost _default_:443>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html/login
        # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
        # error, crit, alert, emerg.
        # It is also possible to configure the loglevel for particular
        # modules, e.g.
        #LogLevel info ssl:warn
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
        # For most configuration files from conf-available/, which are
        # enabled or disabled at a global level, it is possible to
        # include a line for only one particular virtual host. For example the
        # following line enables the CGI configuration for this host only
        # after it has been globally disabled with "a2disconf".
        #Include conf-available/serve-cgi-bin.conf
        #   SSL Engine Switch:
        #   Enable/Disable SSL for this virtual host.
        SSLEngine on
        #   A self-signed (snakeoil) certificate can be created by installing
        #   the ssl-cert package. See
        #   /usr/share/doc/apache2/README.Debian.gz for more info.
        #   If both key and certificate are stored in the same file, only the
        #   SSLCertificateFile directive is needed.
        #SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
        #SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
#####################
        ServerName httpsserver.eu
        SSLCertificateFile    /etc/apache2/ca/ssl.crt
        SSLCertificateKeyFile /etc/apache2/ca/server.key
        SSLCertificateChainFile /etc/apache2/ca/httpsserver_eu.ca-bundle
####################
        #   Server Certificate Chain:
        #   Point SSLCertificateChainFile at a file containing the
        #   concatenation of PEM encoded CA certificates which form the
        #   certificate chain for the server certificate. Alternatively
        #   the referenced file can be the same as SSLCertificateFile
        #   when the CA certificates are directly appended to the server
        #   certificate for convinience.
        #SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt
        #   Certificate Authority (CA):
        #   Set the CA certificate verification path where to find CA
        #   certificates for client authentication or alternatively one
        #   huge file containing all of them (file must be PEM encoded)
        #   Note: Inside SSLCACertificatePath you need hash symlinks
        #         to point to the certificate files. Use the provided
        #         Makefile to update the hash symlinks after changes.
        #SSLCACertificatePath /etc/ssl/certs/
        #SSLCACertificateFile /etc/apache2/ssl.crt/ca-bundle.crt
        #   Certificate Revocation Lists (CRL):
        #   Set the CA revocation path where to find CA CRLs for client
        #   authentication or alternatively one huge file containing all
        #   of them (file must be PEM encoded)
        #   Note: Inside SSLCARevocationPath you need hash symlinks
        #         to point to the certificate files. Use the provided
        #         Makefile to update the hash symlinks after changes.
        #SSLCARevocationPath /etc/apache2/ssl.crl/
        #SSLCARevocationFile /etc/apache2/ssl.crl/ca-bundle.crl
        #   Client Authentication (Type):
        #   Client certificate verification type and depth.  Types are
        #   none, optional, require and optional_no_ca.  Depth is a
        #   number which specifies how deeply to verify the certificate
        #   issuer chain before deciding the certificate is not valid.
        #SSLVerifyClient require
        #SSLVerifyDepth  10
        #   SSL Engine Options:
        #   Set various options for the SSL engine.
        #   o FakeBasicAuth:
        #     Translate the client X.509 into a Basic Authorisation.  This means that
        #     the standard Auth/DBMAuth methods can be used for access control.  The
        #     user name is the `one line' version of the client's X.509 certificate.
        #     Note that no password is obtained from the user. Every entry in the user
        #     file needs this password: `xxj31ZMTZzkVA'.
        #   o ExportCertData:
        #     This exports two additional environment variables: SSL_CLIENT_CERT and
        #     SSL_SERVER_CERT. These contain the PEM-encoded certificates of the server (always existing) and the client (only existing
        #     when client
        #     authentication is used). This can be used to import the certificates
        #     into CGI scripts.
        #   o StdEnvVars:
        #     This exports the standard SSL/TLS related `SSL_*' environment variables.
        #     Per default this exportation is switched off for performance reasons, because the extraction step is an expensive operation and is usually useless for serving static content. So one usually enables the exportation for CGI and SSI requests only.
        #   o OptRenegotiate:
        #     This enables optimized SSL connection renegotiation handling when SSL
        #     directives are used in per-directory context.
        #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
        <FilesMatch ".(cgi|shtml|phtml|php)$">
                SSLOptions +StdEnvVars
        </FilesMatch>
        <Directory /usr/lib/cgi-bin>
                SSLOptions +StdEnvVars
        </Directory>
        #   SSL Protocol Adjustments:
        #   The safe and default but still SSL/TLS standard compliant shutdown
        #   approach is that mod_ssl sends the close notify alert but doesn't wait for
        #   the close notify alert from client. When you need a different shutdown
        #   approach you can use one of the following variables:
        #   o ssl-unclean-shutdown:
        #     This forces an unclean shutdown when the connection is closed, i.e. no SSL close notify alert is send or allowed to
        #     received.  This violates the SSL/TLS standard but is needed for some brain-dead browsers. Use this when you receive I/O
        #     errors because of the standard approach where
        #     mod_ssl sends the close notify alert.
        #   o ssl-accurate-shutdown:
        #     This forces an accurate shutdown when the connection is closed, i.e. a
        #     SSL close notify alert is send and mod_ssl waits for the close notify
        #     alert of the client. This is 100% SSL/TLS standard compliant, but in
        #     practice often causes hanging connections with brain-dead browsers. Use
        #     this only for browsers where you know that their SSL implementation
        #     works correctly.
        #   Notice: Most problems of broken clients are also related to the HTTP
        #   keep-alive facility, so you usually additionally want to disable
        #   keep-alive for those clients, too. Use variable "nokeepalive" for this.
        #   Similarly, one has to force some clients to use HTTP/1.0 to workaround
        #   their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
        #   "force-response-1.0" for this.
        # BrowserMatch "MSIE [2-6]"
        #        nokeepalive ssl-unclean-shutdown
        #        downgrade-1.0 force-response-1.0
    </VirtualHost>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
For the Apache configuration, it is assumed that the SSLEngine directive in the configuration file is set to on and also that the ssl module is enabled, as described in the previous chapter. If it is not enabled, enable it now using the following:
$ sudo a2enmod ssl
$ service apache2 force-reload

The DocumentRoot directive was also set to /var/www/html/login, which will be the root directory used next in this chapter’s project.

To enable redirection to the HTTPS protocol (https://) when a user connects using the HTTP protocol (http://), edit the corresponding configuration file, e.g., 000-default.conf, and add the following directive:
Redirect / https://httpsserver.eu
The file should look like the following:
<VirtualHost *:80>
    # The ServerName directive sets the request scheme, hostname and port that
    # the server uses to identify itself. This is used when creating
    # redirection URLs. In the context of virtual hosts, the ServerName
    # specifies what hostname must appear in the request's Host: header to
    # match this virtual host. For the default virtual host (this file) this
    # value is not decisive as it is used as a last resort host regardless.
    # However, you must set it for any further virtual host explicitly.
    #ServerName www.example.com
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/login
         Redirect / https://httpsserver.eu
    # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
    # error, crit, alert, emerg.
    # It is also possible to configure the loglevel for particular
    # modules, e.g.
    #LogLevel info ssl:warn
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    # For most configuration files from conf-available/, which are
    # enabled or disabled at a global level, it is possible to
    # include a line for only one particular virtual host. For example the
    # following line enables the CGI configuration for this host only
    # after it has been globally disabled with "a2disconf".
    #Include conf-available/serve-cgi-bin.conf
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
With the redirection directive, the client request shown here:
http://httpsserver.eu
is redirected by the server to the following request:
https://httpsserver.eu

When a URL is entered with the http:// protocol (or no protocol at all), the HTTP status code returned directs the client to a different URL (the connection in this case) with the https:// protocol.

Reload Apache to validate the new configuration.
# service apache2 force-reload

Installing the CA Certificate on the Lighttpd Web Server

Use the certificates and the server’s private key to enable SSL on the Lighttpd web server. If you didn’t previously, enter sudo su to become the root user and avoid prepending sudo in the commands that follow.
$ sudo su
Stop Apache from running so you can work next with Lighttpd.
# service apache2 stop
Next, start the Lighttpd web server.
# service lighttpd start
Create a new directory for storing the private key and the certificates.
# mkdir /etc/lighttpd/ca
Copy the files of the /etc/apache2/ca directory to the new ca directory.
# cp /etc/apache2/ca  /etc/lighttpd/ca
Create the PEM file with the private key and the domain certificate.
# cd /etc/lighttpd/ca
# cat server.key ssl.crt > server.pem
Edit the Lighttpd configuration file.
# gedit /etc/lighttpd/lighttpd.conf
Enter the following lines and save the file:
$SERVER["socket"] == ":443" {
  server.document-root        = "/var/www/html/login"
  ssl.engine                  = "enable"
  server.username             = "www-data"
  server.groupname            = "www-data"
  ssl.pemfile = "/etc/lighttpd/ca/server.pem"
  ssl.ca-file = "/etc/lighttpd/ca/httpsserver_eu.ca-bundle"
  server.name = "httpsserver.eu"
}
Also, to redirect the HTTP requests to HTTPS, include the following lines in the conditional configuration for port 80:
  $HTTP["host"] =~ "(.*)" {
    url.redirect = ( "^/(.*)" => "https://%1/$1" )
  }

Set the document root from /var/www/html/ to /var/www/html/login in the conditional configurations for ports 80 and 443, which will be the one used next in the project.

The file lighttpd.conf should look like the following:
server.modules = (
        "mod_access",
        "mod_alias",
        "mod_accesslog",
        "mod_compress",
        "mod_redirect",
)
server.document-root        = "/var/www/html"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
#server.port                 = 8080
#server.port                 = 443
#server.bind                = "webtoolsonline.servehttp.com"
server.errorfile-prefix     = "/srv/www/errors/status-"
dir-listing.activate        = "disable"
accesslog.filename          = "/var/log/lighttpd/access.log"
#accesslog.format            = "%V %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i""
index-file.names            = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny             = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
compress.cache-dir          = "/var/cache/lighttpd/compress/"
compress.filetype           = ( "application/javascript", "text/css", "text/html", "text/plain" )
$SERVER["socket"] == ":443" {
  server.document-root        = "/var/www/html/login"
  ssl.engine                  = "enable"
  server.username             = "www-data"
  server.groupname            = "www-data"
################################################
  ssl.pemfile = "/etc/lighttpd/ca/server.pem"
  ssl.ca-file = "/etc/lighttpd/ca/httpsserver_eu.ca-bundle"
  server.name = "httpsserver.eu"
################################################
}
$SERVER["socket"] == ":80" {
server.document-root        = "/var/www/html/login"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
#################################################
  $HTTP["host"] =~ "(.*)" {
    url.redirect = ( "^/(.*)" => "https://%1/$1" )
  }
#################################################
}
# default listening port for IPv6 falls back to the IPv4 port
## Use ipv6 if available
#include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
#include "vhost.conf"
Enable the new configuration by reloading Lighttpd.
# service lighttpd force-reload

Testing the SSL CA Certificate

Figure 9-6 displays the home page of httpsserver.eu. This is the home page of the project that you’ll create next in this chapter.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig6_HTML.jpg
Figure 9-6

The home web page of the project’s site

The browser warnings about entering an insecure site do not appear anymore. A padlock icon, indicating a secure connection, appears in the address bar of the browser, and the https:// protocol is not struck through as previously, with the self-signed certificate. Click the padlock icon to find out some details about the current secure connection. For the Chromium browser, the menu options appear as displayed in Figure 9-7.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig7_HTML.jpg
Figure 9-7

Clicking the green padlock allows you to get information about the secure connection

Click the Certificate (Valid) option, as displayed in Figure 9-8, to open the certificate viewer and find details about the current certificate.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig8_HTML.jpg
Figure 9-8

Using the Certificate Viewer in Chromium

Next use an online tool that tests and analyzes the web server certificates, for instance SSL Checker ( www.sslshoper.com ). In the Server Hostname textbox, enter your domain name, e.g., httpsserver.eu, and click the Check SSL button. Figure 9-9 displays the results, indicating no problems were diagnosed.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig9_HTML.jpg
Figure 9-9

Using an online tool to check your SSL certificate

Scroll down to view the chain of trust for your site, as displayed in Figure 9-10.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig10_HTML.jpg
Figure 9-10

Displaying the chain of trust for your SSL certificate

The chain from your domain name certificate leads, in this example, through two intermediate certificates to the CA root certificate.

You will test your SSL certificate next with the project in the following section.

Project: Securely Logging In to a Site

By enhancing your site with an SSL certificate, obtained from a CA, you can now create a site that securely allows the user to create an account and log in to view some personal information. You do this by implementing a MySQL database that stores all the user accounts. The user can remain connected while browsing the site’s pages until finally logging out. For simplicity, this example will just have one page, but you could expand it to more web pages. By implementing PHP sessions, you’ll allow PHP session variables to be shared between the PHP source code of the site’s web pages. In this project, the session_user variable set by the username when the user logs in successfully is shared among the web pages. In web pages like profile.php, which appears when the user logs in, the session_user value is the ticket to querying a database and displaying user-specific information. This can be extended to other web pages and thus allow the user to access more information while connected.

The project requires the following five PHP files:
  • index.php, the home web page that allows the user to either create an account or connect to the site using this account

  • account.php, the web page that creates the account

  • login.php, the web page that logs the user in to the site

  • profile.php, the web page the user is transferred to after a successful login to view user-specific information

  • logout.php, the file that uses the PHP code for the logout process, which also transfers the user from profile.php to login.php

Designing the Project’s Site

Figure 9-11 displays the four web pages and the way they are linked together to form the site.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig11_HTML.jpg
Figure 9-11

The design of the project’s site

The login project site will be placed in a new directory, called login, in the document root. Create this directory with the following command:
$ sudo mkdir /var/www/html/login
You can use either web server you’ve been using in this book, but these instructions will use Apache. The directory /var/www/html/login is already set as the document root of the site for the Apache web server in the default-ssl.conf configuration file with the DocumentRoot directive.
DocumentRoot /var/www/html/login
If Apache is not running currently, enable it now using this:
$ sudo service lighttpd stop
$ sudo service apache2 start

The Source Code for the Home Page of the Site

Next, create the directory index, index.php, so that the site can be accessed from the address bar of the browser simply as follows:
https://httpsserver.eu
or since redirection applies simply as:
httpsserver.eu
Create index.php by entering the following command at the Linux terminal:
$ sudo gedit /var/www/html/login/index.php
Enter the following code and save the file:
<!DOCTYPE html>
<html>
<head>
<style>
body{
background-color:brown;
}
.center {
    margin: auto;
    width: 80%;
    border: 3px solid black;
    padding: 10px;
    background-color:lightsalmon;
}
p{
text-align:center;
font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
font-size:32px;
color:#9F0251;
font-weight:italic;
}
span{
padding:10px;
}
a{
text-decoration:none;
}
a:link, a:visited {
    color: #9F0251;;
}
a:hover {
    color: black;
}
</style>
</head>
<body>
<br><br><br><br><br><br>
<div class="center">
<p>
<span>
<a href="account.php">Create Account</a>
</span>
|
<span>
<a href="login.php">Login&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a>
</span>
</p>
</div>
</body>
</html>
As indicated in its source code, index.php is a simple web page that basically includes two links. Create Account leads to account.php, which is the PHP file that evaluates to the web page used for creating the user account, and Login leads to login.php, which is the PHP file that evaluates to the web page where the user logs in to the site (Figure 9-12).
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig12_HTML.jpg
Figure 9-12

The home page includes two links for creating user accounts and for user login

Next, you will create two more web pages: account.php and login.php.

The Web Page for Creating the User Account

By following the Create Account link, the user goes from index.php to the account.php page, displayed in Figure 9-13, where the account for the specific web service is created. In this example, the service is about securely logging in to a site, where the user can view some details about the books loaned from a local library.

The user is required to enter the following details in the HTML form of account.php (which will be stored in the user table of the login database created in the following section with MySQL):
  • First

  • Last name

  • E-mail address

  • Username

  • Password

  • Password retyped

../images/468152_1_En_9_Chapter/468152_1_En_9_Fig13_HTML.jpg
Figure 9-13

The account.php web page of the site

All form objects are of type text, except the e-mail, which is of type email, and the two password fields, of type password. The e-mail field requires an entry of the form [email protected], and the password fields hide the characters by replacing them with bullets. The password has to be retyped to validate the first password entry. Two checks are done in the PHP form validation process: that all fields are nonempty and that the two password entries match. If those conditions are not met, the appropriate message appears at the top of the web page, as displayed in Figure 9-14.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig14_HTML.jpg
Figure 9-14

Error messages are displayed when the form is incomplete or when the password entries are not identical

To create the file account.php, enter the following command at the Linux terminal:
 $ sudo gedit /var/www/html/login/account.php
Enter the following source code and save the file:
<!DOCTYPE html>
<html>
<head>
<style>
body{
background-color:brown;
}
.center {
    margin: auto;
    width: 80%;
    border: none;
    padding: 10px;
    background-color:lightsalmon;
}
.center2 {
    margin: auto;
    width: 80%;
    border: none;
    padding: 10px;
    background-color:brown;
}
p{
text-align:center;
font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
font-size:24px;
color:white;
font-weight:italic;
}
input{
border-color:#9F0251;
font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
font-size:24px;
color:black;
padding:5px;
background-color:lightsalmon;
}
input[type=submit],[type=reset]{
background-color:brown;
color:lightsalmon;
padding:5px;
}
a{
text-decoration:none;
}
a:link, a:visited {
    color: lightsalmon;
}
a:hover {
    color: black;
}
label {
  font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
  display: inline-block;
  width: 40%;
  text-align: right;
  color:brown;
  font-size:24px;
}
</style>
</head>
<body>
<?php
 $errormsg1 = "";
 $errormsg2 = "";
 $valid1 = 0;
 $valid2 = 0;
if (isset($_POST['s1'])) {
 $first = $_POST["first"];
 $last = $_POST["last"];
 $email = $_POST["email"];
 $user = $_POST["user"];
 $pass1 = $_POST["pass1"];
 $pass2 = $_POST["pass2"];
if((empty($first)) || (empty($last)) || (empty($email)) || (empty($user)) || (empty($pass1)) || (empty($pass2))) {
$errormsg1 = '<p>Please complete all the fields. </p>';
} else {
$valid1 = 1;
}
if((strcmp($pass1, $pass2))) {
$errormsg2 = '<p>Please enter the same password at the Password fields. </p>';
} else {
$valid2 = 1;
}
}
if (($valid1 == 1) && ($valid2 == 1)) {
create_entry($first, $last, $email, $user, $pass1);
}
function create_entry ($first, $last, $email, $user, $pass1) {
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "login";
$mysqli = new mysqli($servername, $username, $password, $dbname);
if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}
$hashed_password = password_hash($pass1, PASSWORD_DEFAULT);
$sql = "INSERT INTO user (first, last, email, username, password) VALUES ('$first', '$last', '$email', '$user', '$hashed_password')";
if(mysqli_query($mysqli, $sql)){
    //echo "Records inserted successfully.";
}else{
      if(mysqli_errno($mysqli) == 1062) {
        echo "<p>Username already inserted (duplicate entry)</p>";
      }else{
         echo "ERROR: Could not able to execute $sql. " . mysqli_error($mysqli);
      }
 }
$mysqli->close();
}
?>
<?php
 if(($errormsg1 != "") && isset($_POST['s1']))
 echo $errormsg1;
 if(($errormsg2 != "") && isset($_POST['s1']) && ($errormsg1 == ""))
 echo $errormsg2;
?>
<br><br>
<form name="form1" method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<div class="center">
<label for="first">First Name: </label><input type="text" name="first">
</div>
<div class="center">
<label for="last">Last Name: </label><input type="text" name="last">
</div>
<div class="center">
<label for="email">E-mail: </label><input type="email" name="email">
</div>
<div class="center">
<label for="user">Username: </label><input type="text" name="user">
</div>
<div class="center">
<label for="pass1">Password: </label><input type="password" name="pass1">
</div>
<div class="center">
<label for="pass2">Retype Password: </label><input type="password" name="pass2">
</div>
<div class="center">
<label for="s1">&nbsp;</label><input type="submit" name="s1" value="Create Account">
</div>
<div class="center">
<label for="r1">&nbsp;</label><input type="reset" name="r1" value="&nbsp;&nbsp;Clear Form&nbsp;&nbsp;">
</div>
</form>
<div class="center2">
<p><a href="index.php">Home</a></p>
</p>
</body>
</html>
A large part of the code is for formatting the web page with CSS and creating the HTML form. PHP code blocks are also used in various positions in the body section of the HTML source code. In the HTML form, the action attribute, indicating the file that will receive the form’s data, is set to account.php.
<form name="form1" method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
When the form is completed by the user and the form data is submitted from account.php to itself, the PHP code included in the following if statement runs:
if (isset($_POST['s1'])) {
...
}

This if condition checks whether data is submitted by the s1 named button of the form, with the POST method, and therefore whether the $_POST['s1'] PHP global variable is set. Notice that s1 is the name of the form’s submit button. The HTTP request method POST is preferred over method GET to send SSL data, because although the GET data is also encrypted, it may be visible to server logs or the browser’s history. Moreover, GET exposes the data to the URL, so the query string may be visible to anyone standing next to you. Also, GET limits the data sent by applying a maximum data length.

The next lines of the same PHP block assign the variables $first, $last, $email, $user, $pass1, and $pass2 to the corresponding values that the form’s fields send. Two checks are performed with the form validation process. First you ensure that the values sent are not empty and, if all the form data is completed, that the two password fields’ values match. If the checks succeed, the function create_entry() is called with the arguments $first, $last, $email, $user, and $pass1.

create_entry() connects to the login MySQL database (which will be created in the following section) and inserts the row provided by the create_entry() arguments in the table user. These rows correspond to the table columns first, last, email, username, and password. Before inserting the values into the database, the PHP function password_hash() is called to encrypt the password value.
$hashed_password = password_hash($pass1, PASSWORD_DEFAULT);
$sql = "INSERT INTO user (first, last, email, username, password) VALUES ('$first', '$last', '$email', '$user', '$hashed_password')";

All passwords included in the database are therefore hashed to a value with a constant size (e.g., 60 characters when password_hash() is applied with the PASSWORD_DEFAULT argument). Hashed values are theoretically irreversible, which means you can’t obtain the password from the password’s hashed value. What you can do is hash the value of the user’s entry and compare it with the hashed value already stored in the database.

The function mysqli_query() submits the query to the database. If this function returns false, the error with the number 1062 is checked, which indicates a duplicate entry. A duplicate entry for the username is not allowed by the database design, which is discussed in the following section.

In this case, an appropriate message is printed at the top of the window. Also, for other reasons of failure, the corresponding error number is printed.

Creating the Database Used for the Project

To test account.php, create the database used in the project. Connect with the mysql client to the MySQL server using the following at the command line:
$ sudo mysql -u root
At the mysql> prompt that appears, enter the following command to create the new database called login:
mysql> create database login;
The MySQL server responds with the following message:
Query OK, 1 row affected (0.00 sec)
Select the new database to work with.
mysql> use login;
The MySQL server responds with the following message:
Database changed
Create the table user with the column id as the primary key. The id field will increment each time automatically by one, starting from one, and therefore you don’t have to provide it when you insert a new record into this table.
mysql> CREATE TABLE user(id int NOT NULL AUTO_INCREMENT, PRIMARY KEY (id), username varchar(255) NOT NULL, UNIQUE KEY (username), password varchar(255) NOT NULL, email varchar(255) NOT NULL, first varchar(255) NOT NULL, last varchar(255) NOT NULL);
The MySQL server responds with a message similar to the following:
Query OK, 0 rows affected (0.43 sec)
At this point, you may want to view the page’s structure. You can enter the following:
mysql> describe user;
The command’s output is as follows:
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| username | varchar(255) | NO   | UNI | NULL    |                |
| password | varchar(255) | NO   |     | NULL    |                |
| email    | varchar(255) | NO   |     | NULL    |                |
| first    | varchar(255) | NO   |     | NULL    |                |
| last     | varchar(255) | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

In the CREATE TABLE SQL command, the UNIQUE KEY constraint was used for the username column in the user table (displayed with the UNI keyword in the table’s structure) to ensure that there are no two identical usernames. Any attempts to insert a duplicate value is detected in the source code of the function create_entry() in account.php, and an appropriate error message is displayed at the top of the window.

Do not exit mysql yet since the MySQL client connection to the database is required for the next section.

Testing the PHP to MySQL Connection

In this section, you’ll enter some records in the user table of the database from the account.php form. In your browser, display the directory index, index.php, by entering the following address in the address bar:
https://httpsserver.eu/
Then follow the Create Account link. You can also view the account page directly, without using the directory index first, by entering the following:
https://httpsserver.eu/account.php
Enter in the form’s fields the details of some test users and submit them by clicking the Create Account button. For instance, you can create two users: Robert and Sophie. Figure 9-15 displays the values inserted in the form fields for user Robert.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig15_HTML.jpg
Figure 9-15

The values entered in the account.php form for user Robert

Although generally not recommended, to compare the output of hashing two different-sized passwords, use a simple password for Robert, such as 123, and then a stronger one for Sophie, such as supersophie3mi8#m&&. Figure 9-16 shows the values entered in the form for user Sophie.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig16_HTML.jpg
Figure 9-16

The values entered in the account.php form for user Sophie

Switch to the terminal and connect to the MySQL client. Use the following command to view the new contents of the table user:
 mysql> select * from user;
The command’s output is as follows:
+----+----------+----------------------+----------------------+--------+---------+
| id | username | password             | email                | first  | last    |
+----+----------+-----------       ----+----------------------+--------+---------+
|  1 | robert   | $2y$10$UWc3eeIjxFlbF                  j75muUiF.wweL2VSpk7b7                  ytsApi5rBGQtGLLCLda  | [email protected] | Robert | Walker  |
|  2 | sophie   | $2y$10$nNTcBgLkzpSbk                  CZTcjmdVetzk2/KYsOq2o                  YyZpQTJIO5hghzkaJwS  | [email protected]  | Sophie | Edwards |
+----+----------+----------------------+----------------------+--------+---------+
2 rows in set (0.00 sec)
As you’ll notice, regardless of their initial size, both passwords after hashing have a constant length of 60 characters. For instance, for Robert, the password 123 after the hashing process has the following value:
$2y$10$UWc3eeIjxFlbFj75muUiF.wweL2VSpk7b7ytsApi5rBGQtGLLCLda
where:
  • $2y$ is the algorithm used, which is the bcrypt algorithm for the PASSWORD_DEFAULT argument of password_hash().

  • 10$ is the algorithm cost.

  • UWc3eeIjxFlbFj75muUiF. is the salt.

  • wweL2VSpk7b7ytsApi5rBGQtGLLCLda is the hashed password.

Password hashing is a nice solution for maintaining password security even when the database is compromised. The one-way hashing applied by password_hash() leads to a result that cannot be reversed from the original password. Since the hashed password, and not the original one, is stored in the database, to verify the password of the user who log ins to the site, the entered password has to be hashed and then compared with the stored password.

The hashing procedure is enforced by using a salt. Applying a common hash algorithm to a password always leads to the same result for the given password. This might be proved insecure, since an attacker might try a number of passwords, hashed with the same algorithm until he succeeds. A collection of hashed passwords used for the attack is called a rainbow table . By attaching a salt to the password, which is a random string either generated by the hashing method or provided by the user, the hashing outcome will be different even when the same password is hashed twice. When comparing the stored password with the password entered by the visitor of the site, the latter is hashed with the salt stored in the hashed password of the database.

Before testing the user login functionality of the site, you need to create a second table for the login database with details about the books loaned by the users. Then on login, the personal information will be available for each user at the web page that the login connection leads to.

At the Linux terminal, with mysql open, enter the following:
mysql> create table book_loan (book_id char(13), primary key(book_id), loan_date date, user_id int, foreign key(user_id) references user(id));
The MySQL server responds with a similar output:
Query OK, 0 rows affected (0.47 sec)
Enter a number of insert operations on the database like the following:
mysql> insert into book_loan(book_id, loan_date, user_id) values(6183443458964, NOW(), 1);
mysql> insert into book_loan(book_id, loan_date, user_id) values(3487368817469, NOW(), 1);

In the previous commands, the MySQL function NOW() returns the current date and time as a YYYY-MM-DD string.

View next the contents of the table book_loan by using the following:
mysql> select * from book_loan;
The MySQL server outputs the following table:
+---------------+------------+---------+
| book_id       | loan_date  | user_id |
+---------------+------------+---------+
| 3487368817469 | 2018-10-04 |       1 |
| 6183443458964 | 2018-10-04 |       1 |
+---------------+------------+---------+
2 rows in set (0.00 sec)

Therefore, the user with an id value of 1 (user robert) is billed with two books so far. In the next section, this information will be available to Robert, and the information that no books are loaned will be also available to Sophie.

The Source Code of the Login Web Page

Figure 9-17 displays the login.php page that appears when the user clicks the Login link on index.php.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig17_HTML.jpg
Figure 9-17

The login.php web page allows the user to log in to the site

To create the file login.php, enter the following command at the Linux terminal:
$ sudo gedit /var/www/html/login/login.php
Enter the following source code and save the file:
<?php
session_start();
?>
<!DOCTYPE html>
<html>
<head>
<style>
body{
background-color:brown;
}
.center {
    margin: auto;
    width: 80%;
    border: 3px solid black;
    padding: 10px;
    background-color:lightsalmon;
}
.center2 {
    margin: auto;
    width: 70%;
    border: none;
    padding: 10px;
    background-color:brown;
}
p{
text-align:center;
font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
font-size:32px;
font-weight:italic;
}
input{
border-color:#9F0251;
font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
font-size:32px;
color:black;
padding:10px;
background-color:lightsalmon;
}
input[type=submit]{
background-color:brown;
color:lightsalmon;
}
a{
text-decoration:none;
}
a:link, a:visited {
    color: lightsalmon;
}
a:hover {
    color: black;
}
</style>
</head>
<body>
<?php
if (isset($_POST['s1'])) {
 $user = $_POST["user"];
 $pass = $_POST["pass"];
if((empty($user)) || (empty($pass))) {
echo '<p style="color:white">Please complete the username and the password. </p>';
} else {
verify_password($user, $pass);
}
}
function verify_password($user, $pass) {
$dbserver = "localhost";
$dbuser = "root";
$dbpass = "";
$dbname = "login";
$mysqli = new mysqli($dbserver, $dbuser, $dbpass, $dbname);
if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}
$user = mysqli_real_escape_string($mysqli, $user);
$sql = "SELECT password FROM user WHERE username='$user'";
if($query = mysqli_query($mysqli, $sql)) {
$rows = mysqli_num_rows($query);
}
if ($rows == 1) {
$row = mysqli_fetch_assoc($query);
$dbstored_pass=$row['password'];
$isValid = password_verify($pass, $dbstored_pass);
if ($isValid){
$_SESSION['session_user']=$user;
header("location: profile.php");
}
} else {
echo '<p>Username or Password is invalid</p>';
}
$mysqli->close();
}
?>
<br><br><br><br><br><br>
<div class="center">
<form name="f1" method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<p>Username: <input type="text" name="user">
</p>
<p>Password: <input type="password" name="pass">
</p>
<p><input type="submit" name="s1" value="&nbsp;&nbsp;&nbsp;Login&nbsp;&nbsp;&nbsp;">
</p>
</form>
</div>
<div class="center2">
<p><a href="index.php">Home</a></p>
</p>
</body>
</html>
Like with account.php and index.php, CSS attributes are implemented to style the login web page. Like account.php, an HTML form is used that implements the POST method to submit the values to login.php and validate the data passed. The form validation checks whether the two fields are empty, and its source code is included in the PHP block that runs when the form with the submit button s1 has sent data back to the same web page.
if (isset($_POST['s1'])) {
...
}
In the case of nonempty fields, the following function is called:
verify_password($user, $pass);
Here, $user and $pass are the PHP variables for the username and the password sent by the form:
$user = $_POST["user"];
$pass = $_POST["pass"];
What verify_password() does is connect to the MySQL server and specifically to the login database and run the following query, which retrieves the stored password in the database of the specific user:
SELECT password FROM user WHERE username='$user'

Before $user is passed to the SQL query, mysqli_real_escape_string() applies to this variable to escape some characters, that is, to prepend a backslash before any special character, taking into account the character set of the current database connection, which appears as the first parameter of mysqli_real_escape_string(). The function mysqli_real_escape_string() is used for security reasons and especially for avoiding SQL injections, that is, executing commands hidden in the SQL query.

With the function mysqli_num_rows(), the number of rows of the query is returned, and if the row number is one (there is a password of the specific user), the row is retrieved as the $row array, and the password is retrieved as the array’s item stored in index password.
$row = mysqli_fetch_assoc($query);
$dbstored_pass=$row['password'];
The following command calls the PHP function password_verify() to verify the password provided by the user and sent by the login form ($pass), with the password stored in the database ($dbstored_pass).
$isValid = password_verify($pass, $dbstored_pass);

As mentioned in the previous section, $pass is the plaintext password (e.g., 123), while $dbstored_pass is the hashed password. The function password_verify() compares the entered password with the stored one after it hashes the former using the salt stored along with the latter.

If the two passwords match, the value of $user is assigned to the PHP session variable session_user. At this point, more session variables could be used if required. Therefore, the username will be available to all other web pages of the site while the user is connected, and its unique value will be the key, which is where all information about the user will be derived from. The session variable, available for all web pages of the site that join the session, is stored on the server side, and the user cannot have any access to it and cannot substitute it with another user’s.

After a successful login, the web browser redirects to profile.php.
if ($isValid){
$_SESSION['session_user']=$user;
header("location: profile.php");
Recall also that for the session to function as expected, the following PHP block that creates the session should be at the top of the web page:
<?php
session_start();
?>

The Source Code for the User Profile Page

Next, create profile.php, which is the web page the user redirects to on login. At the Linux terminal, enter the following:
$ sudo gedit /var/www/html/login/profile.php
Enter the following source code and save the file:
<?php
session_start();
$user=$_SESSION['session_user'];
?>
<!DOCTYPE html>
<html>
<head>
<style>
body{
background-color:brown;
}
.right {
    margin: auto;
    width: 100%;
    border: none;
    padding-right: 20px;
    text-align:right;
    background-color:lightsalmon;
    font-size:24px;
}
table{
background-color:lightsalmon;
color:black;
font-size:24px;
width:70%;
margin: 0 auto;
padding:20px;
}
td{
text-align:center;
}
h1{
text-align:center;
}
</style>
</head>
<body>
<p class="right">
You are currently logged in as user <?php echo $user ?>
<a href="logout.php">Logout</a>
</p>
<br><br><br>
<h1>Books Loaned</h1>
<?php
display_books($user);
function display_books($user) {
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "login";
$mysqli = new mysqli($servername, $username, $password, $dbname);
if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
} else {
$sql = "SELECT book_loan.book_id, book_loan.loan_date FROM user INNER JOIN book_loan ON user.id = book_loan.user_id WHERE user.username='$user'";
if($result=mysqli_query($mysqli, $sql)){
  if (mysqli_num_rows($result)>=1){
    echo "<table>";
    echo "<tr><td>" . "ISBN" . "</td><td>" . "Loan Date" . "</td></tr>";
    while ($row=mysqli_fetch_assoc($result))
      {
        echo "<tr><td>" . $row['book_id'] . "</td><td>" . $row['loan_date'] . "</td></tr>";
      }
    echo "</table>";
    mysqli_free_result($result);
  } else{
    echo "<h1>No results found</h1>";
  }
} else{
    echo "ERROR: Could not able to execute $sql. " . mysqli_error($mysqli);
}
$mysqli->close();
}
}
?>
</body>
</html>
The first block of PHP source code allows profile.php to join the PHP session and also retrieves from variable $user the value set by login.php for the session variable session_user.
<?php
session_start();
$user=$_SESSION['session_user'];
?>

In the top-right area of the web page, the message “You are currently logged in as user” appears followed by the username of the logged-in user, provided by the $user value. Next to the name, a Logout link appears. (I’ll discuss later in this section how this link works.)

Under the heading “Books Loaned” is a list with the book ISBNs that correspond to the book_id column of the book_loan table and also the date loaned that corresponds to the loan_date column of the same table. If the user has not currently loaned any books, as in the example with the user Sophie, the message “No results found” appears instead.

Like the source code in the files account.php and login.php, a connection with the login database is established with the function mysqli().
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "login";
$mysqli = new mysqli($servername, $username, $password, $dbname);
On a successful connection, the following SQL command is executed, using the function mysqli_query():
$sql = "SELECT book_loan.book_id, book_loan.loan_date FROM user INNER JOIN book_loan ON user.id = book_loan.user_id WHERE user.username='$user'";

This time, data from two tables is required, and an inner join between the two tables is formed. With the inner join, a new result table is created that selects only the rows from the two tables that match the join condition. In the previous SQL command, the join condition is equality among the primary key of table user and the foreign key of table book_loan. This type of join based on equality is often referred as equijoin , and this is often used to denormalize data. The term normalizing refers to the process of storing data in different tables. With the denormalizing process, data from different tables can be retrieved.

With the previous SQL command, the book_id and loan_date columns from the book_loan table are selected for the matching columns of user_id (of table book_loan) and id (of table user) from the rows where the username column (of table user) is the value of $user (e.g., robert). The query depends therefore on the PHP session variable session_user, whose value is assigned to the PHP variable $user.

If the return value of mysqli_num_rows() holds one or more rows, the results ($row['book_id'] and $row['loan_date']) are included in an HTML table.
  if (mysqli_num_rows($result)>=1){
    echo "<table>";
    echo "<tr><td>". "ISBN" . "</td><td>" . "Loan Date" . "</td></tr>";
    while ($row=mysqli_fetch_assoc($result))
      {
        echo "<tr><td>". $row['book_id'] . "</td><td>" . $row['loan_date'] . "</td></tr>";
      }
    echo "</table>";
The function mysqli_fetch_assoc() is used to provide the result rows. If no rows are returned, no table is formed, and the message “No results found” appears in a heading element.
} else{
    echo "<h1>No results found</h1>";
  }

The user-specific data is thus displayed. In the following section, you’ll create the web page that logs the user out.

Allowing the User to Log Out

On profile.php, the PHP session is maintained, and the session variable session_user indicates the user who logged in and provides information specific to this user. Other web pages of the site could also be used to access the session_user variable and allow the user to stay connected and view user-specific data while visiting the site.

As mentioned previously, a message at the top right reminds the user of their name, and also a link called Logout allows the user to disconnect. This link leads to logout.php.
<p class="right">
You are currently logged in as user <?php echo $user ?>
<a href="logout.php">Logout</a>
</p>
Create the file logout.php using the following command at the Linux terminal:
$ sudo gedit /var/www/html/login/logout.php
Enter the following PHP code and save the file:
<?php
session_start();
setcookie(session_name(), “, 100);
unset($_SESSION['session_user']);
session_destroy();
header('Location: login.php');
exit;
?>

The previous PHP source code snippet uses the function session_start() to join the current session and then deletes the cookie from the user’s browser by setting the expiration time to a value in the past. By setting the expiration time to 100, you indicate value 100 as a Unix timestamp, which corresponds to the number of seconds after the Unix epoch, which is January 1, 1970 00:00:00 UTC. The session variable session_user is destroyed with the function unset(), and the PHP session is terminated by calling the function session_destroy(). With the header() function , the web page redirects to login.php.

Testing the User Connection to the Site

To test the two users, you can use the login.php page , which appears when you return to the home page. Just click the Home link on account.php and then use the Login link. Or, since you already know the site’s structure, enter the following:
https://httpsserver.eu/login.php
Enter the username, e.g., robert, and the user’s password, e.g., 123, for Robert, and click the Login button, as displayed in Figure 9-18.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig18_HTML.jpg
Figure 9-18

Testing the site by connecting as Robert

User Robert gets connected to his profile page with personal information about his recent activity. In the top-right area of the page, the message “You are currently logged in as user robert” appears.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig19_HTML.jpg
Figure 9-19

The profile web page for Robert, displaying the ISBN and loan date of loaned books

For books currently loaned to user Robert, the ISBN and loan date appear.

The Logout link leads to logout.php, where its evaluated PHP source code logs out the user by terminating the current session. The user then gets transferred back to the login.php web page.

Improving the profile.php Web Page

The data format for the loan date, appearing in profile.php, is the default one used by MySQL. To change this to a more readable format and also add a new column called Due Date, replace the display_book() function with the following new version:
function display_books($user) {
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "login";
$mysqli = new mysqli($servername, $username, $password, $dbname);
if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
} else {
$sql = "SELECT book_loan.book_id, book_loan.loan_date FROM user INNER JOIN book_loan ON user.id = book_loan.user_id WHERE user.username='$user'";
if($result=mysqli_query($mysqli, $sql)){
  if (mysqli_num_rows($result)>=1){
    echo "<table>";
    echo "<tr><td>". '<b>ISBN</b>' . "</td><td>" . '<b>Loan Date</b>' . "</td><td>" . '<b>Due Date</b>' . "</td></tr>";
    while ($row=mysqli_fetch_assoc($result))
      {
        $date1 = strtotime($row['loan_date']);
        $newformat1 = date('l, j F Y', $date1);
        $date2 = strtotime("+ 21 days", $date1);
        $newformat2 = date('l, j F Y',$date2);
        echo "<tr><td>". $row['book_id'] . "</td><td>" . $newformat1 . "</td><td>" . $newformat2 . "</td></tr>";
      }
    echo "</table>";
    mysqli_free_result($result);
  } else{
    echo "<h1>No results found</h1>";
  }
} else{
    echo "ERROR: Could not able to execute $sql. " . mysqli_error($mysqli);
}
$mysqli->close();
}
}
The first call to the PHP function strtotime() converts the string $row['loan_date'] to a Unix timestamp and returns this value to the variable $date1. The function date() is called to change $date1, passed as its second parameter, from a Unix timestamp to a readable format, indicated by the first parameter, ('l, j F Y').
        $date1 = strtotime($row['loan_date']);
        $newformat1 = date('l, j F Y', $date1);
In the example of the source code, the following characters are used for the format parameter:
  • l, a full textual representation of the day of the week

  • j, the day of the month without leading zeros

  • F, a full textual representation of a month

  • Y, a full numeric representation of a year

The second time strtotime() is called, it results in a Unix timestamp increased by 21 days, which is the specified period in the local library in this example for book loans. The date() call converts the Unix timestamp to the previous readable format:
        $date2 = strtotime("+ 21 days", $date1);
        $newformat2 = date('l, j F Y',$date2);
Finally, $newformat1 and $newformat2 are printed in the Loan Date and Due Date columns, respectively, as viewed in Figure 9-20.
../images/468152_1_En_9_Chapter/468152_1_En_9_Fig20_HTML.jpg
Figure 9-20

The new version of profile.php includes a Due Date column

Summary

In this chapter, you obtained an SSL certificate from a certificate authority to enable your site to implement cryptography without getting any browser warnings, just like well-known commercial sites.

You also created a project that allows the users to log in and remain connected to your site until they log out. While connected, they can view user-specific data provided by the site.

The most common ways to exchange data between the client-side JavaScript and server-side PHP language are discussed in the next chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.61.142