COMP 10065: Module 5c Guide - Csunix.mohawkcollege.ca

Transcription

COMP 10065: Module 5c Guide Sam Scott, Mohawk College, 2020The topic of this submodule is web app security. Web app designers face special concernswhen it comes to protecting their apps and their user’s data from online vandals and burglars.This module presents an overview of some of the main security issues facing web app designersalong with some coding practices and tools that can help to mitigate these threats.1. The Danger of User InputServer-side PHP programs routinely receive input from users through HTTP Requestparameters sent from HTML forms. They take this user input and incorporate it into web pagesand SQL queries. In other words, user input is used to write code that gets executed on theclient or server machine. If programmers are not careful about how they handle this user input,they open their apps to various kinds of attacks. One common kind of attack is code injection.HTML Code InjectionTo see a simple, and relatively harmless, code injection attack in action, try the file hackMe.phpfrom the examples on Canvas. This app receives an HTTP Request parameter called param andechoes it to the page without using filter input to sanitize it first.To see why this is a problem, try the GET parameter show below in the URL:hackMe.php?param script location.reload() /script This benign hack causes a JavaScript command to be injected into the HTML code for the page.The command refreshes the page as soon as it’s loaded, effectively disabling it and wastingbrowser cycles and bandwidth. At the time of writing, this hack works on Firefox and IE but noton Chrome, which is smart enough to detect it (check the messages in the Chrome DeveloperConsole).The Problem: GETIf you take a look at the hackMe.php code, you’ll see that it is accessing the get parameterusing a method that has not yet been officially introduced in this course.echo " p GET[param] /p ";The above statement is retrieving the request parameter from a superglobal associative arraycalled GET instead of using filter input to do the retrieval. The superblogal POST is alsoavailable for post parameters and REQUEST contains all get and post parameters together aswell as any cookies received with the request. These variables are the oldest and mostestablished ways of receiving parameters in PHP.1

The Solution: Validate and SanitizeThe fix for the code injection problem above is easy: sanitize the parameter using filter inputbefore placing it on the page. The hackMeFixed.php example on Canvas does just that. It is notsusceptible to this code injection.There are other ways to sanitize and validate your parameters but filter input is considered abest practice solution by many programmers because it allows you to avoid using GET and POST anywhere in your code. If you never access these variables directly, then it is much lesslikely that you will accidentally expose yourself to a code injection attack.The Problem of Run-time ErrorsMaking sure you always use filter input with the correct filter type and check its return valuewill also insulate your code from run-time errors. Run-time errors are to be avoided becausethey can lead to a negative user experience, but they can also sometimes tip off a clever hackerabout other vulnerabilities in your system.For example, if your server is configured to display error messages and you access the POSTarray looking for a userid parameter, the user might see something like the message below.Notice: Undefined index: userid in https:\\example.com\library\utilities.php on line 2This error exposes:1. The language this part of the app is written in,2. The name of the parameter it receives,3. The URL of one of the PHP files involved in the app (which might not have been visible ifit was loaded using an include statement).All of this information could be used by a clever hacker to figure out a way to gain unauthorizedaccess to your app or your user data. For this reason, most production servers turn off errorreporting entirely. Error reporting is used only during development, and only on internaldevelopment servers. (We have left error reporting active on CSUnix only because it is used forteaching and learning.) Still, any server can be accidentally misconfigured, so you should alwaysavoid any possibility of run-time errors to avoid exposing vulnerabilities to a malicious user.2

