Instructor Lead On-Demand Learning Courses - August Big $10 Sitewide Extravaganza All Pro Web Designs and Udemy are happy to offer this special to you, good only on dates: 08/21-08/31 Act Now!

Building Your Startup: Securing an API

Final product image
What You’ll Be Creating

Welcome to the Building Your Startup With PHP series, which is guiding readers through the launch of an actual startup, Meeting Planner. Each episode details different coding and business challenges, with detailed examples you can use to learn.

Introduction

Recently, I introduced you to Yii’s simple REST API generation and Meeting Planner’s new “RESTful” service API. At that time, I mentioned that these APIs were only loosely secured. Sure, there was a shared secret between the client and the server, but there were a couple of problems.

First, the secret key and user tokens were repeatedly transmitted in query parameters of SSL calls. And there was no other authenticity check for the data, allowing a middle-person attack.

In today’s episode, I’ll guide you through how I secured the API against these weaknesses for a more robust API.

If you’ve been reading our startup series, you’ve probably already tried Meeting Planner and Simple Planner, but if not, please do. Scheduling a meeting is easy:

Instructor Lead On-Demand Learning Courses - August Big $10 Sitewide Extravaganza All Pro Web Designs and Udemy are happy to offer this special to you, good only on dates: 08/21-08/31 Act Now!

As usual, I’ll be participating in the comments below, so please offer your thoughts. You can also reach me on Twitter @lookahead_io. I’m always especially intrigued if you want to suggest new features or topics for future tutorials.

As a reminder, all of the code for Meeting Planner is written in the Yii2 Framework for PHP. If you’d like to learn more about Yii2, check out our parallel series Programming With Yii2.

The Initial API Security

Let’s begin by taking a look at the early API security I coded. We’ll presume there’s a mobile app that I’ve shared an $app_id and $app_secret with. Only API callers with these keys are accepted.

For example, the app tries to register its owner, likely a new Meeting Planner user:

