Obscuring network data with base64

When meeting with clients and potential employers, I’m often asked If I have experience working with API’s (application programming interface). The answer is yes, and then I follow up with, “Have you reviewed my blog?” Just recently, I was asked the API question in an interview and a few minutes later the interviewer wrote this pseudo code on the dry erase board.

var _username:String;
var _password:String;
var _apiUrl:String;

private var logIn():void
{
var path:String = _apiUrl +"&"+_username + "&" + _password;
var request:URLRequest = new URLRequest(_path);
_loader = new URLLoader();
_loader.load(request);
}

Then proceeded to ask, “What’s wrong with this code?” Well, I looked over the method and pointed out that the code wasn’t complete, but the engineer replied, just assume that the syntax is correct and all variables needed are present. The only thing that came to mind, when dealing with API’s, is that there is usually some type of security measure, usually in the form of a user token or session variable. I felt like I was being tricked, the only other answer I could provide was to encrypt the string to secure the data passing over the network, and in short, that’s the answer they were looking for. They should have asked, “How would you protect the users login data when submitting it to an API?” Thanks for the trick question, guys, but as you know almost every API has a different strategy in handling security.

Creating secure SWF applications is not an easy task. The best way to protect your sensitive data is to leverage the HTTPS protocol for sending encrypted data instead of performing the encryption within ActionScript code. Unfortunately, we cant always hide behind HTTPS and It is not advisable to permit HTTP content to access HTTPS content.

There are several strategies to encrypt data in SWF web applications. The most popular are hashing algorithms and symmetric and asymmetric ciphers. Hashing algorithms is a method for performing non-reversible encryption that transforms the original text into a unique string, or hash. The hash is non-reversible, meaning it can not be decrypted and is usually stored on disk. This method is usually used as password authentication and storage. Symmetric and asymmetric ciphers are useful when you want to create a reversible encryption of data, however, the secret and private keys for these tools are usually stored in the SWF, which can be exposed using a decompiler. You can try to hide the key from a decompiler by using a flash obfuscation software, tho I have never implemented this method, I can not advise doing so.

This post is not about Flash security, because I’m not a security expert, it’s about base64 encoding.

Exactly what is Base64 encoding? Base64 encoding is a scheme that encodes arbitrary binary data as a string composed from a set of 64 characters. The exact character set can be any 64 distinct ASCII characters, but by far the most common set is “A” through “Z,” “a” through “z,” “0” through “9,” “+,” and “/.” Base64 encoding is used by many applications to “obscure” data when it travels across the network. Base64 encoding does not implement a cryptographic algorithm to protect sensitive information, yet is often used in many networks and end-user applications to my amazement.

I recently developed a sweepstakes micro-site for a client, which prerequisite was to obscure users personal data over the network. Since base encoding visually hides otherwise easily recognized information, It satisfied the prerequisite, with an understanding that it was not a secure method and the client signed off on it.

I scripted up a quick little demo app which shows you how to use Base64 encoding.

Get Adobe Flash player

The demo gathers the users input, encodes it in a JSON object, encodes the object using base64, and passes it to the server where a php script receives it, decodes the base64 JSON object and then passes it back to the flash app. As you noticed, I may have added a complexity by using JSON. JSON is your friend, the JSON format is often used for serializing and transmitting structured data over a network connection. It is primarily used to transmit data between a server and web application, serving as an alternative to XML. I have become accustomed to using it over the years, so I just left it in my code, just for clarification you can use a simple string.

Lets take a look at the demo. I created this app in CS4 using the UI flash components. As most flash apps start out, I created a Main document class:

Main.as
/****************************
* Manuel Gonzalez           *
* design@stheory.com        *
* www.stheory.com           *
* www.codingcolor.com       *
*****************************/