SQL Code Injection AttacksIn this course, we showed you how to use PDO to prepare and execute statements withparameters (i.e. question marks) that get filled in, often using HTTP Request Parameters, whenyou execute.But an alternative and very dangerous practice would be to simply concatenate data from theuser directly into the command string.Here’s an example: id filter input(INPUT POST, "id", FILTER SANITIZE SPECIAL CHARS); deletecommand "DELETE FROM MyTable WHERE ID id"; stmt dbh- prepare( deletecommand); stmt- execute();User input concatenated directlyinto the SQL command string.The code above is vulnerable to an SQL Injection attack in which a hacker types some SQLsyntax into your form which then gets concatenated (i.e. injected) into your SQL commandstring and wreaks havoc on your database.A simple attack on the code above would be for the user to send a pollid parameter set tosomething like “1 OR 1”. In that case the command string becomesDELETE FROM MyTable WHERE ID 1 OR 1;Since 1 is interpreted as TRUE in SQL, the above is equivalent to:DELETE FROM MyTable WHERE (ID 1) OR (TRUE);The WHERE clause evaluates to TRUE for every row, so every row in the table gets deleted.Using other SQL Injection attacks, a clever hacker might be able to retrieve passwords or othersensitive data, login as an administrator, etc. The SQL Injection section of the SQL tutorial onW3Schools has a few other examples of possible injection attacks.Try Your own SQL InjectionYou can try out the example above using the sql injection example on Canvas.1. Import newtesttable.sql using phpMyAdmin. Browse the table.2. Load index.html and use the form to delete one record. Verify using phpMyAdmin.3. Load index.html again, and this time use “1 or 1” as the delete ID. Verify that the attackworked in phpMyAdmin.If you change index.html so that the form directs to deleteSafe.php instead of delete.php, theabove attack will not work. The deleteSafe.php version of the code uses a prepared statement3

parameter (a question mark) in the SQL command string, and then sends the data to fill in theparameter as an argument to the execute method.How Do Parameters Help?One way to think of the difference between using prepared statement parameters and justconcatenating the command string together is that the prepare method compiles the commandinto a function which accepts an argument and executes the command using that argument.Let’s call the function created by the prepare method in the above example deleteById. Then if id contains the string “1 OR 1” the execute statement in the above example calls deleteByIdlike this:deleteById(“1 OR 1”);The function goes to the database looking for rows with id set to “1 OR 1”. Since there are nosuch rows, the function deletes nothing. The parameter helped because input from the userwas treated as data rather than being executed as code.This SQL Injection example could also have been neutralized using FILTER VALIDATE INTinstead of FILTER SANITIZE SPECIAL CHARACTERS because “1 OR 1” is not an integer andwould have caused filter input to return false. To be as safe as possible, it is a best practice touse the correct input filter and also use parameters in the SQL query string as well. If multiplelines of defense are always in place, then all the code you write will be safer.Client Side ValidationThe story so far is that you must validate and sanitize all the data coming into your server, andyou must use prepared statement parameters for SQL so that your code is not vulnerable toinjection attacks and other possible hacks. But what if we carefully construct HTML forms anduse HTML attributes and JavaScript event listeners to validate the forms and prevent the userfrom submitting invalid data in the first place?For example, an HTML form could:-use the required attribute on an input element to prevent empty parameters,use type "number", type "email", etc. to force a parameter into the proper format,use a select element to force the user to pick from a list of valid choices,use JavaScript event listeners on the submit, input, or change events can to catch otherissues, even as the user types,etc.Using all the above tricks and more, you can lock down the user very effectively. Butunfortunately, client-side validation is not enough to make your app safe from attack. Theserver has no way of knowing where an HTTP Request is coming from or how it was created. Aclever user can enter the browser’s developer tools to turn off all the client-side validation, orcreate their own HTTP Request using a form or script written for that purpose.4

But client-side validation is still important for other reasons.-Client-side validation creates a nicer user experience. Users will not have to wait for apage load to find out they have submitted bad data, and with JavaScript, you can alertthem to problems as they fill in the form.-Client-side validation minimizes wasted resources by preventing HTTP Requestscontaining invalid data. Every bad HTTP Request wastes bandwidth and processing timeon the server, which has to create the HTTP Response, run scripts, and possibly executedatabase queries for the invalid request.The bottom line is that you must validate user input on both the server-side and the clientside of your app.5

