Use PHP To Check Whether Remote URL, Email Or Image Link Exist

In PHP, we have a built-in function file_exist that can help us to verify whether a particular file exist in our directory. However, do you ever have the need to check whether a particular URL, email or image link exist? We can use regular express to validate that the syntax of an email is correct but won't it be nice to reduce the amount of spam mail received? How about those images you have on your site? Won't you want to check whether there is a broken image or url link? Well, i have! Its always good to be informed in advance than meeting dissatisfy visitors. Anyway, in this article you will get to find and learn some of the more efficient and quicker ways to verify whether a particular link exist to use on your web application.

Check Remote Image Link Exist

There are many ways to check whether a particular image link exist after the introduce of PHP 5, GetImageSize. GetImageSize allows us to take in a remote link to retrieve the size of the image. Hence, we can do a simple check such as the one shown below,

$external_link = 'http://www.example.com/example.jpg';
if (@GetImageSize($external_link)) {
echo  "image exists ";
} else {
echo  "image does not exist ";
}

The above work well for any server that had GD installed. But there are more problem than just the one mention. This method is actually inefficient as it will download the entire image into your server before checking it. Thus, making the process very long. There might also be security risk as mention on Secure File Upload Check List that many image format allow comment to be embedded within the image and these comment might just be some PHP code that hacker has written. In short, this method download file from remote server to your server and take the risk of hacker using it to run malicious code after it has been downloaded on your server. BOMB! So if you are using the above method i advice you to change it and if you insist to use this, you can provide more validation checking for it. Just drop it.

So how do we check Image link in a more secure and quick environment? If you are using curl, you can try the following script:

function checkRemoteFile($url)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL,$url);
    // don't download content
    curl_setopt($ch, CURLOPT_NOBODY, 1);
    curl_setopt($ch, CURLOPT_FAILONERROR, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    if(curl_exec($ch)!==FALSE)
    {
        return true;
    }
    else
    {
        return false;
    }
}

Like i said, this will depend on curl. However, it is secure and quick! How about the rest of us? Lucky, PHP also provides another method called file_get_contents. file_get_contents returns the file in a string, starting at the specified offset up to maxlen bytes. On failure, file_get_contents() will return FALSE. With these in mind we can create a function to check that the file downloaded is valid by taking only 1 bytes of information from the file. Hence, whatever evil things exist on the file will only be able to run 1 byte and furthermore the function returns a string where code cannot be run. Thus, this will gives us a simple solution such as the one below,

function url_exists($url) {
if(@file_get_contents($url,0,NULL,0,1))
{return 1;}
else
{ return 0;}
}

The above one is more secure than the initial one that we have and it has no dependency. The speed for this method is also faster than the initial one. But is there a faster one than the curl version?

Check For Remote URL Link

There are many ways to check whether a remote url link exist. However, checking Remote URL required the page to return certain header code to indicate that the page is successfully loaded (200). Hence, you might not want to use the method for checking image link for url link. Furthermore, For different cases you might be interested with different solution. Assuming you are just checking whether an existing domain exist, you can use the following code

function url_exists($url){
    if(strstr($url,  "http:// ")) $url = str_replace( "http:// ",  " ", $url);
    $fp = @fsockopen($url, 80);
    if($fp === false) return false;
    return true;
}

which is written by adam at darkhousemedia dot com. The above method definitely run faster than fopen but for https and domain names but for path url such as 'http://example.com?p=231', the above won't work although the speed is definitely one of the fastest.

Nonetheless, there are still better alternative than the one presented. We can just tried to check the header with the following code:

function url_exists($url){
     if ((strpos($url,  "http ")) === false) $url =  "http:// " . $url;
     if (is_array(@get_headers($url)))
          return true;
     else
          return false;
}

The above work perfectly without the need to worry about complex code. However, the above method only work for HTTP and PHP 5 and above, other lower version of PHP will not. Therefore, we will need some modification on the above method to cater for lower PHP version.

function is_valid_url($url)
{
    $url = @parse_url($url);
    if (!$url)
    {
        return false;
    }
    $url = array_map('trim', $url);
    $url['port'] = (!isset($url['port'])) ? 80 : (int)$url['port'];
    $path = (isset($url['path'])) ? $url['path'] : '';
    if ($path == '')
    {
        $path = '/';
    }
    $path .= (isset($url['query'])) ?  "?$url[query] " : '';
    if (isset($url['host']) AND $url['host'] != gethostbyname($url['host']))
    {
        if (PHP_VERSION  >= 5)
        {
            $headers = get_headers( "$url[scheme]://$url[host]:$url[port]$path ");
        }
        else
        {
            $fp = fsockopen($url['host'], $url['port'], $errno, $errstr, 30);
            if (!$fp)
            {
                return false;
            }
            fputs($fp,  "HEAD $path HTTP/1.1\r\nHost: $url[host]\r\n\r\n ");
            $headers = fread($fp, 4096);
            fclose($fp);
        }
        $headers = (is_array($headers)) ? implode( "\n ", $headers) : $headers;
        return (bool)preg_match('#^HTTP/.*\s+[(200|301|302)]+\s#i', $headers);
    }
    return false;
}

The code here is the more detail version of the previous one which is created by SecondV on forums Dot digitalpoint Dot com. The above code cater for lower PHP version while still using the same approach of getting the return header value. Furthermore, it also validate the URL by using the parse_url method. f_open can also be used to check remote URL link.

function image_exist($url) {
	if (@fclose(@fopen( $url,  "r "))) {
	 // true;
	} else {
	 // false;
	}
}

However, this method required allow_url_fopen to be enabled on your php.ini file or else it will fail. Furthermore, i will not prefer this method over the previous one as it seems more sense that the page return success due to header code to indicate a success.

Check Email Exist

We can't actually check whether a given email exist or not as it really depend on how the SMTP is being setup for each respective mail server. Nonetheless, we are still able to check whether a given domain exist to reduce the number of invalid ones. PHP has a function checkdnsrr which does the work nicely.

function email_exist($email) {
	list($userid, $domain) = split( "@ ", $email);
	if (checkdnsrr($domain,  "MX ")) { return true;} else { return false;}
}

Using the function above will help us to verify whether a particular domain exist to trick user that you have an email checker if they really intend to fake one. However, Windows doesn't support such function yet. Hence, we will need to create such function just for Windows server.

if(!function_exists('checkdnsrr'))
function checkdnsrr($hostName, $recType = '')
{
 if(!empty($hostName)) {
   if( $recType == '' ) $recType =  "MX ";
   exec( "nslookup -type=$recType $hostName ", $result);
   // check each line to find the one that starts with the host
   // name. If it exists then the function succeeded.
   foreach ($result as $line) {
     if(eregi( "^$hostName ",$line)) {
       return true;
     }
   }
   // otherwise there was no mail handler for the domain
   return false;
 }
 return false;
}