package {

  import flash.display.Sprite;
  import fl.controls.Button;
  import fl.controls.TextArea;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import flash.text.TextField;
  import flash.text.TextFieldAutoSize;
  import flash.text.TextFormat;
  import com.stheory.view.LoginForm;
  import com.stheory.business.LoginProxy;

  public class Main extends Sprite {



    private var _logInView:LoginForm;
    private var _logInProxy:LoginProxy;
    private var _traceField:TextArea;
    private var _okButton:Button;


    public function Main() {

      showLoginView();
    }
    /*
     Method: showLoginView
     Parameters:
     Returns:  
     */

    private function showLoginView():void {
      _logInView = new LoginForm();
      _logInView.x=20;
      _logInView.y=50;
      _logInView.addEventListener(Event.COMPLETE,submitData);
      addChild(_logInView);
    }
    /*
     Method: removeLoginView
     Parameters:
     Returns:  
     */

    private function removeLoginView():void {
      _logInView.removeEventListener(Event.COMPLETE,submitData);
      _logInView.dispose();
      removeChild(_logInView);
    }
    /*
     Method: submitData
     Parameters:
     event:Event
     Returns:  
     */

    private function submitData(event:Event):void {
      removeLoginView();
      createTraceField();
      var loginObj:Object={username:event.target.username,password:event.target.password,remember:event.target.rememberMe};
      _logInProxy=LoginProxy.getInstance();
      _logInProxy.addEventListener(Event.COMPLETE,logInComplete,false,0,true);
      _logInProxy.addEventListener(Event.CANCEL,logInError,false,0,true);
      _logInProxy.submitFormData(loginObj);
    }
    /*
     Method: logInComplete
     Parameters:
     event:Event
     Returns:  
     */

    private function logInComplete(event:Event):void {
      var results=event.target.scriptResult;
      _traceField.text=results;
      _logInProxy.removeEventListener(Event.COMPLETE,logInComplete);
      _logInProxy.removeEventListener(Event.CANCEL,logInError);
    }
    private function logInError(event:Event):void
    {
      var results= event.target.errorEvent;
      _traceField.text=results;
      _logInProxy.removeEventListener(Event.COMPLETE,logInComplete);
      _logInProxy.removeEventListener(Event.CANCEL,logInError);
    }
    /*
     Method: createTraceField
     Parameters:
     Returns:  
     */

    private function createTraceField():void {

      _traceField = new TextArea();
      _traceField.setSize(350, 100);
      _traceField.move(25, 25);
      addChild(_traceField);
      _okButton = new Button();
      _okButton.width=75;
      _okButton.move(300,135);
      _okButton.label="OK";
      _okButton.setStyle("textFormat", new TextFormat("Arial"));
      _okButton.toggle=true;
      _okButton.addEventListener(MouseEvent.CLICK, okClick,false,0,true);
      addChild(_okButton);
    }
    /*
     Method: okClick
     Parameters:
     event:MouseEvent
     Returns:  
     */

    private function okClick(event:MouseEvent):void {
      removeTraceField();
      showLoginView();
    }
    /*
     Method: removeTraceField
     Parameters:
     Returns:  
     */

    private function removeTraceField():void {
      _okButton.removeEventListener(MouseEvent.CLICK,okClick);
      removeChild(_traceField);
      removeChild(_okButton);
    }






  }
}

The Main.as class simply instantiates a logIn view, logIn proxy and creates a trace/log view to print out the interactions with the server. I’m going to skip the logIn view, just assume that all it does is set up the login UI and dispatches an event when the Submit button is pressed. The class that we will focus on is the LogInProxy.as. I utilize Steve Websters base64 encoder/decoder class and the AS3CoreLib developed by Mike Chambers, Daniel Dura and Christian Cantrell for the JSON encoding.
LogInProxy.as
/****************************
* Manuel Gonzalez           *
* design@stheory.com        *
* www.stheory.com           *
* www.codingcolor.com       *
*****************************/