2. Hiding Sensitive InformationSensitive information should be kept hidden as much as possible, even from trusted employees.Passwords, credit card numbers, and other sensitive data should not be visible on the device,should not be visible in transmission, and should not even be visible in the database oranywhere else on the server.Hiding Information on the DeviceHiding information on the device means making sure that people and surveillance devices inthe user’s environment do not have a view of it.MaskingMasking information means blocking all or part of it when displaying it on the page.Passwords can be masked using an input element with type "password". This shows thelength of the password, but not its contents.(JavaScript event listeners can also be used tocapture inputs and change the value attribute,masking the length of the password as well.)Credit card numbers should be masked, but inpractice, it is frustrating for the user to not be able tosee a long number while entering it. So, most appsdo not mask during initial entry but do mask most ofthe credit card number when echoing it forconfirmation.POST ParametersSensitive information should always be sent using an HTTP Post Request. This does not keepthe information safe from hackers, but it does stop the information from being viewed,bookmarked, copied, and pasted.Oops! Password sent in a GET requestcan be seen by anyone nearby.6

Hiding Information in TransmissionAny site that uses HTTP (Hypertext Transfer Protocol) is sending plain text data over anunencrypted connection. This information might pass through dozens of servers on its way toits destination, where it may get copied, cached, and harvested by malicious users. Using HTTPPost Requests does not hide data during transmission – anything you can see by viewing arequest in the Network tab of the Chrome Developer Tools can also be seen duringtransmission if the request uses HTTP.The solution is to use the HTTPS protocol (Hypertext Transfer Protocol Secure) when sendingsensitive data. This forces the browser and the server to exchange their public encryption keyswhich are used to turn every message into unreadable cryptographic code. Decoding requiresprivate encryption keys, which the server and browser keep to themselves.Encryption keys are integers, usually presented in hexadecimal. At the time of writing, thepublic key for https://wikipedia.com 446ae5f79de3 31 27 5e 41 99 58 92 86 35 4c 8f21 9c 86 aa 13 94 05 fe ae 9c fc b26b b2 79 d8 97 1f 9a 57 c2 ad 7b c3e2The above key was obtained by clicking on thepadlock icon next to the address bar in Chrome andselecting the certificate (see figure at right).Messages encrypted using a public key are notuncrackable but cracking them requires figuring outthe private key that goes with the public key. Thesophisticated mathematical used to generate thekeys ensure that solving this problem is intractablefor most users. They can figure out the private key intheory, but the computations involved will takemany years even on the fastest available machines.In order to use HTTPS for your web app, you have todo two things:1. Obtain an SSL Certificate containing a publickey, issued by a trusted authorityThe Wikipedia.com SSL Certificate.2. Redirect users from HTTP to HTTPS7

Obtaining an SSL CertificateAn SSL Certificate is a data file that the server sends to the client for verification. It contains theserver’s public key and domain name and is signed by a trusted authority that issued thecertificate. If the certificate is valid and the client trusts the authority that issued it, then it willinitiate communications using HTTPS. Otherwise, the user may find themselves blocked with asecurity warning from their browser. Most web hosting services will either include an SSLCertificate for free or charge a bit extra to obtain and install one for you.Redirecting to HTTPSCSUnix is a little unusual in that it only supports the HTTPS protocol. It does not support theHTTP protocol at all. If you type http://csunix.mohawkcollege.ca into the address bar ofChrome, you’ll get an error.Not supporting HTTP is fine for a student server, but it’s not a good idea to do this to users ofreal apps, who will often not type https:// when entering an address, leaving the defaulthttp:// protocol to be filled in by their browser. But if you support both protocols, then you’llhave to do something to redirect naïve users to the secure version of your site.The site example.com supports both protocols and does not redirect. Try these links and lookfor the padlock icon in the address bar in each case:http://example.comhttps://example.comOn the other hand, if you try to go to http://google.com, you will immediately be redirected tohttps://google.com and your searches, logins, etc. will all be encrypted and safe fortransmission.There are a number of ways to accomplish this redirection. At the server level, URL rewritingoften does the job. On an Apache server (e.g. CSUnix, XAMPP) you can include URL rewritingrules in a special file called .htaccess in the public html folder. Here is an example:RewriteEngine OnRewriteCond %{HTTPS} offRewriteRule (.*) https://%{HTTP HOST}%{REQUEST URI} [R 301,L]The above code directs the server to check for HTTPS in the URL of every HTTP Request. That’sthe RewriteCond (Rewrite Condition) line above. If HTTPS is not found (i.e. it’s “off”), theRewriteRule is executed. This rule instructs the server to send a response back to the browserincluding the HTTPS version of the URL and with a status of 301, which means that this site hasmoved permanently. The browser then launches a new request to the HTTPS URL.You can see all this happening in the Network tab of the Chrome Developer tools when youlaunch a request for google, facebook, or any other major site using the HTTP protocol. Youmight have to use the incognito browser to see it, though, because 301 responses may causethe browser to remember the rewritten URL and automatically translate future requests.8

