Skip to main content
Engineering LibreTexts

5: Tying it Together

  • Page ID
    45738
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)

    Section 5:

    Tying it Together

    Learning Goals:

    By the end of this section, you should be able to demonstrate:

    • The ability to create a site that incorporates multiple languages
    • The ability to extract information from a database and present it in a site
    • Basic security considerations to deter malicious attacks
    • The ability to interact with user data and alter a page as a result

    Chapter 43

    Security

    Risk

    It can be easy to both over and under consider the security of your website. New initiates tend to do everything possible to secure their code, which often results in overblown measures that can take considerable time to incorporate. This may only serve to protect something that may not require security in the first place. The dangers, then, are: getting tired of the complicated process, becoming complacent because nothing has happened, or neglecting to consider security in the first place. As a result, systems end up exposed.

    Risk management is the practice of finding the sweet spot where the time and cost of implementing the measure is proportionate and acceptable to the perceived threat a compromise would create. Risk analysis is another of the many topics in this text that begs for much deeper study, and I would encourage everyone to read beyond what is here as it applies to everyone involved in a programming project.

    In short, some things to consider about your system are its level of exposure, cost to acquire/replace its contents, importance of contents in relation to the company, and importance of the contents in relation to your clients. A number of industries are also bound to compliance measures based on certain types of information, or in order to achieve certain certifications.

    Some questions to ask are things like:

    1. Is this system only available on our network, or is it publically exposed?
    2. Is this system only accessible through other security mechanisms like credentialing systems, SSL, etc.?
    3. How much would it cost to replace the data the system holds?
    4. Would it be possible to replace the data if it is lost?
    5. Does the value of the data change if it is stolen or made publically available?
    6. If the data is compromised, how would it affect our relationship with our customers?
    7. Is any of the information required to be secured to a certain level?
    8. Are we an industry (e.g., healthcare) that has to comply with a federal or other reporting guideline (e.g., HIPAA)

    Answering these questions can be difficult, as you need to find quantifiable measures to questions that are better suited to qualitative analysis. An example of how to approach this is to determine risk values that address each topic. If data is irreplaceable, it might be an 8/10. If compromise poses no risk if stolen or exposed, it may be a 1/10. Using the same min/max range for each value allows us to add up our scores in order to get a summary value of the risk. This value can help you see the importance of your system better. From here, you can determine based on its value, particular values, or “red line” items that require specific minimums like federal requirements, exactly what security implementations you will need. You can also create estimates of what damages (data recovery costs, man hours to fix/replace the system, loss from lawsuits, etc.) might cost against that of implementing specific security measures as a cost/benefit ratio. For example, a mitigating effort of installing or developing a credentialing system might cost $5,000 dollars, but if the data is exposed a lawsuit might entitle the plaintiff to $80,000 dollars in damages. How much is spent to defend a system in relation to what the perceived losses might be will ultimately be determined by your executives and what the analysis reveals.

    A common response to this issue is what if the system never gets attacked? Then the costs would be wasted! Alas, in many cases it can be hard to know or prove that risk management efforts actually stopped or prevented an attack. Time and money spent on security are difficult to defend, as their return on investment can rarely be proven. Was the system not compromised because of your efforts, or because no one tried? The typical reaction is to consider the survivability of the organization if the system is compromised or lost, and this is where strong metrics and analysis can save the day, the system, and maybe even your job.

    Our example above of measuring each item on a fixed scale to determine a single threat value is just one mechanism. It is good for quick (relatively speaking) analysis and as a means of measuring multiple systems, or different possible versions of a system, against each other. More advanced techniques take more factors into consideration. For example, the NIST SP 800-301 analysis includes a threat-likelihood-impact matrix where values are assigned to each element (the amount of likeliness of a given threat compared to its impact), repeated for each threat. Placing things into the matrix helps to quantify the scenario.

    To perform a full risk analysis, you will want to consider all possible sources of damage to your system. These include intentionally malicious or accidental actions of users both in and outside of your company (just because a user is authorized to access data does not mean it is being used correctly or as intended), natural or man-made disasters that would affect your system (floods = water; water + electronics = bad), or just general failure (power surge + electronics = bad). For the remainder here, we will only focus on the former as it directly applies to the topics we address in the rest of the text.

    To delve deeper, look at some of the United States federal risk management initiatives.

    One parting note: No matter the size of your organization, even if you are the lone programmer, a risk analysis exercise should involve many minds, especially those of upper management. Their determinations will weigh heavily on your activities, and their exposure to these exercises will increase their awareness overall.

    Now that we have an idea of how to discover and quantify risks, we can look at some basic methods of protecting ourselves.

    PHP

    One of the easiest ways for a malicious user to attempt to take advantage of our database, or gain unauthorized access, is to use our own scripting language against us. Let us consider a basic example. Say you have a form that takes a username and password to log in a user. Even if you employ an encrypted password with hashing, a malicious person could still take advantage of your form. Since your form action and field names are available to anyone who views your page’s source code, they could read your form to generate the following URL: http://yoursite.com/youractionpage.p...password=12345’ or 1

    Since they are using your form’s variable names, your action page will, in this basic example, assume they are legitimate. The key element to note here is the apostrophe or 1 portion of the password field. If you are accepting user input without checking it first, like this:

    1. mysqli_query("select * from users where username='$_GET[username]' and password='".md5($_GET[password])."'");

    Your populated query would actually read as follows:

    1. select * from users where username='FAKEUSER' and password='12345' or 1

    When evaluated, the malicious user is automatically logged in because the “or 1” makes the logic statement true, since “or true” will always be true, regardless of the first half of the statement. Your malicious user is now logged into your site! If you did not follow the “least needed privileges” theory (we will look at this next), they would then have access to anything in the database that the account is allowed to access—potentially even other databases.

    To protect against this, we can use the sanitization5 and validation6 features of PHP to ensure our users’ responses are valid.

    For example, if we ask a user to enter their email, we can first remove anything that should not be in an email address:

    1. $email = filter_var($_GET['email'], FILTER_SANITIZE_EMAIL);

    And then make sure it is still in proper format:

    1. if(filter_var($email, FILTER_VALIDATE_EMAIL)){echo "OK!";}
    2. else{ echo "Please re-enter email!"; }

    The above code would be OK for a regular email address, but anything missing an @, or a top level domain like .com, would ask for it to be re-entered. Our sanitization for an email address will remove all characters except letters, digits and !#$%&’*+-/=?^_`{|}~@.[]. You typically want to sanitize before validating. If your sanitization removes something that was actually a desired part of the input, the validation would then fail.

    MySQL

    The source (for us, at least) of the risk: the data itself. We are storing our data in MySQL, which our web pages interface with in order to build our interactive experience. When we store user credentials in our database, we need to consider the fact that since our website communicates with the database, there is an inherent weakness. This is true whether or not our database is on the same physical system, or lives in the same operating system, as the site itself. One of the basic mechanisms to protecting your site is to prevent unauthorized access. Part of this can be achieved by obfuscating our users’ passwords when we store this. To do this, we can employ a couple of tactics called hashing and salting.

    Hashing is the process of passing the password through a mathematical algorithm that turns the password into a much longer string of characters. The algorithm is designed to be one-way, meaning a hash cannot be passed to an algorithm that reveals the original text. The algorithm will produce the same hash every time for a given password (although occasionally duplicates may occur, a fault called a “hash collision”). The result is stored in the database in place of the user’s actual password. Each time the user logs in, they type in their original password, your authentication system passes it through the algorithm, and you can compare the result to the stored value to make sure they match.

    The use of a hash ensures that if someone is able to gain access to your database, they cannot simply copy the credentials and use them to their advantage, as the stored value is not the actual password they would use. A couple of popular methods that have been used to achieve this are MD5 and SHA-1, among others. I discourage the use of MD5, as its hashes are comparatively shorter than many others in use today, and enough data has been gathered about the algorithm that many password hashes can already be found just by using sites like md5decrypter.com.

    Another issue of only relying on the original hashing algorithm is that other sites may be doing the same. If someothersite.com and your site are both using MD5 by itself, and someothersite.com is compromised, the attacker would have the usernames and passwords for their site. If any of your users also used someothersite.com, odds are some of them are probably using the same email address, username, and password. With all of this data, the attacker now has a set of usernames and passwords to try on your site. Since you are using the same hashing mechanism, the credentials used on both sites would work on yours as well.

    The hashed value of the word password, for example, returns to us the value 5f4dcc3b5aa765d61d8327deb882cf99. If you take this value and paste it into md5decrypter.com, you will see our original text (password) given back to us.

    Never fear, however, as we can defend against this with a salt. Salting is the process of adding something extra to your user’s password, like adding salt to food to make it better. This should ensure that even if a user has the same password on the other site, the one stored in our database is still different. This means a malicious person cannot use the encrypted password to get into our site.

    If the salt for our user was #hsy5, our user’s password that we would hash could be password#hsy5 or #hsy5password or even pass#hsy5word. When we hash password#hsy5, we get 5b48480a7171f41d2bf52093f4850281. Now, if someone tries to use known passwords, their hash for password will not work on our site. You can use salts by either creating a salt for every password, or using one that is coded directly into your script.

    Keep in mind that if you are using a single salt for all values, make sure it is not running in a script the user would have access to, like a linked JavaScript value, or would appear in your HTML as a hidden value in a form. This gives them the missing key to circumvent the extra protection you added. By only having the salt in one script, malicious agents would need to gain access to your web server as well as your database, especially if this script is segregated from your database’s home system. Storing a unique salt for each password means if one salt is discovered, not every account has been compromised. If the compromised account allows reading the salt table though, other accounts would eventually be accessible. Ultimately, using the best hashing algorithm available and both salting approaches would provide a high level of protection. How much you actually need to implement should be determined by completing a risk analysis.

    Another strong self-protection method is to make sure your user accounts in your database are as locked down as possible. By this I mean abiding by a “least-needed security” approach. It is easy when developing to create a user account for your project with a simple password and wide open permissions to make development easier. Once you move your project live, however, you should take away all the permissions your users would not need and should not have. For example, your basic end users will likely not need the ability to alter or drop tables, so you should remove user access to those features. This means that if injection methods are used on your site, even if they succeed, the malicious threat still could not drop or alter your tables without also finding a vulnerability in your database.

    To provide administrative users with an interface, you can set those pages to utilize a database user with higher credentials. The most sensitive operations (full database deletions, creating new administrators, etc.) should still be left in the hands of users who log into your database directly from a secured machine.

    In MySQL, the full list of permissions elements are: select, insert, update, delete, index, alter, create, drop

    Administrative privileges are: create temp table, file, lock table, process, reload, replication, show dbs, shutdown super.

    We can quickly assign users the initial set of actions or the administrative set by using the keywords “usage” or “all” when assigning the account. Most sites can achieve everything necessary with the limited permissions of select, insert, update, and maybe delete. Keep in mind although overall this is quite restrictive, without proper precautions injection attempts could still use insert or update to infiltrate your site.

    Depending on the element, security can be restricted to the user based on the levels of global (your entire database server, i.e., every database), database, table, or column. This granularity allows you to ensure users can only change what they need to.

    Our database de jour for this text also allows us to require SSL connections, providing greater security between the user (an admin or our webserver) and the database. MySQL also allows us to limit the number of updates over a span of time, as well as the number of concurrent connections and queries per hour. Typically, we would want these values very high to support the highest volume of end users possible, but you may have a use case where you know these numbers should be in a certain range. For example, if your company has 50 employees, and only 40 need your site, then you could cap your concurrent users to 40. Anything beyond that could be an indicator of database problems or hacking.

    Types of Hashes

    There are a number of strong hashing algorithms available. Some of these are considered out-dated and are easily to reverse, like DES. Others are still widely in use even though databases of known encrypted/unencrypted values for many passwords are freely available, like md5. Sites like md5decrypter.com are examples of this weakness. For this reason, you will likely want to favor the newer approaches available to you at the time. As new encryption algorithms are created, the encrypted string lengths become much larger, making the computation power required to reverse them too time consuming (if even possible) for the current hardware available.

    Credentials

    Never say never, but never give root. Root, or the default user on your MySQL server, has the highest level of permissions in the system. This means a PHP script that uses the root account to establish a connection can do anything, even drop all databases. Because of the damage an accidental or malicious command can have on your data, any MySQL user account that your web server utilizes to interact with your data should be as limited as possible. The vast majority of applications can get by with simple CRUD actions (that is create, read update, and delete). In most implementations, I would go one step further and not even allow deleting, replacing it instead with an “inactive” flag for records to hide them from the user as if they were deleted. Depending on the system, your web users might need additional features like temporary tables, and these should be allowed only as needed. This means if the attacker tries to perform administrative type actions, and you are not preventing them, they will still be blocked as the user they are acting as will not allow it.

    JavaScript

    We always (Yes, always—laziness breeds poor security in our world) want to take a look at anything a user gives us before we interact with it. This is for two reasons: first, the user may have made a mistake. Maybe they mistyped an email address, or left a required field blank. Or, perhaps, the user is a malicious person or script attempting to do something other than what we intend with access to our site. It might be using our forms to spam others, gain access to our data, or make unsolicited changes to our site. We already discussed this under PHP but we can attack the problem with JavaScript too.

    Taking this into account, before we use anything a user gave us, we need to make sure (as much as possible) that it is safe data to interact with. Usually we want to do as much of this as possible on the user (or client) side, so they do not need to click submit and wait for a response from the server to find out something is not quite right. To do this, we can use client-side scripting like JavaScript to make sure things are OK as we progress. As fields are changed, JavaScript can look at the content and make sure addresses are formatted correctly, required fields are filled in, etc. Coloring, highlighting, or providing messages to the user when problems occur. We can achieve this easily by tapping into jQuery’s validation library:

    1. <script src="/lib/js/jquery.validate.js"></script>
    2. <script>
    3. $(document).ready(function(){$("#commentForm").validate(
    4. cname : { required : true, minlength: 2 }
    5. );}
    6. );
    7. </script>

    This example would execute the validation once the form is loaded, showing that the cname field is required and the minimum length is two characters. Not only can jQuery help us display these requirements on the form itself, we can call the validator as fields are changed and/or when the form is submitted before leaving the page to enforce the rules we provided.

    In terms of user experience, this is typically done in real time. As soon as a user leaves a field, the script makes sure it is OK, and provides confirmation of the fact (typically a green highlighting or “OK!” type of marker) or by not marking the field as bad (typically red, or prompting the user to re-enter the field).

    Once the form is completed, JavaScript should ensure that the user’s submission will be good on the first try (at least content-wise—we cannot confirm things like a username and password without talking to the server). This accounts for our number one concern: mistakes from the user. Even though we checked the submission, we want to repeat this process on the server-side in more depth. If the user is malicious they may be circumventing our page, or the user may have JavaScript disabled.

    The server-side script should take into account the nefarious user. If someone tried to subvert our form, JavaScript probably caught it. If, however, we are using GET or they use a script to send data directly to our action page from our form (which they can easily find in our page source) then they can get around our JavaScript.

    Execution Functions

    Both PHP and JavaScript support features that allow the user to access and run other programs or scripts on the web server or local system. This can be useful when you want to interface with another application or system that the language does not have the ability to communicate with directly, but it exposes a huge security risk. Anything passed to these functions will be executed as if that user was sitting at the command prompt of your web server. The implications here are fairly obvious, as anything your server’s “web user” account has permission for would be allowed. If you are passing a variable into the execute function, you have created a path directly to the heart of your system.

    The best bet is to avoid using these entirely unless absolutely necessary. If you must, ensure that variables are not passed to the function if at all possible to prevent injection. Finally, if all else fails, sanitize and validate anything passed, limit your web server user role as much as possible, and keep your system as up to date as possible to deter hackers.

    In PHP you will want to avoid the exec() function. JavaScript is a bit more removed, but some actions can create the ability, such as creating an ActiveX object:

    1. <script>
    2. var wsh = new ActiveXObject('WScript.Shell');
    3. wsh.run('notepad.exe');
    4. </script>

    Segregated Systems

    Your database server, ideally, would not live under the same operating system as your web server. This does not mean the same OS cannot be used on both systems, but that they are not residing on the same exact installation. This is important because if your system is exposed to, or faces, the Internet it is at a higher risk of compromise. Keeping the database within your network with a single controlled access point between the two means your data is not as compromised if the web server is.

    Learn more

    Keywords, search terms: Web server security, risk management, secure programming

    76 Tips for Securing Your Server: http://www.rackaid.com/resources/server-security-tips/

    Apache’s Security Tips: http://httpd.apache.org/docs/2.2/misc/security_tips.html

    Symantec’s Tips for MySQL: http://www.symantec.com/connect/articles/securing-mysql-step-step

    Chapter 44

    Integration Examples

    The following code examples will demonstrate how the languages we have studied can be combined to create dynamic systems. In each of these examples two or more elements covered in the text will be combined. These examples are not intended to function fully based on the excerpts you will see, but are meant to demonstrate methods of integrating the languages.

    Connecting to MySQL

    In order to have our users interact with our database, we need to establish a bridge of communication between them. To do this, we will create a connection to our database and store it in a variable. Through PHP we have a variety of methods of creating this connection, among them are libraries called MySQL, MySQLi, and PDO. These libraries have different approaches to interacting with a database, and there are differences in what commands are available and how the connection is treated. While you will see many examples online that use the standard MySQL connector, I would warn you away from it. It is on its way to deprecation, and has little injection protection built in. The MySQLi library is the one we will focus on in our examples here as it is a better start for entry level programmers. Ultimately, once you are comfortable with integrating these languages, I would recommend moving to PDO. While it is not tailored for MySQL, it supports a wider range of SQL databases that will allow you to more easily change your backend system.

    To begin, we will call a function to create our connection. The shortest avenue to do this is as follows:

    1. $mysql = mysqli->connect("localhost","user","password","database");

    By inserting your server’s values in each set of quotes, the variable $mysql will become our line of communication to our MySQL database. When we created our connection by using a class method, our $mysql variable is now a MySQLi object. We could also have used procedural style with mysqli_connect. Assuming your database is the same system as your website, “localhost” or “127.0.0.1” should work just fine for you. Your username and password can be any account that exists in your SQL database. In a fresh installation, “root” as the user and “root,” “password,” or nothing—“” as a password will usually let you in, but as we saw in security, you should avoid this unless necessary and only on a low-risk machine. The declaration of the database we want to use is optional, but saves us from having to select one later or always declare our database in our queries.

    In the spirit of this section, we will revise this example to make it more useful. By replacing our values with variables, we can keep our actual values apart from the rest of the code. Additionally, we can separate our connection out to its own file. By doing this, we can require() or include() it on any page that we need a connection. Then all we need to do when we use our database is remember to close the connection at the bottom of our page. An additional advantage is that we could also wrap our connection in a class of our own, allowing us to re-declare from our own class what functions are available. For now, we will keep it simpler:

    1. $host = "localhost";
    2. $user = "username";
    3. $password = "password";
    4. $dbase = "database";
    5. $mysql = mysql->connect($host, $user, $password, $dbase);

    If this code is its own file like database.php, we could use it in all of our sites, simply changing the values at the top to match the settings for the site it is in. To get information from our database, create or modify our databases, or create or change records, we use the same exact queries that we did from the command prompt. Our only difference is that we do it through a function, and store the response in a variable:

    1. $results = $mysql->query("select * from albums");

    The $results variable here, like our connection variable, is a reference. It is a pointer that lets us continue to communicate with the database and specifies what we are looking for, but is not the actual data. To get the data, we need to ask for each record. Since our example is very small, we will get all of the results at once, and build an array:

    1. while($row = $results->fetch_assoc(){
    2. $data[]=$row;
    3. }

    This block of code uses the while statement to get every record available in our result set. Note that we used the variable with our result pointer to get the results, and not our connection itself. Inside our loop, we are simply taking the row (in this case, each album from our albums table) and adding it to a new array called data.

    Secured Login

    Logging into a web page involves receiving user input, sanitizing and validating their submission, appending any salts, hashing the submission, and sending it to the database for verification. If the database responds that the user’s credentials match what is stored we can then continue and create a cookie and/or session so the user can interact with secured content.

    Creating a User:

    1. <?php
    2. //create our salt
    3. $salt=^%r8yuyg;
    4. //store the filtered, salted, hashed version of the password
    5. $passwordHash = sha1(filter_var($_POST['password'].$salt, FILTER_SANITIZE_STRING));
    6. //Add the user to the database
    7. $sql = 'INSERT INTO user ($username, passwordHash) VALUES (?,?)';
    8. $result = $db->query($sql, array($_POST['username'], $passwordHash));
    9. ?>

    Logging them in:

    1. <?php
    2. //Prep their login credentials
    3. $passwordHash = sha1(filter_var($_POST['password'].$salt, FILTER_SANITIZE_STRING));
    4. $sql = 'SELECT username FROM user WHERE username = ? AND passwordHash = ?';
    5. $result = $db->query($sql, array($_POST['username'], $passwordHash));
    6. //This time, look at the result to see if they exist
    7. if ($result->numRows() < 1){
    8. echo 'Sorry, your username or password was incorrect!';
    9. }
    10. else{
    11. // Create the session
    12. $session_start();
    13. $_SESSION['active'] = true;
    14. echo ('Welcome back!);
    15. }
    16. ?>

    Dynamic Canvas

    By adding a loop in a canvas drawing of a circle, and using the random number function in the math library of JavaScript, we can instruct the browser to draw circles (in this example, 1000) of random shapes, sizes, and colors, all over our canvas. Each time the page is loaded or refreshed in the browser, the circles will be redrawn, and since we are using random values, this means our image will change each time.

    1. <canvas id="myCanvas" width="600" height="600"></canvas>
    2. <script>
    3. var canvas = document.getElementById("canvas");
    4. var ctx = canvas.getContext("2d");
    5. var w = canvas.width, h = canvas.height; //Set variables of the width, height of the canvas
    6. var i = 0;
    7. do {
    8. ctx.fillStyle = "rgb(" + Math.round(255*Math.random()) + "," // Creates an R value
    9. + Math.round(255*Math.random()) + "," // … and for G
    10. + Math.round(255*Math.random()) + ")"; // … and for B
    11. ctx.beginPath();
    12. ctx.arc(w*Math.random(), h*Math.random(), // creates our random size
    13. 50*Math.random(),
    14. 0, Math.PI*2, true); // Uses Pi*2 to make the arc a circle
    15. ctx.closePath();
    16. ctx.fill();
    17. } while (++i != 1000); // Loops this do 999 more times
    18. </script>

    Table of Results

    If we only need to display the information to the user on the screen, or do not plan on manipulating the data or using the data elsewhere on our page, creating the array of results can be a wasteful use of memory. Instead, we will modify our while loop to create a table of our results:

    1. <table width="75%">
    2. <tr><th>Title</th><th>Artist</th><th>Year</th><tr>
    3. <?php
    4. while($row = $results->fetch_assoc(){
    5. echo "<tr><td>$row[title]</td><td>$row[artist]</td><td>$row[year]</td></tr>";
    6. }
    7. ?>
    8. </table>

    This approach only stores one record at a time in our $row variable and is much more conservative, especially when using larger data sets. You will notice we nested our PHP within regular html in this example. Take a look at what our whole page might look like, assuming we also created our database.php file:

    1. <?php
    2. require("database.php"); // Now that this file is loaded, we can use $mysql on this page
    3. $query = "select title, artist, year from albums";
    4. $results = $mysql->query($query);
    5. ?>
    6. <table width="75%">
    7. <tr><th>Title</th><th>Artist</th><th>Year</th><tr>
    8. <?php
    9. while($row = $results->fetch_assoc(){
    10. echo "<tr><td>$row[title]</td><td>$row[artist]</td><td>$row[year]</td></tr>";
    11. }
    12. ?>
    13. </table>

    At this point, we now have all of the data we need from the database, and since we will not need the database anymore for this example, we can close our connection by adding the following after our table:

    1. <?php mysql->close($mysql); ?>

    Repopulating Forms

    If a user has submitted a form or is editing existing data, we can use these values to repopulate our form so the user does not have to type them in again. This is done by echoing the value inside the quotes in the value attribute of the form element. For example, if our name was in the URI as page.php?name=myName, we could make it the value of the input field with:

    1. <form action='page.php' method='get'>
    2. <input type='text' value='<?php echo $_GET['name']; ?>' />
    3. </form>

    By suppressing errors with error_reporting, using this technique, and with a little logic, we can combine all of the elements of providing the form, validating the submission, and taking action on what the form is for, all from one single page.

    With some careful planning, we can provide our user with a form, check their submission, and store their information, all from one page. First we need to create some pseudo-code of the flow of logic so we can dictate what we want to do, and under what conditions:

    1. <?php
    2. if(form has not been submitted){
    3. show user the blank form
    4. }
    5. else{
    6. check the form for errors
    7. if (there are errors){
    8. show user their form and data
    9. }
    10. }
    11. ?>

    By following the logical order of events, the above pseudo-code represents what would be a perfectly functional page. With a couple of tweaks, however, we can make some improvements. First, in this example, we would have to create the same form twice—once blank, and again with placeholders for what the user submitted. While we could use copy/paste and then modify the second copy, this will greatly inflate the overall size of our page. We can also simplify our logic by reversing the order of events in our code. First, we will see if the form has been submitted. If it has, we will check for errors. If there are none, we will complete the submission. After that, we will simply display the form with placeholders for user submitted data if there are errors or the form has not been submitted. This will cover both cases in one place and replaces our if/else and nested if with three if statements:

    1. <?php
    2. if(form as been submitted){
    3. check it for errors.
    4. create a status flag declaring if there are errors or not
    5. }
    6. if(the status flag is set to "ok"){ // The form must have been submitted, and there are no errors
    7. submit the user info to the database
    8. send any confirmation emails
    9. display a success message
    10. set the status flag to show the form is complete
    11. }
    12. if(the status flag is anything other than "ok" or does not exist){ //either there were errors, or the form has not been submitted
    13. show the form with placeholders for submitted data
    14. }
    15. ?>

    To make this form more flexible, we can declare an array of field names in our first if statement that lists what elements in our table are required or need to be validated. Once we have done this, we can check each submitted field to see if it needs to be checked, pass it through the appropriate tests, and create a list of feedback. There are a number of ways to approach this. Here we will create an array of responses as our error flag. If there are no errors, we will simply set it to “OK.” We will create a hidden field with a known value that will be included every time the form is submitted. This will help deter outside scripts from using our form, as they would need to know to include the hidden field (which we will check for in our logic) in order for our script to respond. In our example, we will make a short registration form:

    1. if($_GET[hiddenDate]==now() [check]){
    2. $check = array('firstName', 'lastName', 'email', 'email2'); //We will require these fields
    3. foreach($check as $field){ [make sure & is in php section] [include "for each thing in" way to remember
    4. if($field in $_GET) [verify]{
    5. //sanitize variable
    6. if(length < 3){ //establishes that our required fields should be at least 3 characters long
    7. $_GET[$field]="; //clear the user submitted value as it does not qualify
    8. $errors[]="$field is required and must be at least 3 characters long";
    9. }
    10. }
    11. }
    12. if($_GET['email'] != $_GET['email2']){ // Make sure the user entered the same email twice
    13. $errors[]="Both email fields must match";
    14. $_GET['email'=''; $_GET['email2']='';
    15. }
    16. else{ // email wasn't entered and/or fields matched
    17. if(!empty($_GET['email'])){ // Eliminate the possibility that email is simply empty
    18. if(validate($_GET['email'], EMAIL)==false{$errors[]="Invalid email address" [check]; // We only need to validate one, since they are the same
    19. $_GET['email']=''; $_GET['email2'];
    20. }
    21. }
    22. if(!isset($errors)){ // if nothing tripped an error, the array was never created
    23. $errors='ok'; // Set the flag to 'ok' (or 1, or whatever else you like) so next section fires
    24. }
    25. }

    We have now checked all of the required fields from our form. We may have had more fields, but for whatever reason are not concerned enough about their contents to require or validate them. You may be wondering why we are validating in PHP since JavaScript can do this before the form is submitted in real time. There are two reasons (re)validating with PHP can help you. First, is the more obvious case in which the end user’s browser, firewall network policy, etc. has disabled JavaScript. While your form would still function, you would end up with possibly invalid data. Second is that any bots that find your form and attempt to use it maliciously are likely to read your form’s destination and send data directly to your processing script, also circumventing your validation. This allows you to add sanitization through PHP in addition to other safety precautions to further harden your site.

    Next we will take a look at some options of what we can do once the form has been checked and is OK. Ultimately, there are two main tasks for you here. The first, is do whatever it is you want to do with what the user gave you—email the results, store them in a database, process a registration or login, use them to search data or affect output, etc. The second, is to provide feedback that demonstrates to the user how their action affected the system. It could be search results, a “successful” notice like “Thank You for Registering!” or the result of their interaction, like them being logged into the system.

    1. if($check=='ok'){ // email ourselves a copy of what they submitted and tell them they are done
    2. mail("us@oursite.com","$_GET[firstName] created and account.", print_r($_GET,true),"From: noreply@oursite.com");
    3. echo "Thank you for registering!";
    4. }

    Finally, our last logical test will be true if the user has not submitted anything or if there were errors. By creating this section as follows, we can support both cases at the same time:

    1. if($errors!='ok'){ //there were errors or the form is not submitted ?>
    2. foreach($errors as $error){echo "$error</br>";}
    3. <form action='<?php echo $_SERVER['PHP_SELF']; ?>' method='get' name='registration'>
    4. <input type='text' name='firstName' value='<?php echo $_GET['firstName']; ?>' /><br/>
    5. <input type='text' name='lastName' value='<?php echo $_GET['lastName']; ?>' /><br/>
    6. <input type='text' name='email' value='<?php echo $_GET['email']; ?>' /><br/>
    7. <input type='text' name='email2' value='<?php echo $_GET['email2']; ?>' /><br/>
    8. <input type='submit' name='Register' value='submit' />
    9. </form>
    10. <?php } ?>

    In our last section, the foreach in the second line will print any errors that were added to the array. Since we have reached this point, $errors either is an array and our entries will print to the screen, or it was never set, and will not show anything on the screen if we are suppressing notices. If you want to avoid the notice generated when the form has not been submitted, we could wrap line 2 with an If statement:

    1. if(!empty($errors)){foreach($errors as $error){echo "$error</br>";}}

    In our form you will see we re-entered PHP inside of the value attribute of each input. By echoing the value of the input in our get array, if there is one, we will re-populate our form with what the user entered. Since the first section of our code already checked these values if the form was submitted, any bad entries will have already been reset to nothing, helping the user see what needs to be re-entered.

    This effectively completes our one page form, validation, and response. We could add jQuery validation on top of our form elements to improve the user experience as well by validating during the form completion process, but bear in mind this is a progressive enhancement, meaning we should assume JavaScript is off, and that anything we use that works improves upon an already working system.

    Drag and Drop

    Certain tags in HTML5 now support the ability to be treated as drag and droppable items. Items that support the ability allow for attributes including draggable, ondragenter, ondragover, ondragstart, ondragend, and ondrop. When we want to define the actions that take place when one of these conditions is met, we need to call a JavaScript function, that we define ourselves. We will look at our example by creating it in layers, first defining the structure with HTML, then adding our CSS apply our visual, and finally we will add our JavaScript to give it full functionality.

    The first piece of our structure is to define the places in our page where moveable objects are allowed to be. These will typically represent the start and end areas that we are allowed to move objects to and from, like a product page to a shopping cart icon, or just two big empty areas. We will create a simple two location page for now. To define our two areas that are drag and drop friendly, we define our divs as we are accustomed to doing and simply add the references to actions that are allowed, or that we want to instigate actions or changes in our visual cues:

    1. <div id="startingLocation" ondragenter="return dragenter(event)" ondragover="return hover(event)" ondrop="return drop(event)"> </div>
    2. <div id="endingLocation" ondragenter="return dragenter(event)" ondragover="return hover(event)" ondrop="return drop(event)"> </div>

    Next, we will add the objects we want to interact with. They need a place to live when the page loads, so we will put them in the startingLocation div.

    1. <div id="startingLocation" ondragenter="return dragenter(event)" ondragover="return hover(event)" ondrop="return drop(event)">
    2. <div id="item1" draggable="true" ondragstart="return start(event)" ondragend="return end(event)">Item #1</div>
    3. <div id="item2" draggable="true" ondragstart="return start(event)" ondragend="return end(event)">Item #2</div>
    4. <div id="item3" draggable="true" ondragstart="return start(event)" ondragend="return end(event)">Item #3</div>
    5. </div>

    While this now gives us a drag and drop foundation, it is not exactly user friendly yet. If you save and test what we have, you will find a very blank screen that is probably rather difficult to interact with as we cannot tell where the different objects start and end, and even at that we have no actions. To address this, we need to add some CSS to our file:

    1. <style type="text/css">
    2. #startingLocation, #endingLocation{
    3. Float:left;
    4. Width:200px;
    5. Height:200px;
    6. Margin:10px;
    7. }
    8. #startingLocation{
    9. Background-color:red;
    10. }
    11. #endingLocation{
    12. Background-color:green;
    13. }
    14. #item1, #item2, #item3{
    15. Width:60px;
    16. Height:60px;
    17. Padding:5px;
    18. Margin:10px;
    19. }
    20. </style>

    To give us functionality, we need to add JavaScript to dictate what happens when items are moved around on the screen. We need to provide the start function permission to move items, dictate what information it needs to bring with the dragged object, and what to display when the object is in motion:

    1. <script type="text/javascript">
    2. function start(event){
    3. //Give the draggable object permission to move
    4. event.dataTransfer.effectAllowed='move';
    5. //Grabs the dragged items ID for reference
    6. event.dataTranser.setData("id",event.target.getAttribute('id'));
    7. // Sets our drag image with no offset
    8. event.dataTransfer.setDragImage(event.target, 0, 0);
    9. return true;
    10. }
    11. </script>

    Next, we need to define what happens to our objects when they are held over an area that takes drops. To do this, we will add the definition of the hover() function we referred to when we created our HTML:

    1. function hover(){
    2. //reads the ID we provided of the dragged item
    3. var iddraggable = event.dataTransfer.getData("id");
    4. // reads the ID of the object we are hovering over
    5. var id = event.target.getAttribute('id');
    6. //All items can be dropped into endingLocation
    7. if(id=='endingLocation') return false; else return true;
    8. }

    If we wanted to declare that only our first two draggable items are allowed into the endingLocation box, we would change our if statement to specify which items are allowed:

    1. If(id=='endingLocation')&& (iddraggable== 'item1' || iddraggable=='item2') return false;

    Next we need to complete the act of moving our item to its new location. We will add one more function we have already made reference to in our HTML, drop():

    1. function drop(){
    2. var iddraggable event.dataTransfer.getData('id');
    3. event.target.appendChild(document.getElementById(iddraggable);
    4. event.stopPropagation();
    5. return false;
    6. }

    Finally, we need to clean up. Now that our item is dropped, we do not need to worry about its value any longer:

    1. function end(){
    2. event.dataTransfer.clearData('id');
    3. return true;
    4. }

    If we were going to use our drag and drop system as a shopping cart, we would want to flesh out more actions in our end function. We would add code to add the item to the session or cookie record of our shopping cart, and could trigger other actions like updating our cart total on the screen or prompting for a number of that item we want in our cart.

    Chapter 45

    Finishing Touches

    Search Engine Optimization (SEO)

    Search engine optimization is the process of making your site the best possible candidate for favorable listing placement in search engine results. The factors that weigh in on a site’s scoring and ranking and constantly growing and evolving, and encompass far greater than just the correspondence between a searched word or phrase against the page content in your site.

    Covering all aspects of SEO is a task in and of itself just for one search engine. Accounting for the differences between Google, Yahoo, Bing, and all the others would be a text in and of itself and would be out of date before the printer finished the first page.

    True optimization is an almost daily task, involving monitoring changes in algorithms, refining the site as content changes, and using systems like Google’s Ad Sense for commercial placements on search results pages. Even at that, portions of today’s SEO algorithms are out of your control, in that they take into account things that are out of your control like previous traffic, outside links, and more. Start with these basics first to get a good start:

    1. Make sure you are mobile optimized
    2. Use meta tags and <h> tags to emphasize important content
    3. Use those same keywords as page titles
    4. Do not include too many keywords in your meta tags
    5. Update your content regularly
    6. Integrate with social media for exposure

    Analytics

    A close cousin to the tasks of optimizing your site for search engines is optimizing it for your target audience. There is no better way to do this than to understand who your audience is, which you can find in your website logs. You can learn surprising things with analytics, which has spurred many of the extreme large data sets now in existence, and resulted in some of the more controversial features of websites like tailoring ads to your interests or recent searches.

    Within your web server’s logs, you might find that your visitors are coming from countries you had not anticipated—signs that may induce you to add additional language support, improving their experience. You might find that your users are trying to use mobile devices that you were not quite supporting, or that they are using a resolution higher than you thought, allowing you to redesign your site to provide more content or a smaller interface.

    Tracking where a user goes, what they look at, how long they look at it. In fact, every action they take can provide insight. Aggregating this data across a volume of users over time give you the means to discover things you (and perhaps even your users) are not aware of.

    You can see these data sets in use when you see features like “Other users also viewed this show” or “You recently looked at this item.” Following up on trends by interviewing users has also shown companies how to covert more leads. To address users leaving with items in their shopping cart before purchasing, some companies discovered through surveys that most of those users decided against a purchase because of shipping costs, or uncertainty over taxes and surcharges. This allowed them to make changes to their site, providing more information sooner to address concerns, and allowed for particular marketing techniques. Instead of making all shipping free, a number of companies will trigger automatic follow-up emails when digital shopping carts are abandoned, or orders are immediately canceled, by offering free shipping or additional coupons or discounts to convince the shopper to complete the sale, improving their conversion rate.

    To begin with analytics, you can start by taking a look at the raw logs your server creates. Since our example throughout the book has been using a LAMP, your log location is probably in /etc/httpd/logs. A file search for the word logs will likely reveal the correct folder for your particular installation. Depending on your server configuration this folder may have one or more files that track things like people accessing files on your site (everything from pages to images and anything else a user can see or use) as well as errors that were encountered or reported by elements of your server involved in rendering pages.

    These files by themselves are not easy to interpret in aggregate form until you have spent time working with them or are seeking answers that are small amounts of the record like the last error or searching for a particular file. Instead, most people who interact with logs prefer to use an outside program that reads the files and helps them see trends over time so they can extrapolate more information than the raw file provides by itself. A free, open source solution (for personal use) that you can use to this end is AWStats, which compiles your logs into a variety of charts, graphs, and tables that focus on a number of aspects of your site like where visitors are coming from, what browser they used, what pages are most popular, and more.

    For more specialized information, many sites create databases that track everything about their site, a user’s entire experience, and apply everything else they know about a user to determine larger questions like how best to get the customer to return to the site again or what coupons to offer them like the shopping cart example above.

    Privacy Statement

    You may wish to include (and abide by) a privacy statement for your users. Spell out exactly what type of information you collect about them, how long it is stored, whether or not you will share or sell their information to another party, and so on. This is also a good place to spell out or link to a location where they can request that their information be removed as well.

    This type of statement will allow users to determine if they are comfortable using your site and gives transparency into how you will treat their data.

    Terms of Use

    In much the same vein as a privacy statement you may also wish to include a terms of use that clarifies to the user the extent to which they are allowed to use your site and its information. We are all familiar with the epic novel terms of use documents that are frequently included with software and hardware purchases these days, but there is a growing movement to embrace a more non-lawyer friendly set of these types of documents.

    Which one you decide to use is up to you, and there a great deal of examples in the wild. This is one area in which you may still want to include a lawyer, attorney, or your company’s legal department before posting.

    Chapter 46

    Now What?

    By this point you are hopefully comfortable with the fundamentals of web design, and have the ability to balance and integrate at least those languages covered within in order to optimize your site and provide a positive, useful experience for your end users. You have, as some might say, just enough knowledge to be dangerous. There is still far more to learn. Each topic we covered in this text was a tip of the iceberg. The languages we covered, methods and approaches, design techniques, histories and all go into much greater depth.

    This text took effort to give you an understanding of each topic, but in many implementations each of these topics is addressed by different parties. Fortune 500 companies typically have one or more employees addressing focused topics such as networking, database development, front-end programming, etc. The consumer research and design aspects alone have created entire departments. Who your team is composed of and how many of you there are become determined by the size of the project, the size of your company, the particular needs and complexities of the project in questions, as well as time line and budget. Similarly sized, timed, and budgeted projects may have distinctly difference staffing simply because one is a financial system requiring input from accountants and lawyers and the other is a museum, requiring input from librarians and art historians.

    Now it is time to get some real projects under your belt. After a few full implementations, you will learn which aspects are of most interest or come most naturally to you. Once you have these identified, you have found your niche. Then it is time to go deeper, learning the facets and minutia of one or more of the topics we covered. The references section is a treasure trove of excellent resources to go deeper into any of the topics covered here.


    This page titled 5: Tying it Together is shared under a CC BY-SA license and was authored, remixed, and/or curated by Michael Mendez (Open SUNY Textbooks, Milne Library) .

    • Was this article helpful?