Once you have cater for both Linux and Windows server, you may want to create a full flag function to check on email such as the one shown below:

function check_email($email)
{
	$email_error = false;
	$Email = htmlspecialchars(stripslashes(strip_tags(trim($email)))); //parse unnecessary characters to prevent exploits
	if ($Email ==  " ") { email_error = true; }
	elseif (!eregi( "^([a-zA-Z0-9._-])+@([a-zA-Z0-9._-])+\.([a-zA-Z0-9._-])([a-zA-Z0-9._-])+ ", $Email)) { email_error = true; }
	else {
	list($Email, $domain) = split( "@ ", $Email, 2);
		if (! checkdnsrr($domain,  "MX ")) { email_error = true; }
		else {
		$array = array($Email, $domain);
		$Email = implode( "@ ", $array);
		}
	} 
	if (email_error) { return false; } else{return true;}
}

Now we know why we need to verify our email after we sign up for any particular services online! Since we cannot check whether a particular email exist, we will force to send out verification email to our user before they are able to access our portal. However, if you are interested to check through PHP forcefully, you may want to visit webdigi. They create a mail class to verify an email through checking the port of mail SMTP 25 but like i said previously it really depend on how each mail server is being design. This might not work. But we can still force user to verify their email through a simple script shown below,

function check_email($email)
{
	$email_error = false;
	$Email = htmlspecialchars(stripslashes(strip_tags(trim($email)))); //parse unnecessary characters to prevent exploits
	if ($Email ==  " ") { email_error = true; }
	elseif (!eregi( "^([a-zA-Z0-9._-])+@([a-zA-Z0-9._-])+\.([a-zA-Z0-9._-])([a-zA-Z0-9._-])+ ", $Email)) { email_error = true; }
	else {
	list($Email, $domain) = split( "@ ", $Email, 2);
		if (! checkdnsrr($domain,  "MX ")) { email_error = true; }
		else {
		$array = array($Email, $domain);
		$Email = implode( "@ ", $array);
		}
	} 
	if (email_error) { return false; } else{return true;}
}
function EmailValidation($email) {
    if (check_email($email)) {
    $domain = explode(  "@ ", $email );
        if ( @fsockopen ($domain[1],80,$errno,$errstr,3)) {
			$code =  "here we place a secret key  with the email address: $email ";
			mail($email,  "Your Verification Code ",  "Please click the following URL to verify your email:\n\n  ". $_SERVER['PHP_SELF']. "/?v=$code amp;email=$email ", "From: \ "example\ "  <[email protected] > ");
			echo  "Your account needs to be verify. We have send you an email, click the link provided and you are verified. ";
			return true;
		} else {
            return false; //if a connection cannot be established return false
        }
    } else {
        return false; //if email address is an invalid format return false
    }
} 
function EmailForm(){
    if(empty($_POST['email'])){
        echo  " <form action= ".$_SERVER['PHP_SELF']. " method='post' >
         <table border='0' >
         <tr >
         <td >Email </td >
         <td > <input name='email' type='text' id='email' / > </td >
         </tr >
         <tr >
         <td > amp;nbsp; </td >
         <td > <input type='submit' name='Submit' value='Validate' / > </td >
         </tr >
         </table >
         </form > ";
    } elseif(isset($_POST['email'])) { 
        if(EmailValidation($_POST['email'])) {
            echo  "An email has been sent to you. Please follow the instructions to activate your account. ";
        } else {
            echo  "Your email address appears to be invalid. Please try again. ";
        }
    }else elseif(isset($_GET['v'])  amp; amp; isset($_GET['email'])) {
		$clean['emai'] = $_GET['email']; //need to filter these data to be clean
		$clean['v'] = $_GET['v']; //need to filter these data to be clean
		$code =  "here we place a secret key  with the email address: $email ";
		$code = md5($code);
		if ($clean['v'] != $code) {
			echo  "The Verification Code is invalid. Please Try Again. ";
			exit(0);
		}else
		echo  "The email  ".$clean['emai']. " has been verified ";
	}else { 
        echo  "An error has occured, please contact the administrator. "; 
    }
} 
EmailForm();

Might be a bit confusing but i believe you will get the above code since its quite simple. Instead of breaking them into different pages, i sum them up on a single one.

Quick check whether link is broken

Here is another tips to check whether a link is broken.

$file = 'http://www.domain.com/somefile.jpg';
$file_headers = @get_headers($file);
if($file_headers[0] == 'HTTP/1.1 404 Not Found') {
    $exists = false;
}
else {
    $exists = true;
}

And the same version on a curl version

function url_exists($url) {
    if (!$fp = curl_init($url)) return false;
    return true;
}

credit goes to havran @ http://www.php.net/manual/en/function.file-exists.php#74469

Summary

The above solution can help many people to verify the content that the user has entered. Remember it is not safe to trust user input and such verification can come in handle. On the other hand, this can also help us to check broken and invalid links so that we get the information we need from our users. The information above might not be solid but it is good enough for me and hoping it will work for you.

Solutions to Cross-Site Request Forgery Attacks

Let's see..previously i was researching on session attacks and find it really useful in ensuring a web portal to be secured on the server side. Preventing client side security was also discovered on XSS. However, i missed out something important on the client side that is required to protect my users! Another important security measure that i forgotten was cross-site request forgery! Therefore, in this article i will write all the things i know about such attack and how serious it can affect your web portal.

Cross-Site Request Forgery

Cross-site request forgery which is also known as CSRF. CSRF attacks exploit the trust that a site has for a particular user. This means that the attacks is launched by one of the users of the website. Unlike XSS which exploits the trust a user has for a particular site, CSRF exploits the trust that a site has in a user's browser. As mention on XSS article where images can be placed with URL instead of image is the exact thing that is happening on CSRF.

Example of Cross-Site Request Forgery

Let's consider an example of cross-site request forgery. Assume there is a user 'Attacker A' in your system. Your system is an auction site or any site that required credit on an account in order to perform a service or purchase a product. Currently, 'Attack A' do not have any credit. He wants to find a way to get it and CSRF is something he wished to do.
Let's say the attacker send an email to the administrator to enquiry him on a particular issues and embedded some harmful links back to their site as shown below,

Hi,

i have a problem updating the following images 

<img src='http://example.com/givecredit.php?give=clay&credit=9999' width='200px' height='200px'/>
into your form <a href='http://example.com/givecredit.php?give=clay&credit=9999'>this</a> particular page to purchase credit. 
i tried and it return me an error message. Please help me out on this. 

Regards,
Clay

On the PHP part we have a handler givecredit.php as written below,