Hiding Information on the ServerMasking, POST parameters, and HTTPS prevent user data from being seen on the device andduring transmission, but further steps must still be taken to make sure that the information ishidden from prying eyes on your servers.Strong Server PasswordsAll server passwords must be strong and kept secret. This includes database user passwords(for phpMyAdmin and connect.php) as well as FTP and remote login passwords. They are yourfirst line of defense against hackers who would steal your users’ data.PhpMyAdmin contains tools for generating secure passwords, as shown below.Session StorageNever store passwords or credit card numbers in the SESSION object. Information that youplace here will eventually be cached in plain text session files on the server for later retrieval.Anyone with access to the server will be able to retrieve the information in these files.Facebook recently caught themselves in exactly this ping-passwords-secure/Database StorageYou should not store plaintext sensitive information in your database either. Passwords shouldnot be visible to intruders who have broken into your system, nor even to your trustedemployees. Credit card numbers should be encrypted before storage and only decrypted whennecessary.Hashing and Salting PasswordsPasswords are stored using a kind of one-way encryption known as hashing. A hashing functionturns a password into an unrecognizable series of characters. It’s a one-way function – there’sno reliable way to get back the original password from the hash. When the user registers, youhash their password and store the hashed version in your database. When they log in again,you hash the password they entered in the login form and check it against the hashed passwordstored in the database. When passwords are handled in this way, no attacker or employee willever see an unencrypted user password. Knowing the hashed password is not helpful becauseyou need the original password to log in and you can’t get that from the hashed version.Although it’s not easy to compute the original password from its hashed version, it is possibleto precompute hash codes for a list of common or known passwords and then look up hashed9

passwords to find the original. A table of passwords and their hashed versions is called arainbow table. Modern hashing algorithms also use a process called salting to preventattackers using this technique. Salting makes this much more difficult by adding a random stringof characters (the salt) to each password so that the hashed version in the database is not adirect hash of the original password, but of the salted version of the password.In PHP, you can use the password hash function to generate a hashed and salted version of astring to store in the database. When the user attempts a login, you can retrieve the hashedpassword from the database, and then check it against what the user typed into the login formusing password verify. Examples are shown below and in the hashing code folder on Canvas.Hashing a New Password for Storage hash password hash( user password, PASSWORD DEFAULT);Original password entered by the user.Hashed and salted using the best algorithm available (the default one).This is what gets stored in the database.Verifying a Password to Log a User Inif ( password verify( userpwd, hash) ) {// granted}Original password entered by the user.Hashed and salted version from the database.10

Security Issues Quick ReferenceQuick Facts:Always validate and sanitize user input on both the client side and serverside of your apps. Always use SQL command string parameters. Forsensitive information: always mask, use POST, encrypt with HTTPS, andencrypt for storage. Use hashing and salting to store passwords. Neverstore passwords or other sensitive information in the SESSION object.Hashing: hash password hash( user pwd, PASSWORD DEFAULT)password verify( user pwd, hash)11

3 SQL Code Injection Attacks In this course, we showed you how to use PDO to prepare and execute statements with parameters (i.e. question marks) that get filled in, often using HTTP Request Parameters, when you execute. But an alternative and very dangerous practice would be to simply concatenate data from the user directly into the command string.