I’ve spent the past few weeks creating this blog from scratch down to the very last line of code. In doing this, I found the need to lazy load the comments for this blog after the user scrolls down to read them- similar to how youtube does it. Essentially, some javascript on the webpage will make a request back to my php script on the server to fetch and encode the json data for the comments.
Even though my site is small, I wanted to try implementing a little bit of security so I can get some practice in. I wanted to generate a single-use key that would have to be sent with the request to load comment data so people wouldn’t be able to abuse my API endpoint (not that there’s any real use for this data).
This is actually pretty straight forward, and PHP already provides almost all of the infrastructure to make this happen. Today we will be covering the php hash functions and then building a simple JSON endpoint using them.
So, what is a hash?
In simple terms, a hash is a function that generates a fixed length string from a set of input data. The string it returns is almost impossible to turn back into the input data. The input data will always generate the same hash so it cannot be changed. This is actually how passwords are stored, since it isn’t considered good practice to save passwords as raw text. The password can just be hashed every time you log in and then checked against the original hash created when you made your account. Tom Scott has an awesome video explaining this process:
Our implementation of hashing is actually going to be even simpler than this. All we need is two strings, a secret key and a public key. Our secret key is going to be a random string, and our public key is going to be the current timestamp. We will then combine them with the php hash() function to get our hashed string. It would be possible to do this without the timestamp, but using it will allow us to make our api key expire in the future. Here’s the code for what I’ve just explained:
function generateAPIKey() {
$secretKey = "dQw4w9WgXcQ"; // this never changes
$publicKey = time(); // seconds since epoch
$hash = hash_hmac('sha256', $secretKey, $publicKey); // create hash
return array("hash" => $hash, "publicKey" => $publicKey);
}
When we make a request to our api, we will pass the $hash and $publicKey variables as GET parameters so we can validate our hash against the public and private keys. Here’s what that looks like in our api.php file:
//hash-api.php?hash=54eef70ba4e86d68fbb8e052c9ecefab2d32f32d12fa1a7ae10fc41f981bab6d&publicKey=1681933372
$secretKey = "dQw4w9WgXcQ"; // same as before
$publicKey = $_GET["publicKey"];
$oldHash = $_GET["hash"];
$newHash = hash_hmac('sha256', $secretKey, $publicKey); // same as before
if (hash_equals($oldHash, $newHash)) { // test the hash we generated before against the new credentials
echo "valid request!";
} else {
echo "invalid request :(";
}
That pretty much covers the "security" side of our API endpoint. The other side is returning data to the browser in the correct format. We tell the browser what format we are using with a MIME Type header. The MIME Type for json is "application/json". We also will encode our data as json with the php json_encode() function.
header('Content-Type: application/json'); // set mime type
echo json_encode($dataToReturn, JSON_PRETTY_PRINT); // encode properly
It's been a cold and dark couple of months here in the desert, so as you can imagine I was pretty pleased when I woke up to a warm, beautiful morning on New Year's Day a few weeks ago. I immediately had the desire to ride, so I pulled my bike down to…
Prompt Injection is very similar to other types of injection attacks that we talk about in security research. Just like in Cross Site Scripting (XSS) and SQL Injection (SQLi) attacks, we have unsanitized user input mixed with instructions meant to be…
Garmin offers two options for recovering a lost device. The first is called "Find My Edge" and it requires a Bluetooth connection to the device in order to work. This wasn't a real option for me since my Garmin could potentially be miles away from my…