<?php
	#validate session and cookie to check whether the user is logged in
	if(isLogin())
	{
		$clean['give'] = filter($REQUEST['give']);
		$clean['credit'] = filter($REQUEST['credit']);
		addAmount($clean['give'], $clean['credit']);
		#other instructions
	}
?>

Our not careful administrator entered the link on the email to verify (this is also used to trick administrator of a site to attempt an XSS attack) and an attack was launch on the site easily. Please take note that the display on the email is totally pure text and images without any html code written unless the user change the mode of viewing. On the other hand, the administrator is logged in, the code will be valid and whatever within the code will be run. This is the best way to illustrate cross-site request forgery (CSRF).

Requirement of Cross-site request forgery

An attacker will have to consider a few requirement before CSRF can be achieved.

  • A site that doesn't check the Referrer header (which is common) or a victim with a browser or plugin bug that allows Referrer spoofing
  • A form submission page that is useful for the attacker (example, getcredit.php)
  • Guess all the correct value needed for such submission page to be valid. If there is a need for a valid identifier in the previous page and the attacker can't guess, CSRF will fail.
  • Attack must lure the user to the page or mail to click on the link provided or visit the page to cause the damage

Different CSRF Attacks Location

There are many places that CSRF codes can be placed to lure victim into clicking them. Some doesn't even required any clicking as it is embedded on the page itself. Below are some examples.

#IMG SRC
  <img src="http://example.com/getcredit.php?give=clay&credit=9999">
 
#SCRIPT SRC
  <script src="http://example.com/getcredit.php?give=clay&credit=9999">
 
#IFRAME SRC
  <iframe src="http://example.com/getcredit.php?give=clay&credit=9999">
  
#JavaScript Methods
'Image' Object
  <script>
  var obj = new Image();
  obj.src = "http://example.com/getcredit.php?give=clay&credit=9999";
  </script>
 
#'XMLHTTP' Object, $_POST is vulnerable as shown below
 #IE
<script>
	var post_data = 'give=clay&credit=9999';
	var xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
	xmlhttp.open("POST", 'http://example.com/getcredit.php', true);
	xmlhttp.onreadystatechange = function () { 
	if (xmlhttp.readyState == 4) 
		xmlhttp.responseText;
	
	}; 
	xmlhttp.send(post_data);
</script>
 
#Mozilla
  <script>
  var post_data = 'give=clay&credit=9999';
  var xmlhttp=new XMLHttpRequest();
  xmlhttp.open("POST", 'http://example.com/path/file.ext', true);
  xmlhttp.onreadystatechange = function () {
  if (xmlhttp.readyState == 4)
	xmlhttp.responseText;
  
  };
  xmlhttp.send(post_data);
      </script>

As shown above, using GET, POST or REQUEST can still be vulnerable. But using POST can caused certain level of difficulty for the attackers. Thus, using POST is still preferable.

Solutions to cross-site request forgery

We see above how CSRF can be used to attack our server and caused important monetary value to be wrongly distributed. We won't want this to happen to our users. Hence, some protection has to be implement into our server. On the other hand, end user will also be advisable to perform certain action on a portal to further protect themselves in a website (in the case where CSRF can be launch against you)

End user

Below list some solutions for end user to protect themselves from poor implementation site.

Always log out

It is a good practice to always log out any web application after finish using. Logging out will help ensure any attempt to use your account for attacker benefit is eliminated.

Don't save password/username

It is best not to save any password and username on any browser to prevent other form of attacks. 'Remember Me' feature should not be used as it will usually logged in the user automatically upon accessing the site. Hence, 'Always log out' principle became invalid since every visit will always automatic logged in the user.

Use difference browser for different purpose

Pure surfing and accessing sensitive data on your web application should be separated to prevent attacks on your sensitive data. Default browser will always be used for pure surfing while other non default browser should be used for sensitive data access usage. This will help ensure that links or unknown page accidentally visited will not caused major damage to your sensitive data.

Plain text email

Although it is always secure to view your email in plain text to easily identify any attacks and eliminate script from running through opening an email. However, it is not practical to do this. Thus, always try to use plain text for any suspicious email or check the links on the mail before clicking it.

Avoid Spam Mail

Spam mail are usually mails that contains such attacks which are being reported by other users to the email provider. Hence, avoid displaying external images or not clicking links in "spam" or untrusted e-mails may also help.

Developer

Below listed some ways to protect our users against cross-site request forgery attacks.

Always use POST method

Although using POST can still be vulnerable to CSRF attacks through flash application or XMLHttps request as shown above. Nonetheless, it does make it harder for attackers to exploit your system structure than using pure GET or REQUEST method. However, this doesn't really solved CSRF attack problem.

Always check HTTP Referrer header

Another simple counter measurement against CSRF attacks is to check the HTTP Referrer header. However, the header is optional and may not be present. In some browsers, it is disabled and is not available when interactions occur between HTTPS and HTTP served pages. The risk of header spoofing exists, and tracking the valid sources of invocations may be difficult in some applications. Hence, empty or invalid HTTP Referrer header should be marked as dangerous and access should be denial.

Automatic Log out

Implement automatic log out mechanism for your web application will greatly help your user to secure their account from being attacked. One of the criteria that makes CSRF attack possible is when user remain logged in to the website while links or page is being visited. Automatic log out mechanism can help minimize such mistake made by the users and reduce the risk of CSRF attacks.

Authenticate POST and Cookie data

Another security measure is to authenticate your variable POST and Cookie data. We can ensure that the global variable is being authenticate by ensuring certain criteria is being met such as length, data type, value and etc. This can help ensure other form of attack is eliminated and possible CSRF attack is being minimized. On the other hand, Cookie data can be manipulated by attacker and is required to be authenticate always through some information such as information previously stored in the cookie that can be verify through your database or user browser.

Double Confirm Form Submission

Another simple method you can apply is to write a simple script to alert the user of an action to be performed on behalf of the user before processing so that the user is aware that some form of submission is being create out. Another more proper way of handling such confirmation is to send in an email or sms to authenticate the action by providing an action code to be submitted by the account user themselves. This will help prevent the attackers from getting what they want when they do not hold the email or sms of the system provided. (sniffing is possible but the whole process harden the system security)

Secret Key Required

Make all form submission required a secret key to be passed along. The key will act as a protection for our user as the key is generated by a self made authentication process that will change in every form submission. typically this key is tied to the user session and validate that you get the same value back in the form post. In this way, we can ensure that the session contains such secret key and post method also return such key to verify that the submission is valid and previous page was a form. The form is the one which initial the session key and post key that will be bought to the submission page. If the user have not visited the form page and was bought directly to the form submission page, the attack will fail since the key wasn't provided and generated on the form page previously. Consider the following form which create the key initially.

<?php
session_start();
$key = md5(uniqid(rand(), TRUE));
$_SESSION['key'] = $key;
$_SESSION['key_time'] = time();
 
?>
<form action="givecredit.php" method="post">
<input type="hidden" name="key" value="<?php echo $key; ?>" />
<p>
To: <input type="text" name="give" /><br />
Credit: <input type="text" name="credit" /><br />
<input type="submit" value="Submit" />
</p>
</form>

On the form submission page we will validate whether the submission and post value is the same.

<?php
if(isset($_POST['key']) && isset($_SESSION['key']))
if ($_POST['key'] == $_SESSION['key'])
{
   $key_age = time() - $_SESSION['key_time'];
	if ($key_age <= 600)
	{
		/* Less than ten minutes has passed. */
	}
}
?>

if we didn't get the post key value and session key value to be identical, we can assume there is an attempt of attack on our system and you might consider to log that up for further investigation and protection.

Below contains some recommended reading which is pretty good in this topic.

Good Reading

Below are some resources on CSRF

Summary

Even Digg, GMail, and Wikipedia suffer CSRF attacks previously. Do not make such mistake and protect your system carefully before it become a problem in your system. The solutions above might not be full bullet proof solution for future cross-site request forgery attacks. Nonetheless, it can be used for discussion on solutions of future such attack.

Solutions To Session Attacks

Recently i wrote two other security article on XSS and SQL Injection. I find many interesting facts and solutions on those topic that i research about and wanted to know more about other security measure. Thus, in this article i will discuss on different type of session attacks and how we can better protect ourselves against these attacks to better secure our web portal.

Session attack is nothing more than session hijacking. The most important information in session attack is to obtain a valid valid session identifier (SID). There are three common methods used to obtain a valid session identifier.

  • Session Prediction
  • Session Capture
  • Session Fixation

Session Prediction

Prediction refers to guessing a valid session identifier. With PHP's native session mechanism, the session identifier is extremely random. Hence, it is difficult to guess such SID. Although it is not the weakest point of a secure website. There are still chances of accessing the site through this method.

Solution To Session Prediction

Use of a longer random number or string as the session key. This reduces the risk that an attacker could simply guess a valid session key through trial and error or brute force attacks.

Session Capture

Capturing a valid session identifier is the most common type of session attack. Rather than predicting a correct session identifier is much easier. This approach is also known as Session Sidejacking, where the attacker uses packet sniffing to read network traffic between two parties to steal the session cookie. Unsecured Wi-Fi hotspots are particularly vulnerable, as anyone sharing the network will generally be able to read most of the web traffic between other nodes and the access point.

Solution To Session Capture

Encryption of the data passed between the parties; in particular the session key. This technique is widely relied-upon by web-based banks and other e-commerce services, because it completely prevents sniffing-style attacks. However, it could still be possible to perform some other kind of session hijack. Many web application apply SSL only on login page where session id can still be sniff out on subsequence pages. The password of such application is protected but not the session.

Session Fixation

Session fixation attacks attempt to exploit the vulnerability of a system which allows one person to fixate another person's session identifier (SID). Most session fixation rely on session identifiers being accepted from URLs (query string) or POST data.

Scenario Of Session Fixation Attacks

Here are some ways session fixation can be launched.

Simple Attack

The most simple way of an attack that can be launched due to website vulnerability.

  1. Attacker A knows http://unsafe.com is an unsafe site that accept SID directly through query string and no validation is being done on the site.
  2. Attack A send an email to Victim B 'Someone tried to access your bank account. Please access your account at http://unsafe.com?SID=H2SK9XSU1fL197DQ621Sh to change your password.' Attacker A is trying to fixate the SID to H2SK9XSU1fL197DQ621Sh.
  3. Victim B clicked on the URL provided and was bought to the login site.
  4. Victim B logged in with his access and tried to verify.
  5. Attack A then used the same URL to gain Victim B access since the session has been fixed to H2SK9XSU1fL197DQ621Sh.

Server Generated SID Attack

Misconcept of server generated session identifier is safe from fixate. Unfortunately not.

  1. Attacker A visits http://unsafe.com/ and checks which SID is returned. For example, the server may respond: Set-Cookie: SID=9AS82120DK8E0DI.
  2. Attacker A send in an email to Victim B. 'Please join our latest promotion at 'http://unsafe.com?SID=9AS82120DK8E0DI'.
  3. Victim B logged in and caused the fixate on 9AS82120DK8E0DI. Finally, Attacker A enters the same URL to gain unauthorizes access.
  4. Typically the same way as simple attack. The only differences is that the session identifier is being created by the server instead of the unsecured one.

Cross-Site Cooking Attack

Cross-site cooking is a type of browser exploit which allows a site attacker to set a cookie for a browser into the cookie domain of another site server. It exploits browser vulnerabilities although the original site was secure.

  1. Attacker A sends Victim B an e-mail: "Hey, celina has recommend you a new site at http://unsafe.com/"
  2. Victim B visits http://unsafe.com/, which sets the cookie SID with the value H2SK9XSU1fL197DQ621Sh into the domain of http://safe.com/
  3. Victim B then receives an e-mail from Attacker A, "Due to new implementation we will required you to login to http://safe.com/ to verify your bank account".
  4. Once Victim B logs on, Attacker A can use her account using the fixated session identifier.

Cross-Subdomain Cooking Attack

This is the same thing as cross site cooking, except that it does not rely on browser vulnerabilities. Rather, it relies on the fact that wildcard cookies can be set by one subdomain that affect other subdomains.

  1. A web site such as www.blogprovider.com hands out subdomains to untrusted third parties
  2. Attacker A who controls evil. blogprovider.com, lures Victim B to his site
  3. A visit to evil.blogprovider.com sets a session cookie with the domain .blogprovider.com on Victim's B browser
  4. When Victim B visits www.blogprovider.com, this cookie will be sent with the request, as the specs for cookies states, and Victim B will have the session specified by Attacker A cookie.
  5. If Victim B now logs on, Attacker A can use her account.

Victim B can be a administrator on blogprovider.com/administrator.

Each session attack scenario has resulted in Privilege escalation or Cross-calation which exploit a bug or flaw in the system to gain unauthorizes access. Such attacks are dangerous as Attack A can spy on Victim B on whatever he is doing on the system. If Victim B decides to purchase something on this site and enters her credit card details, Attack A may be able to harvest Victim B history access to see such sensitive data and details.

Session Fixation Attack

In order to better illustrate session fixate, we will consider the following example,

<?php
session_start();
if (!isset($_SESSION['visits']))
{
	$_SESSION['visits'] = 1;
}
else
{
	$_SESSION['visits']++;
}
echo $_SESSION['visits'];
?>

The above state that every different browser or new cookie session should have a start index of '1'. To demonstrate session fixation, first make sure that you do not have an existing session identifier, then visit this page with domain.com?PHPSESSID=1234. Next, with a completely different browser (or even a completely different computer), visit the same URL again with domain.com?PHPSESSID=1234. You will notice that the output show 2 instead of 1 because the session was continue! This is session fixate where the same session is used to continue previous session although you were not the first initializer.

Solutions To Session Fixation Attacks

Although Session Fixation is widely used. There are also many solutions for such attacks.

Do not accept session identifiers from GET / POST variables

We know from XSS attack article that global variable is dangerous. Session Fixation is one of the live example of such danger. We are all aware from this article that session fixation is achieve through query string or POST variable. Thus, prevent using such method for SID one of the best solution you can undertake to prevent simplify attack.

Additionally, such usage also increase the risk of:

  1. SID is leaked to others servers through the Referrer
  2. SID is leaked to other people through communication channels and social network
  3. SID is being stored in many places (browser history log, web server log, proxy logs, ...)

Change Important Session identifier

Session Fixation can be largely avoided by changing the session ID when users log in. In every important event, changing the session identifier will prevent attackers from accessing these important event. When the victim visits the link with the fixed session id, however, they will need to log into their account in order to do anything "important" as themselves. At this point, their session id will change and the attacker will not be able to do anything "important".

Store session identifiers in HTTP cookies

The session identifier on most modern systems is stored by default in an HTTP cookie, which has a moderate level of security. However, modern systems often accept session identifiers from GET/POST as well. The web server configuration must be modified to protect against this vulnerability.

; Whether to use cookies.
session.use_cookies = 1

; This option enables administrators to make their users invulnerable to
; attacks which involve passing session ids in URLs; defaults to 0.
session.use_only_cookies = 1

Utilize SSL / TLS Session identifier

When enabling HTTPS security, some systems allow applications to obtain the SSL / TLS session identifier. Use of the SSL/TLS session identifier is very secure, but many web development languages do not provide robust built-in functionality for this.
SSL/TLS session identifiers may be suitable only for critical applications, such as those on large financial sites, due to the size of the systems. This issue, however, is rarely debated even in security forums.

Regenerate SID on each request

Similar to 'Change Important Session identifier', however, this will regenerate the session identifier every single time a page is being request. This will further enhance the security since every single request by the user will eventually change the session identifier. Even if someone were able to capture session identifier through sniffing, the session id will eventually change which will make attacks difficult.

Accept only server generated SIDs

One way to improve security is not to accept session identifiers that were not generated by the server. Making life difficult for attacker is also one of the most important way of improve security.

#remove any session that may exist
if (!isset($_SESSION['SERVER_GENERATED_SID'])) {
   session_destroy(); 
}
#generate a new session id through built-in function
session_regenerate_id(); 
$_SESSION['SERVER_GENERATED_SID'] = true;

Logout function

Session Fixation will only be active when the known session identifier is the same. Thus, the attacker known session identifier will be invalid when the user logout themselves. Hence, having the following function is critical to improve session security.

if (isset($_POST['LOGOUT']))
   session_destroy(); // destroy all data in session

Time-out Session Identifier

Another very critical process of securing a web application is to implement a time out function to destroy the session whenever a session has expired. A session will expired after a given time. This defense is simple to implement and has the advantage of providing a measure of protection against unauthorized users accessing an authorized user's account by using a machine that may have been left unattended.

Store a session variable containing a time stamp of the last access made by that SID. When that SID is used again, compare the current timestamp with the one stored in the session. If the difference is greater than a predefined number, say 5 minutes, destroy the session. Otherwise, update the session variable with the current timestamp.

Destroy session if Referrer is suspicious

As mention on the scenario of session fixation, cross site cooking required an access to a bad URL and set the cookie session to the real URL. Destorying the session will prevent such attack from happening.

#check whether the referer is from our domain.
if (strpos($_SERVER['HTTP_REFERER'], 'http://mywebsite.com/') !== 0) {
   session_destroy(); // destroy all data in session
}
session_regenerate_id(); // generate a new session identifier

Verify IP

One way to further improve security is to ensure that the user appears to be the same end user (client). This makes it a bit harder to perform session fixation and other attacks. As more and more networks begin to conform to RFC 3704 and other anti-spoofing practices, the IP address becomes more reliable as a "same source" identifier. Therefore, the security of a web site can be improved by verifying that the source IP is consistent throughout a session.

if($_SERVER['REMOTE_ADDR'] != $_SESSION['PREV_REMOTEADDR']) {
   session_destroy(); // destroy all data in session
}
session_regenerate_id(); // generate a new session identifier
$_SESSION['PREV_REMOTEADDR'] = $_SERVER['REMOTE_ADDR'];

However, there are some points to consider before employing this approach.

  • Several users may share one IP. It is not uncommon for an entire building to share one IP using NAT.
  • Inconsistent IP as the users are behind proxies (such as AOL customers) or from mobile/roaming.

User Agent

Although the attacker may be able to change the user agent to match the one on the session. However, it makes the process even more difficult and may help prevent some attacker from penetrating through the session. A web application might make use of User-Agent detection in attempt to prevent malicious users from stealing sessions.

#check whether the stored agent is similar to user agent
if ($_SERVER['HTTP_USER_AGENT'] !== $_SESSION['PREV_USERAGENT']) {
   session_destroy(); // destroy all data in session
}
session_regenerate_id(); // generate a new session identifier
$_SESSION['PREV_USERAGENT'] = $_SERVER['HTTP_USER_AGENT'];

Full Defense

Creating multiple layer of defense mention above into a fully functional method to be used in every session authentication.

function validate_session($url)
{
	if (strpos($_SERVER['HTTP_REFERER'], $url) !== 0 ||
		isset($_GET['LOGOUT']) ||
		$_SERVER['REMOTE_ADDR'] !== $_SESSION['PREV_REMOTEADDR'] ||
		$_SERVER['HTTP_USER_AGENT'] !== $_SESSION['PREV_USERAGENT'])
	  session_destroy();
    #time-out logic

	session_regenerate_id(); // generate a new session identifier

	$_SESSION['PREV_USERAGENT'] = $_SERVER['HTTP_USER_AGENT'];
	$_SESSION['PREV_REMOTEADDR'] = $_SERVER['REMOTE_ADDR'];
}

Other Attacks

Cross-site scripting, where the attacker tricks the user's computer into running code or link which is treated as trustworthy because it appears to belong to the server, allowing the attacker to obtain a copy of the cookie or perform other operations.