public function actionRegister($app_id='', $app_secret='',
    $source='',$firstname ='',$lastname='',
    $email = '',$oauth_token='') {
      Yii::$app->response->format = Response::FORMAT_JSON;
      // verify app_id and app_key
      if (!Service::verifyAccess($app_id,$app_secret)) {
        // to do - error msg
        return false;
      }

The app calls the above actionRegister via https://api.meetingplanner.io/user-token/register/ with arguments as follows:

  • $app_id and $app_secret for authentication
  • $source = 'facebook' for the OAuth service we’re using, and accompanying $oauth_token from that service
  • $email, $firstname, and $lastname provided via OAuth

All of those are query arguments such as:

$signature => 9f6d2f7dd7d674e85eff51f40f5f830787c37d84d4993ac9ccfea2800285bd02

Service::verifyAccess($app_id,$app_secret) looks up the keys to authenticate the call as shown below:

class Service extends Model
{
    public static function verifyAccess($app_id,$app_secret) {
      if ($app_id == Yii::$app->params['app_id']
        && $app_secret == Yii::$app->params['app_secret']) {
            Yii::$app->params['site']['id']=SiteHelper::SITE_SP;
            return true;
        } else {
          return false;
        }
      }

Because the keys and the data were sent via SSL, they’re pretty secure but not invincible. Neither is the secret key safe on users’ iPhones for certain.

How can we make this more secure? Here are a few ideas:

  1. Don’t ever transmit the secret key over the Internet.
  2. Don’t transmit any of the data as URL parameters which might show up in server logs.
  3. Sign all the data to verify its accuracy.

These are actually standard practices used for securing APIs.

Note: An example of the risk of transmitting data that could be exposed in server logs would be the email and the Facebook OAuth token. If found in logs, these could be used with the Facebook API to access someone’s Facebook account.

Implementing Better API Security

Using Hash Signatures

First, I’m going to stop transmitting the $app_secret. Instead, we’ll sign the outgoing data with it before making an API call. 

So we’ll alphabetize the variables and concatenate them into a string, like this:

$signature => 9f6d2f7dd7d674e85eff51f40f5f830787c37d84d4993ac9ccfea2800285bd02

Resulting in:

$signature => 9f6d2f7dd7d674e85eff51f40f5f830787c37d84d4993ac9ccfea2800285bd02

Then, we’ll hash the data with PHP’s hash_hmac and the sha256 algorithm using our secret key.

$signature = hash_hmac('sha256',
    $data,Yii::$app->params['app_secret']);
Building Your Startup Securing API - PHP hash_hmac docs

This creates a unique hash code based on the arguments of the API call and our shared secret key:

$signature => 9f6d2f7dd7d674e85eff51f40f5f830787c37d84d4993ac9ccfea2800285bd02

Now, we can make a call on the API without transmitting the secret key. Instead, we transmit the signature of the hashed data above.

I’ve been using Postman to test the API, but you can also use cURL:

Building Your Startup Securing API - Testing the API with a signature

Here’s the receiving API code that responded to the call above:

public function actionRegister($app_id='', $source='',$firstname ='',$lastname='',$email = '',$oauth_token='',$sig='') {
      Yii::$app->response->format = Response::FORMAT_JSON;
      $sig_target = hash_hmac('sha256',$email.$firstname.$lastname.$oauth_token.$source,Yii::$app->params['app_secret']);
      if ($app_id != Yii::$app->params['app_id'] && $sig==$sig_target) {
        return 'it worked!';
      } else {
        return 'failed!';
      }
      

Furthermore, as I reviewed last time, each user receives their own token when they access Meeting Planner through the API, e.g. via their mobile phone. So, subsequent to registration, we can sign calls with their individual token and don’t need to transmit either the application’s secret key or the user’s individual token.

Sending Data in the HTTPS Headers

Next, we’ll migrate sending data in the headers. You can do this easily with Postman or cURL. Here’s Postman:

Building Your Startup Securing API - Testing the API sending data in the headers

And here’s cURL:

public function actionCurl($sig) {
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL,"http://localhost:8888/mp-api/user-token/register?sig=".$sig);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      $headers = [
        'app_id: '.'imwithher',
        'email: '.'tom@macfarlins.com',
        'firstname: '.'thomas',
        'lastname: '.'macfarlins',
        'oauth_token: '.'zuckerburger',
        'source: '.'facebook',
      ];
      curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
      $server_output = curl_exec ($ch);
      var_dump($server_output);
      curl_close ($ch);
    }

Here’s the receiving code which gets the API data from HTTPS Headers:

public function actionRegister($sig='') {
      Yii::$app->response->format = Response::FORMAT_JSON;
      $headers = Yii::$app->request->headers;
      $email= $headers->get('email');
      $firstname= $headers->get('firstname');
      $lastname= $headers->get('lastname');
      $oauth_token= $headers->get('oauth_token');
      $source = $headers->get('source');
      if ($headers->has('app_id')) {
        $app_id = $headers->get('app_id');
      }
      $sig_target = hash_hmac('sha256',$email.$firstname.$lastname.$oauth_token.$source,Yii::$app->params['app_secret']);
      if ($app_id != Yii::$app->params['app_id'] && $sig==$sig_target) {
        return 'it worked!';
      } else {
        return 'failed!';
      }

In Closing

We began today with the following goals:

  1. Don’t ever transmit the secret key over the Internet.
  2. Don’t transmit any of the data as URL parameters which might show up in server logs.
  3. Sign all the data to verify its accuracy.

And we accomplished all of these goals with only modest changes to our API code. It was fun making these changes and seeing how easily we can better secure an API. I hope you enjoyed following along with today’s episode.

I regularly monitor the comments, so please join in the discussion. You can also reach me on Twitter @lookahead_io directly. And, of course, watch for upcoming tutorials here in the Building Your Startup With PHP series.

If you didn’t earlier, try scheduling a meeting at Meeting Planner and let me know what you think. I especially appreciate feature requests.

Related Links

  • Simple Planner or Meeting Planner
  • How to Program With Yii2 (Envato Tuts+)
  • Building Your Startup: Designing a RESTful API (Envato Tuts+)
  • Programming With Yii: Building a RESTful API (Envato Tuts+)

See more about Building Your Startup: Securing an API

Instructor Lead On-Demand Learning Courses - August Big $10 Sitewide Extravaganza All Pro Web Designs and Udemy are happy to offer this special to you, good only on dates: 08/21-08/31 Act Now!

Leave a Reply