Skip to content
All posts
LaravelAPISecurity

API extra security layer with Input Checksum using Laravel

January 25, 2021·Read on Medium·

A Checksum is a value used to verify the integrity of a file or a data transfer. So basically, it’s the digital fingerprint of each request.

How its work?

The sender send a request including the checksum and the receiver will reconstruct their own checksum based on sender inputs. Then, the receiver will compare it own checksum with sender’s checksum whether match or not. If not matched, the sender request consider invalid.

So i came out just simple solution using Middleware to validate request checksum.

Basically, what i plan to do is the client-side will construct a MD5 checksum based on input and put it in HTTP header. Then, server-side will check if http header exist and validate it.

Client Side

  1. Sort every input value based on key in alphabetical order and make it inline with | delimiter. For example, username: john123, password:Xyz@1234 => Xyz@1234|john123
  2. Put the checksum in custom HTTP header (X-Checksum) and send to the server.

Server Side

  1. Construct checksum using the same way as client side do.
  2. Compare server checksum with request checksum.

Why MD5? It’s the simplest way to show how actually to do checksum 😁. You are freely to replace it with any Hashing(MD5, SHA..), Asymmetric (RSA..) or Symmetric (AES..) encryption method.

First, we create a middleware first. Let’s call it ChecksumValidationMiddleware.php

class ChecksumValidationMiddleware {}

Specify constant header

Define constant what would be the HTTP header name should be send by API

const CHECKSUM_HEADER = 'X-Checksum';
const DELIMITER = '|';

Reconstruct request and validate checksum

Let’s use collection. Sort all the keys in ascending order and merge using delimiter

public function handle(Request $request, Closure $next)
{
$checksum = md5(
collect($request->all())
->sortKeys()
->join(self::DELIMITER)
);

if ($checksum != $request->header(self::CHECKSUM_HEADER)) {
throw new SecurityGlobalHeaderException("Invalid Checksum");
}

return $next($request);
}

It’s very straight forward actually.

How about we combine with Timestamp validation? it will be something like this

ChecksumValidationMiddleware.php View on GitHub
<?php

class ChecksumValidationMiddleware
{
    const CHECKSUM_HEADER = 'X-Checksum';
    const TIMESTAMP = 'X-Timestamp';
  
    const DELIMITER = '|';

    public function handle(Request $request, Closure $next)
    {
        $checksum = md5(
            collect($request->all())
            ->sortKeys()
            ->join(self::DELIMITER)
        );
      
        if (now()->diffInSeconds(Carbon::parse($request->header(self::TIMESTAMP))) > 30) {
            throw new \RuntimeException('Service blocked! Invalid Timestamp Synchronization');
        }

        if ($checksum != $request->header(self::CHECKSUM_HEADER)) {
            throw new SecurityGlobalHeaderException("Invalid Checksum");
        }

        return $next($request);
    }
}

Conclusion

I recommend you to use asymmetric cryptography to create a checksum. Client will create checksum using public key and Server side will compare it using private key. It’s much more secure actually.

That’s it. A simple checksum in Laravel. Hope its help 😁

Thanks for your time.

Found this helpful?

If this article saved you time or solved a problem, consider supporting — it helps keep the writing going.

Originally published on Medium.

View on Medium