Alternatively, an attacker with physical access can simply attempt to steal the session key by, for example, obtaining the file or memory contents of the appropriate part of either the user's computer or the server.

Summary

Session attacks are common among the attack used on secure website. Effort has to be made to improve session security to prevent any thief from happening. The solutions above might not be full bullet proof solution for future session attacks. Nonetheless, it can be used for discussion on solutions of future such attack.

Solutions to Cross-Site Scripting (XSS) Attack

Recently i wrote an article on Solutions to SQL Injection Attack that have all the great solutions to tackle SQL Injection Attack. In this article, i would like to discuss with you about Cross-Site Scription attack which is also known as XSS attack.

Cross-Site Scripting Attack

What is cross-site scripting attack? You can easily get a definition written on Wikipedia. It is a type of computer security vulnerability typically found in web applications which allow code injection by malicious web users into the web pages viewed by other users. Often look perfectly fine on the end-users side who may be subject to unauthorized access, theft of sensitive data, and financial loss. Here are a few cross-site scripting attack scenarios you may find interesting.

Simple persistent attack

  1. Mallory posts a message to a social network.
  2. When Bob reads the message, Mallory's XSS steals Bob's cookie.
  3. Mallory can hijack Bob's session and impersonate Bob.

DOM-based attack

  1. Mallory sends the URL of a maliciously constructed web page to Alice, using email or another mechanism.
  2. Alice clicks on the link.
  3. The malicious web page's JavaScript opens a vulnerable HTML page installed locally on Alice's computer.
  4. The vulnerable HTML page contains JavaScript which executes in Alice's computer's local zone.
  5. Mallory's malicious script now may run commands with the privileges Alice holds on her own computer.

Non-Persistent

  1. Alice often visits a particular website, which is hosted by Bob. Bob's website allows Alice to log in with a username/password pair and store sensitive information, such as billing information.
  2. Mallory observes that Bob's website contains a reflected XSS vulnerability.
  3. Mallory crafts a URL to exploit the vulnerability, and sends Alice an email, enticing her to click on a link for the URL.
  4. Alice visits the URL provided by Mallory while logged into Bob's website.
  5. The malicious script embedded in the URL executes in Alice's browser, as if it came directly from Bob's server. The script can be used to send Alice's session cookie to Mallory. Mallory can then use the session cookie to steal sensitive information available to Alice (authentication credentials, billing info, etc) without Alice's knowledge.

Persistent

  1. Bob hosts a web site which allows users to post messages and other content to the site for later viewing by other members.
  2. Mallory notices that Bob's website is vulnerable to a type 2 XSS attack.
  3. Mallory posts a message to Bob's website.
  4. Later, Alice views Mallory's message.
  5. Alice's session cookies or other credentials can be taken and sent to Mallory, without her knowledge.
  6. Mallory logs into Bob's website as Alice, and can see sensitive information (billing info etc.), or post messages on her behalf.

Identity Attack

  1. Bob hosts a site that allows users to post messages which includes a stored list of user names as recommendations.
  2. Alice is a regular visitor to the recommendation-based site; she uses an alias to protect her identity.
  3. Mallory knows one or more of Alice's email addresses but not her identity alias.
  4. Mallory uses social engineering to send a disguised recommendation link or the link to a carefully constructed redirect page which recommends a staged posting to Bob's site.
  5. Alice clicks on the link. Her session cookies or willing-login trigger the recommendation.
  6. Mallory reads the recommendation list and discovers Alice's online identity.

The above scenarios are taken from Wikipedia itself which i find it most appropriate to demonstrate the danger of XSS attack.

Solutions

It is well said that the responsibility of preventing XSS attack should not only be held by the developers. End user will really required to know this to better protect themselves from such attacks. It is always better to believe in yourself than others when it comes to security. In this article, we will discuss solutions to better secure ourselves and our web application. Although there are many risk with XSS attack, defending against it is much easier than you might though.

Developers

Filter all foreign data

Anything that will store in our database for future display to other user entered by users are consisted as foreign data. Ensure that all these data are being filter correctly will greatly prevent XSS attack from occurring on your website. Although it may sound easy but it does take discipline to always filter these data whenever there is such input. Especially on larger system where many developers are working on it. Simple filter such as htmlentities() can help eliminate many Cross-site scripting concern.

$filter_input = htmlentities($post['userinput'])
#use $filter_input

Use existing functions

Always use existing functions exist on PHP. Functions like htmlentities(), strip_tags(), and utf8_decode() can be useful. Try not to create your own function for filtering purposes as the functions in build in PHP are usually much faster and more secure to be used. Here are some functions that within PHP that are great for valid or filter user input

  • htmlentities()
  • strip_tags ()
  • utf8_decode ()
  • htmlspecialchars()
  • ctype_digit()
  • ctype_alnum()

Use a whitelist approach

Always assume data to be invalid until it is proved valid. This means that checking the length of the given input, validate the correct type, using regular expression to further validate the creditability till the extreme case you can imagine. This will help prevent any circumstances when the filter failed and caused the script to access your website.

Use a strict naming convention

We all know big systems are usually build by a few developers. Hence, it is important to use a strict naming convention so that whenever a developer look at the code he is able to identify what the other developer is trying to accomplished (you never know what some developer will do during refactoring to make the code more efficient. Yes, they remove these important stuff) and whether the data has been tainted.A lack of clarity yields confusion, and this breeds vulnerabilities. It is a good practice for developer to always be suspicious towards these global variables ($_POST, $_GET, $_REQUEST, etc.). An example of such strict naming convention can be illustrate below,

<?php
$clean = array();
#check whether it is a float, this is just a demo and more validation should be applied before it is clean.
if ($_POST['num'] == strval(floatval($_POST['num'])))
{
$clean['num'] = $_POST['num'];
}
#we use clean array onwards.
?>

Initialize your data

It is a good practice to always initialize your data although it is not necessary to do that in PHP. Since any variable that has not been initialize should always be considered as tainted. It is important to untainted it by initialize them. The user does not have another opportunity to send data as the entire HTTP request has been received, hence no data can be injected into your script (even if register_globals is enabled). Assume register_globals is enabled an attacker can easily make our clean variable into a malicious code if we did not initialize it. Consider the following code,

domain.com?clean=malicious_code

because we did not initialize clean array, clean now contains malicious code that may caused damage to our system.

#this part was invalid
if ($_POST['num'] == strval(floatval($_POST['num'])))
{
$clean['num'] = $_POST['num'];
}
#we use clean array onwards.
?>

Cookie Security

