JAM Stack?
The term “JAM Stack” stands for Javascript, API and Markup. It was coined as a “marketing term” to encourage people to separate the front-end user interface into static files, that can then be managed using version control systems such as git. This decoupling of front- and back-end code bases helps reduce attack surfaces, including potentially expensive operations to render a front-end dynamically.
With the JAM stack, the static front-end interface is meant to load very quickly (e.g. from a CDN, or even an app bundle downloaded from an app store), and then make API calls to fetch data (typically in JSON format) and cache it.
When it comes to front-end interfaces, including web sites and apps in the app store, Qbix has you covered. There’s no need to compromise: you can build your dynamic site as usual, and then use static.php
to generate static pages meant for the general public. For logged-in users, you can choose to generate pages dynamically (e.g. their inbox). You can also take advantage of caching at all layers (reverse-proxy, proxy, CDN, app bundle) and get all the benefits of the JAM stack, without worrying about the details. For a deep dive, feel free to explore this article:
Custom Front Controllers
The Qbix Platform doesn’t have to be a large, monolithic harness that calls your code to make your website work. That inversion of control is useful for handling requests in a standard way, with the full standard lifecycle. For that, Qbix Platform provides the standard front controllers: Q_WebController
and Q_ActionController
.
But when it comes to building APIs that apps interact with in the background, you can streamline things quite a bit by simply writing your own endpoints. They could be PHP scripts that simply call <?php include('Q.inc.php') ?>
and you get the full environment ready to go:
The overhead to load and bootstrap framework is on the order of a few milliseconds! That’s because most of it is cached, except files found in the local
directory (for security reasons).
You can start to approach the speed of Node.js and Go, while retaining the much safer shared-nothing architecture of PHP. This way you don’t have to worry about issues that come from shared state, such as leaking secrets between requests, or continually leaking memory until the whole web server needs to be restarted.
Here is an example of what your PHP script to handle API calls from an iOS app could look like:
<?php
include('Q.inc.php'); // that's it
// check public key signature, and HMAC
MyCoolApp::validateRequest(true); // may throw exception
// use "external ids" from validated requests to authenticate
$deterministicId = Users_ExternalFrom_Ios::sessionId(
$_REQUEST['appId'], $_REQUEST['udid']
);
// don't let clients set session ID unless you can save cookies
Q_Session::start(false, $deterministicId, 'internal');
You can host it at an endpoint like https://api.mycoolapp.com/ios.php
If you have access to the web server’s rule set, you can certainly have it route certain URLs to your php scripts without exposing that ‘.php
’ at the end. But if you’re on a commodity host, you just might have to live with it. Especially if you’re just starting out and it’s only your own clients that are using your API for now.
Qbix Platform automatically reports exceptions to the client depending on the HTTP Accept
header. For example, Accept: application/json
causes the exceptions to be returned as JSON. The default exception handler Q/exception/native
intelligently uses Q_Exception::buildArray()
to remove sensitive information like the stack trace when you are not debugging your app.
Security and Signing Requests
A quick note about what to sign. API requests should be always be done using HTTPS rather than HTTP, so they can be secure from man-in-the-middle attacks. In that case, you don’t have to hash and sign the entire body of the request.
In short, you shouldn’t be signing the whole body of a request, which might be huge. Lots of times, times your clients may be written in a different language (e.g. Swift or Java) rather than PHP, which means serializations involving JSON ({"foo": {"bar": "baz"}}
} or even HTTP querystrings (foo[bar]=baz
) might be subtly different. Along with the signature, you should always be sending the exact serialized string that was signed instead of the original data structure. PHP on the server side can recover the original structure by a function like json_decode()
or parse_str()
.
By the way, the only exception to this rule is when the original structure is when the remote environment is heavily constrained, like a smart contract on an expensive blockchain. In those cases, the serialization is typically a packing binary format rather than JSON, allowing the smart contracts to work with it efficiently. If you’re interested in leveraging blockchain for added security, we’ve spun off an entirely new company in 2018 for that:
Technically, you only have to generate a HMAC from a single nonce, using a shared symmetric secret inside the app and the server. A determined attacker may exfiltrate this secret, which is why you should additionally have the client apps generate a public-private keypair that’s non-extractable and stays on the device, then have the server follow a trust on first use (TOFU) policy. This way both the HMAC and the private key signature are checked, before the request is allowed to proceed.
Notice that your PHP script doesn’t even start doing any expensive I/O operations until that last line in the code example above. Any invalid requests or sessions are rejected early, and you can even have them skip your servers entirely by doing the validation at the edge (e.g. using CloudFlare workers or Lambda@Edge).
If the user is using a trusted mainstream browser, or your own app from the app store, then only the user-agent is able to send the request through that channel. So if that user-agent used a private key or secret to sign something in a request, the thinking is that the entire request body can be trusted.
App integrity attestation is now supported by both Apple’s App Store and Google’s Play Store which means that your servers can make sure they’re dealing with an un-tampered version of your app, as opposed to some random client.
Of course you should still take care not to load third-party scripts into your pristine client environment, or you might suffer XSS attacks. On today’s Web, you’re often encouraged to “trust” large corporations like Google or Facebook and blindly inject their Javascript for to give you things like analytics. Luckily, with Qbix, you don’t have to anymore!
Versioning your APIs
Ideally, your APIs should always be backward compatible, which means you can keep the same endpoints. The clients making requests should include the version number of the SDK in the signed payload, and your API will make a decision about what to do.
However, if you want, you can require the version number in the URL path, e.g. https://api.mycoolapp.com/v1.1/users.php
. This seems like what the “cool kids” do, but as the old school said, “Cool URIs don’t change”. It would be great for your ecosystem if you could try to always maintain backward compatibility.