Part 0.1 - 1000 foot overview

Part 1 - Things to consider when containerizing an app

Part 2 - Running MySQL in a container

Part 3 - Updating legacy code

Part 4 - Further legacy site work

Part 5 - Site Analysis

Previously…

We briefly explored a typical decision making process when deciding how/what to do. We ended up using a PaaS MySQL service above all others.

We’ve also discussed a WiFi review site - that can be found at https://netfinder.gen7tech.com/

(Note: I’ve disabled user sign-up/sign-in, due to the risk of the site getting hosed or bots etc. Once OIDC/OAUTH2 is implemented, that will be re-opened)

This week, we will be implementing some new features into this site. This post will not go into much detail as it’s not really the point of this series.

What features are we adding?

  • Support for ‘state’ query, so we aren’t limited to QLD
  • Groundwork of OIDC/OAuth2 integration (Azure B2C) -> client-side session

State query

Very straight forward:

  • Add a “state” column to the hotspots table
  • Add in 2x hotspots in Victoria to test
  • Modify the DB query and loop logic to render and sort states:

New query:

define("ALL_SUBURBS", 
"SELECT suburb, ANY_VALUE(state) 
FROM hotspots 
GROUP BY suburb 
ORDER BY ANY_VALUE(state)");

And a php loop to set the disabled entry:

$suburb = "";
foreach($suburbs as $key => $value){
	if ($suburb != $value[1]){
		echo "<option selected disabled>".$value[1]."</option>";    
		$suburb = $value[1];
	}
	echo "<option value='$value[0]'>".$value[0]."</option>";
};

What it now looks like:

B2C groundwork - client-side session

Fair bit more work. What we will firstly do is migrate away from server-side in-memory sessions to a client-side cookie session.

Yes we could store session information in a DB, or in something like redis, however:

  • For the DB, we’re using the cheapest, slowest option available
  • For something like redis, utilizing a PaaS service we would have to pay for

So we wills store the session information in a encrypted cookie.

What we have to do

  • Create helper functions to handle CRUD operations for the cookie, including encrypt and decrypt functionality
  • Migrate current $_SESSION functions to our new helper functions

Long story short we use the following functions:

  • encryptCookie
  • decryptCookie
  • setCookie
  • updateSessionKey
  • readSessionKey
  • initSession

We use openssl with AES-128-CBC ciphersuite. We do something like the below:

Init:

  • Session exist? If yes, use/update/whatever
  • If not:
    • Create a new session
    • JSON encode it
    • encrypt it
    • Set-cookie header with updated, encrypted value

Update:

  • Decrypt cookie value
  • Decode into an array
  • Update value
  • Encode, encrypt, set-cookie etc.

Destroy:

  • Set-cookie for the past

The most difficult part was the encrypt/decrypt, so they’re below and you can fill in your own blanks:

$cipher = "AES-128-CBC";
$ivlen = openssl_cipher_iv_length($cipher);
$password = "SuperSecretPassword";
$key = hash('sha256', $password);
$secret_iv = substr(hash('sha256', $password, true), 0, 32);    
$iv = substr(hash('sha256', $secret_iv), 0, 16);

function encryptCookie($value){
	global $cipher,$iv,$key;

	if(!$value){return false;}   
	$ciphertext = openssl_encrypt(json_encode($value), $cipher, $key, $options=0, $iv);
	return base64_encode($ciphertext);
}

function decryptCookie($value){
	global $cipher,$iv,$key;

	if(!$value){return false;}        
	$plaintext = openssl_decrypt(base64_decode($value), $cipher, $key, $options=0, $iv);             
	return json_decode($plaintext,true);
}

Seems to work fine in basic testing. I think there could perhaps be either a race condition, or a condition where setting the session cookie happens 1 request late. We will see.

Assuming it’s all OK, I’m going to wrap it as an actual usable module and put it on github.

Migrate existing session stuff over to our new logic

The $_SESSION functionality is used for lots of functionality throughout the site… after a quick vscode search, we use it for:

  • User firstname
  • User lastname
  • User email
  • User ID
  • User latitude
  • User longitude
  • Logged-in status
  • Some other state-type things, e.g. if user profile has been updated

Need to firstly test the above race condition, but there’s no reason why we cannot cut across one things at a time. For example, store users first and last time in our new session provider, while the rest remains unchanged.

Improvements

We can store a backup of the session in the DB. On session write, write it async to the DB as well as locally. When someone goes to login, if they’ve already got a session we can deliver it out of the DB oppose to starting from scratch. Given we already are using a DB and we don’t require it for session read, I think that would work fine.