PHP openssl_encrypt tip

Recently I had to encrypt some data in PHP and send it to a Java App, the Java app was unable to decrypt the message.

I experimented with (data) padding, changing ciphers and changing the options for openssl_encrypt, but, none of those worked.

It was a requirement at the Java end for the Key to be hashed (sha256) and then used for encryption. The thing that worked for me was converting the hexed key to binary and using that.

<?php
$iv = 'aBCaDU9phtMwtNeV';
$key = 'B47C5126B42C9E192FAEAA5AA1892136';
$string ='testtesttesttest';

openssl_encrypt($string, "AES-256-CBC", hex2bin(hash('sha256', $key)), 0, $iv);
=> "E5vjBpIdWXo2NNuXkPzsEDVX6YVR3oFvHDwX+LohRsg="

# Bash
# >>> bin2hex('aBCaDU9phtMwtNeV')
# => "614243614455397068744d77744e6556"
# hash('sha256', 'B47C5126B42C9E192FAEAA5AA1892136')
# => "58754fcb239dfd17dfba62da3a57556980c69158d23dae6a1c24a174afeb676c"
printf %s "testtesttesttest" | openssl enc -e -aes-256-cbc -base64 -K 58754fcb239dfd17dfba62da3a57556980c69158d23dae6a1c24a174afeb676c -iv 614243614455397068744d77744e6556
E5vjBpIdWXo2NNuXkPzsEDVX6YVR3oFvHDwX+LohRsg=

AWS SSM Parameter Store IAM Policy for restricting by Path and Tag

I wanted to restrict access to some parameters based on the path and tag.

Say, I have a key:
/production/Param1=Value1
with a tag:
Application1=One

I was expecting a policy like this:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ssm:GetParameterHistory",
"ssm:ListTagsForResource",
"ssm:GetParametersByPath",
"ssm:GetParameters",
"ssm:GetParameter"
],
"Resource": "arn:aws:ssm:::parameter/production/*", "Condition": { "StringEquals": { "ssm:resourceTag/Application1": "One" } } }, { "Sid": "VisualEditor1", "Effect": "Allow", "Action": [ "tag:GetResources", "ssm:DescribeParameters" ], "Resource": ""
}
]
}

I had to do

# aws ssm describe-parameters --parameter-filters "Key=tag:Application1,Values=One" --output json | \ #filter by the tag
jq ".Parameters[].Name" | \ # get parameter name
xargs -I {} aws ssm get-parameter --name {} --with-decryption # get the parameter

{
"Parameter": {
"Name": "/production/Param1",
"Type": "String",
"Value": "Value1",
"Version": 1,
"LastModifiedDate": "2020-08-09T18:29:51.197000+10:00",
"ARN": "arn:aws:ssm:ap-southeast-2:378624334311:parameter/production/Param1",
"DataType": "text"
}
}

If you know the parameter name, get-parameters is simpler:

aws ssm get-parameters --name "/production/Param1" --profile k7-test
{
"Parameters": [
{
"Name": "/production/Param1",
"Type": "String",
"Value": "Value1",
"Version": 1,
"LastModifiedDate": "2020-08-09T18:29:51.197000+10:00",
"ARN": "arn:aws:ssm:ap-southeast-2:******:parameter/production/Param1",
"DataType": "text"
}
],
"InvalidParameters": []
}

It would have been simpler to filter by tags when using get-parameters-by-path, but a known issue with AWS:
https://github.com/aws/aws-cli/issues/2850
prevents us from doing that.

AWS Codecommit – PR and Comits with large files

AWS Codecommit console UI fails to display a diff when viewing PRs or Commits with large files (in my case a 7125 line XML file was part of the PR that failed to render the diff).

AWS support confirmed that this is a limitation as of now and failed to provide further details (on what the limit is or when this would be fixed).

It makes reviewing a little bit longer as one has to do the review out of the Console and then Approval/Disapproval happens within the console. It is a pain, to be honest.

Beware 🙂

MySQL substring start is one based

To my surprise, MySQL SUBSTRING functions start position is one based. A start position of ‘0’ will return an empty string.

If you would like to get the first 100 characters of a column named ‘my_column’ you will need to use SUBSTRING(‘my_column’, 1, 100). Using SUBSTRING(‘my_column’, 0, 100). will return an empty string.

Postgres and Microsoft SQL server also seem to be one based, but, have different behaviour when a ‘0’ is passed for the start position.



Using Generators to flatten a JSON doc in PHP

To flatten a JSON like this:

