Basic Secure Web Application Programming Practices

  • Posted on: 6 August 2014
  • By: siteadm

When you learn that a company web server compromised because of a small programming mistake in PHP and it was possible to stop the attack by calling a function, you will want to learn more about all those "function calls".
Basically, in this post, I'll talk about possible attacks to web applications and how to stop them.
 

1) SQL Injection

I think it's most common vulnerability in dynamic web applications. Actually preventing SQL injection is not really hard. SQL injection happens when a web application doesn't check parameters received from browser and directly execute them in database server. So just by checking and sanitizing parameters, you can stop all type of SQL injections.

For example in PHP, you can use mysql_real_escape_string function and it would prevent almost all type of SQL injection attacks, but imagine this:

$id = mysql_real_escape_string($_POST['id']); 
$mQuery = "SELECT * FROM Users WHERE id = $id";

 If hacker enter "1049 OR 1=1" as id, mysql_real_escape_string wouldn't help much, because there is nothing to escape from in "1049 OR 1=1". So for additional layer of security, it's always safer to use some additional security layers based on your application design.
For example if you know ID always should be an integer value, check it. Simply call is_int function in PHP to verify parameter. Also another good security practice would be using Prepared Statements. So instead of code above, you can use prepared statements:

$id = mysql_real_escape_string($_POST['id']); 
if (!is_int($id)) display_error_and_return(); 
$stmt = $dbh->prepare("Select * from Users WHERE id = ?"); 
$stmt->bindParam(1, $id); $stmt->execute();

Now you can call above code bullet-proof. You can do same with ASP.NET:

SqlParameter[] myparm = new SqlParameter[1]; 
param[0] = new SqlParameter("@Id", postID); 
myQuery = "SELECT * FROM Users WHERE ID=@Id";

Also in ASP.NET, you can use TryParse function: if (!Int32.TryParse(postID, out integer)) display_error_and_return();

Also keep in mind that you should:
* Never grant any unnecessary permissions to web application user in database. Simply create a user in database and grant SELECT query only if user is not going to INSERT or UPDATE.
* You can do same type of sanitation for strings, above we used TryParse and is_int because we knew our parameter should be an integer. If you have a parameter that should be string, like Category name, when you know your category name is a single word, check for space character; if you know your category name should not have "=" sign, check for it.

 

2) File Uploaders

When you use a file uploader, you are opening your server doors to foreign files. You need to take necessary precautions to stop hackers:
2-a) If it's possible, encode filename and never store filenames as it is into disk. For example, if user uploads Test.jpg, generate a name like:

$mNow = date("Y-m-d H:i:s"); 
$mOrigName = $OriginalFileName; 
$NewName = md5($mNow + " --- " + $mOrigName;

Now store original filename in database and save file into disk with generated name and no extension or a neutralized extension like .tmp or .usrfile etc.

* Never show link to location you save uploaded files. For example don't show "Thank you for uploading your file, you can see your file here yourwebsite.com/uploadedfiles/Test.jpg"
* If possible upload files into a folder top of WWW folder, for example if your web root folder is /var/www/html, upload files to /var/www/uploads
* Store files in a random name folder instead of names like Upload, Uploads, UserFiles, UserUploads, etc. Just generate a random 10 char string like qS2lVDOL6o
* Never have execute permission on file upload folder, you can do achieve it using .htaccess in Apache and using IIS configuration in IIS.
* Alternatively, you can simply store files in DB (if possible), but again do not serve them back to user in anyway.
 

3) Local or Remote File Inclusion Attacks

As far as I've seen, PHP is most vulnerable to this attack. This happens when a code tries to include a page using dynamic variables. For example:

$NewsId = $_POST['nid'];
include $NewsID;

OR

require_once($NewsID);

This is actually really dangerous. Even hackers can execute a code in your server, simply by injecting PHP codes into apache errors log file by generating errors with PHP codes as error string, then simply including your error_log file using code above will result in direct code execution. Same happens with remote file inclusions. So basically, my advise would be totally stay away from dynamic page inclusion, if you are designing an application with this style, I think it's better to stay away from it and change your design.

If you have to have such design, for example for downloading files, like:

http://localhost/downloadfile.php?filename=products/PDF/design.pdf

I would still suggest staying away from this approach. This is not a good design at all, simply a hacker can read your /etc/passwd file just by entering ../../../../../../../../etc/passwd as filename parameter. Instead you can:

1) For each file, have an address and an ID in database, so generate links like: downloadfile.php?ID=49 and in your code simply query database for filename with ID=49, read it and serve it.

2) Serve file directly instead of downloadfile.php

You can also check for existance of ".." "/" "http" "ftp" "https" etc. But still, as I said above, this approach is not good at all, try to change it.

 

4) XSS

To prevent XSS in PHP:

$UserParam = strip_tags($UserParam);

ASP.NET:

UserParam = Microsoft.Security.Application.Sanitizer.GetSafeHtmlFragment(UserParam);

 

Techniques explained above is an intoduction toward secure web programming, but most of web application hacks are really this easy to prevent.

Comments

strip_tags() is *not* designed for security. Try it with "<p onclick=alert('xss'>this is a paragraph</p>".
You need to use htmlentities() (not bullet proof thoug), htmlspecialchars() or HTMLPurifier for PHP.

As of PHP 5.5, the mysql_* functions are deprecated and no longer maintained. Just use prepared statements, that's enough.

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.