The Web Programming Checklist (for page at a time applications)
Security
- Exposing user data in the URL (shoulder surfing):
Solution: method=post vs method=get
- Exposing user data in transit: form variables etc. available over the network.
Solution: Use https, from the very first page. Have a real, not self signed certificate. You can ensure this via
one of the techniques mentioned in Redirect Request to SSL, or in php...
if($_SERVER["HTTPS"] != "on")
{
header("Location: https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
exit();
}
Just make sure you have not already sent cookies in the clear first.
Note: Researchers Devise New Attack Techniques Against SSL
- Storing users passwords
Solution: Store hashes of passwords, not passwords
See for example: md5
# psuedo code
$user=$_REQUEST["user"];
$pwd=$_REQUEST['password'];
$md5_pwd=md5($pwd); # ie. 1f3870be274f6c49b3e31a0c6728957f
$query="SELECT * FROM accounts where name=\'$user\' and password=\'$md5_pwd\';";
# above is still problematic (see SQL Injection)
- Using cookies to store application data:
Solution: Don't do it. Instead, use cookies to identify a users session. Store application data in the session.
- Session hijacking: Session cookies are flying back and forth across the network
Solution: Establish https connection before establishing the session. Not:
1) http://badpage.net
2) establish session and send login page (PHPSESSID already sent in the clear)
3) user login via https and rest of application done via https
4) attacker can use the session ID in their browser
Also, set the secure; httpOnly;
flags in your cookies. This makes sure that
cookes are sent encrypted (secure) and can only be used by http, not by Javascript (httpOnly).
- Long sessions, long lived cookies: Convenience to allow users to automatically login. If a user is compromised,
their session cookies can be stolen and used by an attacker to login.
Solution: Cookies and sessions should expire quickly, certainly not live past browser termination.
Applications can check if cookie has not been used in X minutes and automatically expire session data.
Also see Sessions and Security.
- Web server, application tools, configuration and permission etc. vulnerabilities. For example,
can users browse around by modifying URLS, can they get to other users session data, server DOS attacks?
Solution: Ask your admin to be careful. Take csc347. Patch servers, systems, tools, configure firewall, ...
- XSS and SQL Injection.
Solution:
- Use prepared statements for SQL, no
$query="SELECT stuff FROM preciousData WHERE user=" + $_REQUEST['user'] + " and amount>" +$_REQUEST['amount'];
instead, use
$dbconn = pg_connect();
$result = pg_prepare($dbconn, "myQuery", "SELECT stuff FROM preciousData WHERE user=$1 and amount>$2");
# check result
$result = pg_execute($dbconn, "myQuery", array($_REQUEST['user'], $_REQUEST['amount']));
if($row = pg_fetch_array($result)) {
# handle the returned row
} else {
$errors[]="invalid login";
}
- use, for example htmlspecialchars($string, ENT_QUOTES, 'UTF-8') when sending user data back to users in web pages.
- User uploaded files: file contents, file name, file location, file permissions
Solution:
- Check file sizes
- Application generated file names for storage, keep user supplied file name in a database (for example)
- Store files in a safe location (correct permissions etc., so access is controlled.
- Check file contents
- Sending user data back to other users: in web pages, as images, files
Solution:
Whitelist before sending, similar to htmlspecialchars for page content.
- Managing protected content (see two sections below)
- Application DOS attacks
Solution: Place resource limits on users. Backup frequently.
Security Solutions General Principles
- Know your inputs: Whitelist inputs (vs blacklisting). Don't trust anything.
- Form variables
- Cookies
- Uploaded files, uploaded file names
- Know your outputs: Make sure you send the user safe responces
- use, for example htmlspecialchars($string, ENT_QUOTES, 'UTF-8')
when sending user data back to users.
- careful about sending user supplied images, files, zip files. You may be putting your users at risk.
- General tools: is_numeric, preg_match
a tutorial
Application State vs Browser State (Low level PHP Solutions)
- The browsers back button, Bookmarking pages
------------------------------------
Firstpage:
------------------------------------
...
------------------------------------
Secondpage:
------------------------------------
# check if $_REQUEST['postback']==$_SESSION['postback']
- Browser cache
Solution: Combination of...
- Website performance
Solution:
Allow caching of images, and pages as appropriate, re-use images.
- Users can have multiple browsers pointed at the same application. At the same page.
Solution:
In some cases, ie wizards is a good example, definitive application state is maintained
in the back end. Application backend knows what the user is supposed to see, no matter what they
say they see. Application only handles user requests addressing current application state. All others
cause user state to be updated.
Application Mechanics (Low level PHP Solutions)
- Forms and page lifecycle
- Validation: Where? On the browser (maybe), certainly on the server
- What to do if inputs are invalid? Redisplay the form, with error messages and previously filled in values reapplied.
- Prefilling form values for update.
- Handling a postback vs a first time view.
Solution: One global approach is to write page code as follows...
- Protected content (ie. only authenticated users allowed here).
Handling login and different roles (users can still attempt to view pages they should not).
Have a stub at the top of your protected pages that ...
- Layout to create a uniform experience: include and require
- Design to create a uniform appearance: centralized in CSS
- Language independence, content and character sets. Lots to say here. We will say nothing. It is a big issue!
Mid Level Solutions
Build your own framework. Typically looks like:
- MVC (Model, View, Controller).
Note: Page at a time MVC lacks change notification arrow.
- Use a front controller. All requests go through the front controller.
It runs a state machine, keeping current page state etc. Submits
go back to the current page state for processing. A subsequent page state
may result.
- Maintain client side state on the server. Which page you expect
the client to submit from. Include a 'token' with a random value.
Check the tokens value when they resubmit.
- Validation takes place on the server. Upon submit, the server checks
if this is a postback. Sends inputs to the page state for validation.
Validation includes collecting error messages in a predefined array.
This array appears on the returned page (usually the current page
states page).
- To prefill/refill form values, input elements must be selectively coded
on the back end to populate them with appropriate values.
High level solution: Use an existing Framework
Use a framework (ie) CodeIgniter, Struts, ASP.NET, DJango, RubyOnRails, ... and hope the framework does
much of what you need done. They typically include:
- Special front end tags, especially for form inputs. Sometimes other useful tags, ie populate an HTML table
with backend collection.
- Templating engine (to help with document structure).
- Declarative page sequencing. Which pages can follow from which pages.
- Declarative validation. What values a particular form variable can take.
- Automatic validation, including form reposts with error messages.
- Claimed programmer and language independence. Designers can focus on design etc.
Issues:
- Many have near vertical learning curves.
- Framework applications end up looking like 'framework applications', they may end up looking like and flowing like the schema.
- Versions makes a difference. Struts1 vs Struts2 are very different. Change requires application re-write.
- Many front end psuedo tag solutions end up being complete programming languages on their own, becomming the problem they were supposed to solve in the first place.
- Getting developers may be a problem for new or old frameworks.
- Application ages: may be hard to update with new web ideas if not framework supported.
- You can only do what the framework lets you do, the way the framework wants you to do it.
- Patching webserver, web server software etc. may break framework.
- Framework may have its own collection of vulnerabilities and issues, causing you to update and maintain
code more than you might like. Forced framework update means forced application update, QA etc.
- Correctly using the frameworks is problematic.
- For declaritive frameworks, simple changes take place in many places, leading to tedious application development
and maintenance. Add a new backend field means: update forms, add form variables, add collection of field descriptors,
add validation logic, add error messages, ... all in potentially different places.
- Framework may be slow, won't scale well.
Application Scalability
Ummm, yea. A whole other topic!