{
   "addresses" : [
     {
       "line1" : "123 Livingstone Rd",
       "id" : 23444555,
       "addressSummaryType": "Building",
       "subAddresses": {
         "Level1": [
           {
             "line1" : "Level 2",
             "id" : 266887373,
             "Level2": [
               {
                 "line1": "Suite 201",
                 "id" : 276888890
               }
             ]
           }
         ]
       }
     },
     {
       "addressSummaryType": "Building",
       "line1" : "124 Livingstone Rd",
       "id" : 2562672
     }
   ]
 }

to:

array(2) {
  ["id"]=>
  int(23444555)
  ["address"]=>
  string(18) "123 Livingstone Rd"
}
array(2) {
  ["id"]=>
  int(266887373)
  ["address"]=>
  string(26) "Level 2 123 Livingstone Rd"
}
array(2) {
  ["id"]=>
  int(276888890)
  ["address"]=>
  string(36) "Suite 201 Level 2 123 Livingstone Rd"
}
array(2) {
  ["id"]=>
  int(2562672)
  ["address"]=>
  string(18) "124 Livingstone Rd"
}

I used the following code:

foreach(flattenAddress($addressObj->addresses) as $flattenedAddress) {
      var_dump($flattenedAddress);
 }

 function flattenAddress($addresses, $baseAddress = '', $level = 0)
 {
     foreach ($addresses as $address) {
         $nextLevel = "Level" . ($level + 1);
         if (property_exists($address, "addressSummaryType")) { //root
             $baseAddress = $address->line1;
             yield ["id" => $address->id, "address" => $baseAddress];
             if (property_exists($address, "subAddresses")) {
                 yield from flattenAddress($address->subAddresses->{$nextLevel}, $baseAddress, ($level + 1));
                 continue; // recurse don't reset level
             }
         } else { //subaddresses
             $subAddress =  $address->line1 . " " . $baseAddress;
             yield ["id" => $address->id, "address" => $subAddress];
             if (property_exists($address, $nextLevel)) {
                 yield from flattenAddress($address->{$nextLevel}, $subAddress, ($level + 1));
                 continue; // recurse don't reset level
             }
         }
         $level = 0; //reset for next root
     }
 }


Add extra validation rules to Laravel ForgotPassword

I wanted to add a captcha to the Forgot Password form, so I ended up doing this to the default ForgotPasswordController:

use Validator;
use \Illuminate\Http\Request;
 
class ForgotPasswordController extends Controller {
 
protected function validateEmail(Request $request)
{
    Validator::make(
        $request->all(),
        [
            'email' => 'required|email',
            'captcha' => 'required|captcha', // extra param to be validated
        ]
   )->validate();
}

Only relevant bits are shown there.

ES6 support on iOS9

Recently while building a Cordova based app, few users complained that they were seeing blank pages or cryptic error messages. Someone mentioned they were on iOS9. Over the weekend, as I ran the app on a simulator, I noticed an error in Safari. And, that’s when I realized ES6 features and syntax is not fully supported in iOS9.  Please see here: ES6 Compatibility (check unstable platforms)

So be-aware of ES6 features compatibility (unless you can transpile/polyfill every bit of code)

Jenkins parallel pipeline

I was very excited to know about the ‘parallel’ feature in Jenkins Pipeline, but, there are many gotchas while making  use of the pipeline feature (many of which are documented here: Jenkins Pipeline Example). After trying and reading a few different solutions, following worked for me (notice in screenshot that the browser jobs run in parallel !)

pipeline {
  agent any
  stages {
    stage('Build') {
      steps {
        echo 'hello'
      }
    }
    stage('Test') {
      steps {
        script {
            def jobs = [:]
            def browsers = ["Chrome", "Firefox"]
            for (int i = 0; i < browsers.size(); i++) {
                def browser = browsers.get(i)
                def jobName = "printing $browser"
                jobs[jobName] = doJob(browser)
            }
            parallel jobs
        }
      }
    }
    stage('Deploy') {
      steps {
        echo 'deploying'
      }
    }
  }
}

def doJob(browser) {
    return {
        node {
            echo "testing in $browser"
        }
    }
}

React Native: Application is not registered

- This is either due to a require() error during initialisation or failure to call AppRegistry.registerComponent.

I had been struggling with this for some time and the answer was right there… I had to make sure I called AppRegistry.registerComponent with the right params/appname – I had renamed my app and forgot to update the class name and also the name passed to registerComponent.

And, one way to rename a project is by renaming in package.json and then running react-native upgrade.