Another form of cross-site scripting attack is through cookie security. Many website uses cookie for various reason such as store information, retrieve information, track activities and etc,. However, most web applications rely on session cookies for authentication between individual HTTP requests (the remember me box on login page). We all are aware that client script have access to such cookie and simple XSS script can be made to retrieve such authentication to fake user access. To mitigate this particular threat, we can tie session cookies to the IP address of the user who originally logged in, and only permit that IP to use that cookie. but it will obviously break when an attacker is behind the same NATed IP address or web proxy.

Use BBCode

Many website such as forums, blogs, CMS or other communication system provides the ability to enhance that content by posting formatted text. However, these formatted text is vulnerable against Cross-site scripting attack. Many will uses BBCode on their system to provide user with formatted text and at the same time protect themselves against such attack. Consider the following example of BBCode

[b]bold[/b] text 
to
<b>bold</b> text

[color=blue]blue text[/color]
to
<span style="color: blue">blue text</span>

You can know more about BBCode in Wikipediawhere more example and explanation is being provided on BBCode. However, using such tag doesn't guarantee XSS security. Consider the following attack example within BBCode,

[url=http://hungred.com]Web development bookmark[/url]
to
<a href="http://hungred.com">Web development bookmark</a>.

The above illustrate a normal anchor tag BBCode. However, if i placed a JavaScript into the BBCode, it is still valid in BBCode,

[url=javascript:alert('Got you!')]Web development bookmark[/url]
to
<a href="javascript:alert('Got you!')">Web development bookmark</a>.

That is why whitelist approach is needed to always prevent such attack from coming. Similarly, in some lower versions of Internet Explorer (IE6 and below), URL format is allowed and will execute Javascript which we will have to take care as well.

XSS CheatSheet

For developers who are concern about XSS attack. (which you should) Please visit this XSS (Cross Site Scripting) Cheat Sheet to further test and protect against XSS attack.

End User

Disabled JavaScript

Many end-users (includes developers) who are well aware of XSS attack and the harm that it may bring upon the user will have their JavaScript disabled on their browsers. Since Cross-Site scripting is using JavaScript to harm users. Disabled such feature is the best way to prevent any attack from Cross-Site scripting. Although this will means that many newer site which only deploy JavaScript may not be accessible, this will also means that many developer will have to ensure that their site is fully protected against such attack. Users can disabled JavaScript and only allows those that are trusted. This approach act as a white list approach where only trusted website has JavaScript enabled to prevent any damage from occurring.

Disabled Cookie

User may want to disabled cookie in order to protect themselves against XSS attack or older browser may not even support cookies. Developers will have to think of alternative ways to provides functionality that required cookies than depending on it.

Summary

Developer must ensure the safety of its visitors and build a safer system for end users. On the other hand, end-user (including developers) will need to know the type of attacks used by attackers to prevent such attacks. The solutions above might not be full bullet proof solution for future Cross site scripting attack. Nonetheless, it can be used for discussion on solutions of future such attack.

Solutions to SQL Injection Attack

Security is one of the major issue we want to take care of other than meeting dateline. Especially when it comes to server data. We always want our data to be correct and secure. No one should be able to manipulate these data and these data should only be confine to people that have access to it. One should need to know the attacks on server data in order to better secure them. In this article, we will discuss SQL injection attack on databases.

SQL Injection Attack

SQL Injection Attack is the most common type of problem most web system face. Let's consider the following code

<?php
$name = ''test'; DELETE FROM users;';
mysql_query('SELECT * FROM users WHERE name='.$name.' ');
?>

Basically, the query above will caused all the data in your the users table to be deleted. This is usually due to inconsistency during development by different people. Lucky, there are many ways to prevent this.

The Solutions

Escape Mechanism

In order to prevent SQL injection attack, PHP's automatic input escape mechanism, magic_quotes_gpc, can provides some rudimentary protection (this is required to be enabled on php.ini). The magic quote will append a backslash to characters that is used to break out of a value identifier. eg, ', ", . etc. However, this is not always automatically enabled due to various reason. If you are using MySQL, it has a built-in function mysql_real_escape_string() to escape input characters that are special to MySQL. However, before calling a database's own escaping mechanism, it is important to ensure that no two escape mechanism is being used at the same time.

Escape Without Discipline

We talk about how escape mechanism can help us prevent SQL injection attacks. However, with the help of escape mechanism doesn't means that we are free from SQL Injection. We can still do some code injection even though we have implemented escape mechanism. Let's consider this example,

<?php
$id= '0; DELETE FROM users;';
$id = mysql_real_escape_string($id);
mysql_query('SELECT * FROM users WHERE id='.$id.' ');
?>

Similarly, SQL Injection attack is still possible since the escape mechanism only add additional backslash to characters that is used to break out of a value identifier such as single or double quote. There are no particular character that will need a backslash on the $id variable string. Thus, nothing was being added and the query will run as usual (danagerous). The solution to this situation is pretty simple, Discipline. Many times developers will like to skip the important process of adding quote in the SQL query. Being discipline and add these particular characters into your query will definitely save yourself from SQL injection attack. Example,

<?php
$id= '0; DELETE FROM users;';
$id = mysql_real_escape_string($id);
mysql_query('SELECT * FROM `users` WHERE id="'.$id.'" ');
?>

This will prevent the SQL from running the second query since there is no such id(int) as '0; DELETE FROM users'. The other way is to valid whether the particular $id is an integer.

<?php
$id= '0; DELETE FROM users;';
$id = (int) $id;
$id = mysql_real_escape_string($id);
mysql_query('SELECT * FROM users WHERE id='.$id.' ');
?>

This will cast $id into integer If the input is not entirely numeric, only the leading numeric portion is retrieved. eg, '999; DELETE FROM users;" will return 999. If the input doesn't start with a numeric value or there is no numeric values, 0 is returned. This is the best way to determine whether a particular value is an numeric value. No escape mechanism is required for this method but this can only be used for numeric validation.

LIKES Operator Danger

The LIKES Operator in SQL is a very powerful filter instruction that allows the query to use '%' for any characters that occurs zero or more times or '_' for a single character. However, both magic quote and built-in escape mechanism will skip these two special character. Thus, denial of services attack can be launch into the web server using SQL injection attack. Consider the following example,

$para= mysql_real_escape_string('%12'); // still %12
mysql_query('SELECT * FROM document WHERE number LIKE "'.$para.'%"');

Imagine this is a transaction table which has millions of documents. Searching number 12 in a particular number for a million times will definitely denial the access of this web portal services. Hence, we will need something that will escape these two special characters. We can use addcslashes() in PHP to add the required backslash onto these special characters!

$para= addcslashes mysql_real_escape_string('%12_')); // it will be \%12\_
mysql_query('SELECT * FROM document WHERE number LIKE "'.$para.'%"');

The above correct solution will provides no result due to the additional of \% and \_ into the query.

MySQL Exception

Fortunately, if you use MySQL, the mysql_query() function does not permit executing multiple queries in a single function call. If you try to stack queries, the call fails. However, other PHP database extensions, such as SQLite and PostgreSQL will permit such action.

Base64-encode

A common techniques is to use base64_encode in PHP to encode all data that are stored in the database. This will prevent any special character from damaging your query statement as any new query to the database will have to be encoded before the database recognize.

<?php
mysql_query('INSERT INTO users (name) VALUES "'.base64_encode('test').'"');
$name = base64_encode('test'; DELETE FROM users;');
mysql_query('SELECT * FROM users WHERE name=''.$name.'"');
?>

However, base64 encode will roughly increase data size by 33%, requiring bigger storage space. Moreover, PostgreSQL query with LIKE will fail due to base64.

Prepared Statements

The most efficient and powerful way to solve SQL injection attack, i would said it would be prepard statements. Prepared statements will only required to set up a statement once, and then it can be executed many times with different parameters. They are designed to replace building ad hoc query strings, and do so in a more secure and efficient manner. A typical prepared statement is shown below,

#MySQL
SELECT * FROM Users WHERE name = ?

The ? is what is a called a placeholder. You would need to supply the value for it during execution. You can read more about Prepared statements in MySQL at their website. Here is one example,

<?php
mysql_query('PREPARE search_template FROM "SELECT name FROM Users WHERE name = ?"');
mysql_query('SET @param = "test"');
mysql_query('EXECUTE search_template USING @param');
mysql_query('SET @param = "hello"');
mysql_query('EXECUTE search_template USING @param');
mysql_query('DEALLOCATE PREPARE search_template;');
?>

Unlike ad-hoc query string, the second query being executed with 'hello' as parameter doesn't add on the additional overhead of the search template which was prepared previously.

SQL Error Handling

How do hackers know your table name and the query you wrote? The answer is pretty simple and straight forward. It is mainly due to poor SQL error handling by the developers. The hackers will try their very best to break your SQL query in any way that will result in an error. Once the error is displayed, they will have many information to launch a proper attack. Let's consider an example,

<?php
#query=test;DELETE FROM breakplease;
$query = 'SELECT * FROM user WHERE name ='. base64_decode($_GET['query']);
mysql_query('$query);
?>

This will caused a decent error to be display on the page. And the user will have an idea what is being passed into the query string and what table is being used. Both structure, fields and GET parameter were exposed to the hacker in this way.

The best way is to prevent such embarassement by providing a more end-user type of message with a php error handling function.

function sql_failure_handler($query, $error) {
$msg = htmlspecialchars('Failed Query: {$query}<br>SQL Error: {$error}');
error_log($msg, 3, '/home/site/logs/sql_error_log');
if (defined('debug')) {
return $msg;
}
return 'Requested page is temporarily unavailable, please try again later.';
}

#query=test;DELETE FOM breakplease;
$query = 'SELECT * FROM user WHERE name ='. base64_decode($_GET['query']);
mysql_query('$query) or die(sql_failure_handler($query, mysql_error()));

This will provides us developer with relevant message on our TEST environment and provides the end-users with a more standard message on LIVE environment.

Authenticate Data Connection

This concern on how we store our application's database credentials. Some will placed it into an external files and gives it a non-PHP extension such as .inc. This post a problem as the file can be access directly outside of the server and it will be shown with plain text since it is not interpreted by PHP machine. Hence, we will have to better secure these database credentials from unauthorized access. One solution is to restrict the access of .inc in that particular folder defined in your web server (which many might not have in a shared hosting environment).

<Files ~ '\.inc$'>
Order allow,deny
Deny from all
</Files>

Or just changed it into .php extension so that it will not be exposed in plain text. However, if there is code written in that file, the same issue might still happen. If you have root access, you can do the following in your apache configuration file, httpd.conf

<VirtualHost ilia.ws>
Include /home/ilia/sql.cnf
</VirtualHost>

Now, set the file sql.cnf with the following codes,

SetEnv DB_LOGIN 'login'
SetEnv DB_PASSWD 'password'
SetEnv DB_DB 'my_database'
SetEnv DB_HOST '127.0.0.1'

This way, the details can be access via $_SERVER or getenv() in your PHP script without hardcoding it somewhere in your system.

echo $_SERVER['DB_LOGIN']; // login
echo getenv('DB_LOGIN'); // login

A more powerful way is to hide them even from the script that needs them. How to do that? we stored it into PHP.ini directives by specify the default login. These also can be set inside of Apache configuration file.

php_admin_value mysql.default_host '127.0.0.1'
php_admin_value mysql.default_user 'login'
php_admin_value mysql.default_password 'password'

Now you will connect to your database without any parameter and it will takes the default value from your apache configuration file.

mysql_connect()

Reduce the damage

It is a good practice to always indicate the number of results needed to retrieved from the table. Consider the following example,

<?php
$name = '"test"; SELECT * FROM users;';
mysql_query('SELECT * FROM users WHERE name='.$name.' LIMIT 1');
?>

Limiting the number of result return can help minimize the damage of SQL injection attack. Especially during authentication process. On the other hand, it is also a good idea to restricting the database permissions. By limiting the users permission, the damage of SQL injection attack can greatly minimized. Eg, only select access should be given to the user on the above query. Therefore, if the attacker tried to change the password by doing a SQL injection, it will fail (unauthorizes access).

Another alternative is to enhance the performance between your database and the script. We see Denial of service attack can be launch against the system due to the usage of LIKES operator. Having a good performance between the database and the web server is strongly advisable to minimize the impact on our business. Thus, to minimize database overloading, we can look at a few simple rules.

  1. Only retrieved the field you need. '*' is always misused by lazy developers
  2. try unbuffered query. It speeds up query but limit the work with only one query
  3. You can speed up connection process by using persistence connection. Eg, in MySql
    mysql_pconnect('host', 'login', 'passwd');
    

    However, if the database is not configure to allow many connection, further connection request will be rejected (since persistence connection is hooking the line). Hence, denial of services occurs.

MYSQL User account

For different action in the system, different MYSQL user account should be used. This will greatly help minimize the risk of damage done to the database if certain page was compromised. For example, a login page should only have SELECT access as other action is redundant. However, if you provide a full access level to a simple login page where any unauthorized user can access, malicious user can easily change the password through the text box provided to gain access to your portal if SQL injection vulnerability was found. Hence, brute force is not necessary to break down the door. They simply ring the bell! You are just inviting guess into your portal.

Summary

Although SQL injection attack is a common attack launch against many website, web developers have to ensure that these attack is minimize and eliminated. The solutions above might not be full bullet proof solution for future SQL injection attacks. Nonetheless, it can be used for discussion on solutions of future SQL Injection attack.