Security
The website builder platform would be worthless without cast iron security being a bedrock of the architecture.
An application which modifies the content of a website has to be hacker proof. Our approach is multi-layered starting with security controls at the database extending through to strict policies applied at the front end.
The platform is centred around an Oracle OCI database which requires 2-factor authentication for access. The data itself is stored on an encrypted tablespace making it impervious to theft. Only authenticated administrators of the database have access. There is no direct access to users.
A website owner on the platform needs first to authenticate using their email account. Their request to authenticate requires them to enter their email address to which is sent either a random 6-digit code or a link containing a server generated token.
After entering the correct code or clicking on the link, a pair of JSON web tokens are then created and sent to the user's browser from which the request was made. Each token is encrypted with a passcode that is auto-generated and is specific to the user's email address, which is the primary key if you like of the USERS table in the database.
The tokens themselves are hashed and their values stored in a table called REFRESH_TOKEN. Again, the content of this and all application tables in the database is exclusively through PLSQL packages compiled on the server.
The table containing the user's passcode is not available on any external media - i.e. it is never part of any backup. It can only be obtained by Oracle PLSQL code controlled by the administrator.
So now we've got a pair of encrypted JSON web tokens sent to the user. One is called a refresh token and the other is called an access token. Their roles are very different.
Refresh Token
The first thing that the client does with a newly issued refresh token is store it in the browser's application localStorage area. For perfomance reasons it is also stored in the browser's memory. We'll see why this is so later.
JSON web tokens are funny in that they consist of both perfectly visible content as well as a piece of encrypted content which is actually the visible content encypted with the user's SECRET_KEY that was created when they made their initial request. This is the signature and what makes the token useless unless you know the signing key. And that's on the server, which no one but the administrator can get to. Confused?
Even more confusing is that the only purpose of the refresh token is to request a new pair of refresh / access tokens which happens every 5 minutes that the user is actively editing content.
Access Token
The issued access token is stored on the user's browser in their application sessionStorage. It's purpose in life is to signal to the database server that it is authenticating a database access request. Every single call to a database endpoint includes the access token which the server validates before executing the request by checking the signature.
This validation ensures a hacker can't replace the originally issued token with one of their own.
The user edits their content repeatedly sending their access token like this for up to 5 minutes after which it expires and invalidated. At that point, the refresh token is woken up and used to make an authenticated request for a new pair of refresh / access tokens.
If the refresh token used to make this request validates, i.e. it has not itself expired (it has a 3 month expiry) and its signature hasn't changed, then the server issues a brand new pair of tokens and sends them back to the user's browser where they are used to replace the old pair.
Phew.
Quick recap
At this stage in the process we have the user's access token being sent with every database request made during their editing session. This carries on for 5 minutes at which point it expires and some javascript code in the browser fetches the refresh token from localStorage and sends that in a request for a brand new pair of refresh / access to be issued.
By the way, the reason that the platform stores the user's access token in memory is purely for performance. During an editing session, the changed content is sent to the database to be saved every 2 seconds. So that the user is unaware that this is happening, we keep their access token in a javascript variable.
How can this be hacked?
The best way to hack this arrangement would be to steal the refresh token. However, it's only valid for 5 minutes before a new one is issued so the window of opportunity to do bad things is limited.
But stealing the token is only possible remotely using Javascript code injected into the application code. A hacker would have to find some way of getting the user to do something that would enable their nefarious code to be executed. Such code would typically get the user's refresh token and send it to a server that they control.
Enter strict Content Security Policy.
Content Security Policy
Every page deployed for a website on the platform contains a content security policy response header. So when the user loads a page on their website a policy header is included that the browser must adhere to governing which domains they can access for example.
The most important policy is the one that governs what javascript can be executed by the browser. The relevant part of the policy looks like this:
script-src 'strict-dynamic' 'sha256-wzQuEPTVR0RRDaQpd6eL1JtAyNRkr/R3fqoI4fJTJFM=';
Which is saying the only piece of javascript code that can be executed by the browser for this application must have a sha256 signature of "wzQuEPTVR0RRDaQpd6eL1JtAyNRkr/R3fqoI4fJTJFM="
It's also saying that it's OK to execute javascript code dynamically imported by the signed code and only by the signed code.
So what does this signed code look like?
document.addEventListener('DOMContentLoaded',()=>{
const script = document.createElement('script');
script.src = "/javascript/deploy_main.min.js";
script.type = "module";
document.body.appendChild(script);
})Which basically says when the content on the page has finished loading then create a piece of javascript to load and execute the javascript module located on the website at /javascript/deploy_main.min.js
And that's how the editing application is secured and bootstrapped. The browser will only execute those lines of code shown above and will reject anything that doesn't hash to the value specified in the Content Security Policy.
Therefore, even if a hacker could inject javascript code on the user's page, the only code that could be injected would have to be those exact lines listed above. Anything else the browser rejects.
Physically stealing the refresh code
Suppose that a website owner leaves their laptop unsecured in a taxi. What would happen in case the laptop is found by some dishonest soul who opens the browser and finds a website page with the editor toolbar active? Could they not maliciously delete all the owner's content? Or worse, replace it with something unpleasant.
Enter fingerprints.
Fingerprinting access
Each time a refresh token is used to request a new pair of refresh / access tokens the server makes an additional check that the requestor's IP address and device fingerprint are known.
It does this typically to provide a further layer of security in the event that the user's refresh token is intercepted online. For example, if a bad person socially engineers a website owner to reveal the value of their refresh token, then they wouldn't be able to use it unless they pass the fingerprint test, which they can only do if they are using exactly the same device and have exactly the same IP address as the victim.
Of course those details can be easily spoofed, but the victim would have had to reveal these additional details as well.
In the case of the laptop left in a taxi, the device obviously hasn't changed, but the IP address most likely has. The server immediately returns an HTTP 401 Access Denied to the browser which is now forced to log in and re-authenticate using the website owner's email address that is stored in the refresh token.
Of course, if the user had left their email open on the laptop they are well and truly screwed.
Passwords. What Passwords?
Effective security is never about one all encompassing solution. You have to provide multiple layers and hope no one figures out how to bypass all of them.
So far we've only shown how to prevent online misuse. But what about getting the user to unwittingly reveal their password?
This is easily prevented because there are no passwords.
Nor are there any cookies.
Passwords and cookies are an anachronism and are the root cause of the worst security breaches. That's how Mark & Spencers were hacked. Frankly, no one's going to be able to steal your JSON web tokens with the server side encryption and Content security policy in place and even if they do, they won't be able to use them.
By basing security on the refresh / access token strategy, authenticated owners can edit as many websites as they like for as long as they like without ever needing to remember a password.
They can be logged on securely for ever or at least until they want to access new editor functionality. We only rebuild the main editor dropdown menu when the user logs in.