How to use Google Charts API in your Secure, HTTPS webpage

November 9th, 2009 by admin Leave a reply »

So you love Google Charts API and want to spice up your app with lots of pretty bar charts, Venn diagrams, Google-o-meters and 2D bar codes (QR Codes) – only problem is, you require that users connect to your site using a secure connection (https://yoursite.com/whatever). Well, as you are probably aware, the Google Charts API doesn’t ex

actly support SSL connections… directly.

Have no fear! If you are willing to give up a little bit of your own bandwidth, you can work around this problem. We are going to do this using a simple PHP script and if you want to get really fancy, Memcached to keep the redundant API calls to a minimum.

Let’s get started.

First, let’s make sure we understand why it is a problem to include a non-secure Google Charts API into your otherwise secure page. When your users are browsing to your page using an address starting with https://, their browser is going to negotiate an TLS/SSL connection which will encrypt all the data flowing between your client and the server. If any element on the page (image, external CSS or JavaScript, etc.) is not being called from a secured connection as well, then the browser will throw up a warning. Each browser is different, but most will alert the user with a broken lock and/or a popup message. The user can usually accept this warning and proceed at their own risk, but this is not acceptable for a production website or application.

The obvious answer to this problem is to make sure that all external elements on the page are loaded over a secure connection. So – how do we do that with Google Charts? Google does not allow you to just take a traditional API call and slap a “https://” in front of it in lieu of the expected http://. Your request will be denied.

Basic Implementation

Here’s what we do:

  1. Create a simple PHP script that will fetch a chart from Google Charts API
  2. Change your web page to call the new PHP “fetcher” instead of Google Charts directly.
  3. Make sure the PHP script is being called by your page over your https:// connection

It is pretty much that simple.

Here is the before example:

<!-- HTML snippet -->
<img src='http://chart.apis.google.com/chart?cht=p&chd=s:Uf9a&chs=200x100&chl=January|February|March|April' alt='Pie Chart'/>

Here is the after example:

<!-- HTML snippet -->
<img src='https://mywebsite.com/gchart.php?api_url=http%3A%2F%2Fchart.apis.google.com%2Fchart%3Fcht%3Dp%26chd%3Ds%3AUf9a%26chs%3D200x100%26chl%3DJanuary%7CFebruary%7CMarch%7CApril' alt='Pie Chart'/>

Notice, the same API URI is used in the “after” example, but it is URL Encoded. This just makes it safer to pass along to the PHP script.

Here is the PHP script that makes this work:

<?php

$url = urldecode($_GET['api_url']);

$image_contents = file_get_contents($url);
echo $image_contents;
exit;

A quick note about this: you may be saying – where is your closing PHP tag!?! Well, PHP doesn’t require that you have a closing PHP tag, as the PHP interpreter will automatically close any open tags. I do this on just about every file that will be used as an include of some sorts – particularly in something like this where any extra, inadvertent white space at the end of the file can alter the output adversely and cause errors in the page.

Advanced Implementation w/ Caching

The script as it is above, is fine and dandy, but terribly inefficient. You see, every time the user requests the page, your script is going out and using your bandwidth to make the request to the Google Charts API instead of the client. You are becoming the proxy for all requests to and from Google Charts. This will not fly – especially if you are in a high volume environment.

So what is the solution? I chose Memcache to store a copy of the image contents for a certain period of time before having to re-query the Charts API and re-fetch the data again.

It looks something like this:

<?php

$url = urldecode($_GET['api_url']);
$cache_key = md5($url);
$item_cache_expire = 3600;
$mc_host = '127.0.0.1';
$mc_port = 11211;

# Connect to Memcache
$memcache = new Memcache;
$memcache->connect($mc_host, $mc_port) or die ("Could not connect");

if ( $get_result = $memcache->get($cache_key) ) {
    $image_contents = $get_result->chart_image;
}
else {
    $image_contents = file_get_contents($url);

    $tmp_object = new stdClass;
    $tmp_object->chart_image = $image_contents;

    $memcache->set($cache_key, $tmp_object, false, $item_cache_expire) or die ("Failed to save data at the server");

}
echo $image_contents;
exit;

Here’s what is happening.

<?php

$url = urldecode($_GET['api_url']);
$cache_key = md5($url);
$item_cache_expire = 3600;
$mc_host = '127.0.0.1';
$mc_port = 11211;

Setup some variables we’ll need in the script. Grab the api_url value from the GET array, create an MD5 hash of the request URL (we’ll use this as our memcache item key), set the expiration time of the cached item to 3600 seconds (a.k.a. 1 hour) and finally provide the connection information to your memecached server.

# Connect to Memcache
$memcache = new Memcache;
$memcache->connect($mc_host, $mc_port) or die ("Could not connect");

Instantiate a new Memcache object using the connection info provided above.

if ( $get_result = $memcache->get($cache_key) ) {
    $image_contents = $get_result->chart_image;
}
else {
    $image_contents = file_get_contents($url);

    $tmp_object = new stdClass;
    $tmp_object->chart_image = $image_contents;

    $memcache->set($cache_key, $tmp_object, false, $item_cache_expire) or die ("Failed to save data at the server");
}

Using the MD5 hash that we created above, check to see if there is a valid cached object on the memcache server for us to retrieve. If not, go out and grab a fresh version from Google Charts API and stick it into memcache. Either way, we end up with a variable $image_contents which contains the desired chart image.

echo $image_contents;
exit;

All that is left to do now is print out the image contents and exit the script. Again, I do not use a closing PHP tag on purpose to avoid extra whitespace.

All ye, all ye, now hear this…

This is a very basic example. It should probably be categorized more as a proof of concept. It does NOT, I repeat – DOES NOT – take into account a variety of security concerns such as SQL injection or CSRF (cross-site request forgeries), etc. Notice that I didn’t even validate the user input from the GET variable. Who does that!?! So, in other words, don’t just copy and paste and then come back and blame me for getting your site hacked. Be smart about employing something like this.

BTW, if you want to know more about how to make your PHP scripts more secure, go see Chris Shifflet’s excellent blog at http://shiflett.org/.

Advertisement

1 comment

  1. Tim Davies says:

    Very useful information. I have been looking for a work around for the Google Charts / SSL digital certificate issue and seems that we have cracked it!!

Leave a Reply