package com.stheory.business{
  import flash.display.Sprite;
  import flash.events.EventDispatcher;
  import flash.events.IEventDispatcher;
    import flash.events.Event;
  import flash.events.IOErrorEvent;
  import flash.events.SecurityErrorEvent;
  import flash.net.URLLoader;
  import flash.net.URLLoaderDataFormat;
  import flash.net.URLRequest;
  import flash.net.URLRequestMethod;
  import flash.net.URLVariables;
  import com.adobe.serialization.json.JSON;
  import com.utils.Base64;



  public class LoginProxy extends Sprite {

    private static  var _instance:LoginProxy;
    private var _loader:URLLoader;
    private var _formObj:Object;
    private var _encryption:String;
    private var _path:String = "http://www.stheory.com/stheory/downloads/scripts/securityTest.php";
    private var _errorEvent:*;
    private var _testScriptResult:String;
   

    public function LoginProxy(singletonEnforcer:SingletonEnforcer) {
      super();
    }
    /*
     Method: getInstance
     Returns: AppServices
     */

    public static  function getInstance():LoginProxy {
      if (_instance == null) {
        _instance=new LoginProxy(new SingletonEnforcer  );
       
      }
      return _instance;
    }
    ////////////// Getters ///////////////////////
    public function get scriptResult():String
    {
      return "base64 to server :\n" +_encryption + "\n\nResults from Server :\n" + _testScriptResult;
    }
    public function get errorEvent():*
    {
      return _errorEvent;
    }
    //////////////////////////////////////////
    /*
     Method: submitFormData
     Parameters:
     inObj:Object
     Returns:  
     */

    public function submitFormData(inObj:Object):void
    {
      _formObj = inObj;
      encodeAndEncryptData();
      sendData();
    }
    /*
     Method: encodeAndEncryptData
     Parameters:
     Returns:  
     */

    private function encodeAndEncryptData():void {
     
     
      var jStr:String = JSON.encode(_formObj);
      _encryption  = Base64.encode(jStr);

    }
    /*
     Method: sendData
     Parameters:
     Returns:  
     */

    private function sendData():void {

      var dataVars:URLVariables = new URLVariables();
      dataVars.data =  _encryption;

      var request:URLRequest = new URLRequest(_path);

      request.method = URLRequestMethod.POST;
      request.data = dataVars;

      _loader = new URLLoader();
      configureListeners(_loader);

      try {
        _loader.load(request);

      } catch (error:Error) {
        trace("Unable to load URL");
        _errorEvent = error;
        dispatchEvent(new Event(Event.CANCEL))
      }

    }
    /*
     Method: configureListeners
     Parameters:
     dispatcher:IEventDispatcher
     Returns:  
     */

    private function configureListeners(dispatcher:IEventDispatcher):void {
            dispatcher.addEventListener(Event.COMPLETE, completeHandler);
            dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        }
    /*
     Method: removeListeners
     Parameters:
     dispatcher:IEventDispatcher
     Returns:  
     */

    private function removeListeners(dispatcher:IEventDispatcher):void {
            dispatcher.addEventListener(Event.COMPLETE, completeHandler);
            dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        }
    /*
     Method: completeHandler
     Parameters:
     event:Event
     Returns:  
     */

    private function completeHandler(event:Event):void {
      var result = _loader.data;
      try {
          _testScriptResult = result.toString()
          dispatchEvent(new Event(Event.COMPLETE))
        }
       catch (error:Error) {
        _errorEvent = error;
        dispatchEvent(new Event(Event.CANCEL))
      }
      removeListeners(_loader);
      _loader=null;
    }
    /*
     Method: securityErrorHandler
     Parameters:
     event:SecurityErrorEvent
     Returns:  
     */

    private function securityErrorHandler(event:SecurityErrorEvent):void {
      //trace("securityErrorHandler: " + event + " \n\n");
      _errorEvent = event;
      dispatchEvent(new Event(Event.CANCEL))
    }
    /*
     Method: ioErrorHandler
     Parameters:
     event:IOErrorEvent
     Returns:  
     */

    private function ioErrorHandler(event:IOErrorEvent):void {
      //trace("ioErrorHandler: " + event + " \n\n");
      _errorEvent = event;
      dispatchEvent(new Event(Event.CANCEL))
    }

  }
}

internal class SingletonEnforcer {
}

I created the LogInProxy class as a Singleton pattern, because I only want to make one object which will handle the data transaction between the client side and the server. LoginProxy is responsible for the base64 encoding, it also submits the data and reports the results. Review the encodeAndEncryptData() method:

 private function encodeAndEncryptData():void  {
           var jStr:String = JSON.encode(_formObj);
            _encryption  = Base64.encode(jStr);
   }

this is where all the magic happens. You can revise this method to meet your needs, for example remove the option of using JSON and just use a simple string. The SendData method, does what it implies, pretty straight forward. Now, lets take a look at the Php.

The Php script is really simple and was created just for this demo:
<?php
$data= $_POST['data'];
$decoded = base64_decode ($data);
echo "$decoded";
?>

It receives the base64 encoded data, decodes it and prints it back to Flash.

Now, for a sanity check, I would recommend using a packet sniffer that captures network traffic, like Service Capture. Here’s a screenshot of the demo app in action:

As you can see, we successfully passed an encoded string to the Php script, which in return passed back a JSON object.
Its really that easy! I could have gone the extra mile and re-encoded a response in base64 and had Flash decode it, but I’ll leave that up to you.
Enjoy!

Download Source:
Base64 encoding example (226)

2 thoughts on “Obscuring network data with base64

  • Pingback: Obscuring network data with base64- Coding Color | Neorack Tutorials

  • October 19, 2012 at 3:02 am
    Permalink

    Thanks! Encrypting the users’ names and passwords makes a lot of sense. And that would have been my answer, too, except I believe I saw a slight syntax error (in your first set of code), which threw me off:
    You declare:
    var path:String (….)
    Then set the URLRequest to be:
    (….) new URLRequest(_path);
    You want the first and second “path” & “_path” to be the same… just like in the later code in the post. There you use only “_path”.
    Cheers! Thanks again for your blog!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: