A Three-tiered Architecture of API Service/endpoints with Coldfusion
API Service and Security (with Coldfusion)
Don Chunshen Li / November 2020
A famous VC, Marc Andreessen once said “Software is eating the world”, and in the software world, API is eating the software world, so, let’s talk about API.
For this piece, I’m going to talk about an API architecture composed of API service/endpoints at one end, a lightweight or super thin gateway in the middle and the API consumers or callers at the other end. See the following diagram.
A side note, this architecture should work with other server side programming languages as well other than Coldfusion.
Motivation: a super thin gateway in the middle serves as an extra security layer without incurring any noticeable cost. And practically speaking, with this approach, small CF shops do not need to pay for the expensive ACF license for the extra ACF API Manager module. And for Lucee developers, they don’t have to fumble with Lucee’s unreliable REST service via its admin. And when you have several developers on a team (especially remote team), do you want each developer have access to the admin? …
Let’s get to work, so, how does it work?
First, I’ll explain its overall workflow conceptually.
Part One — The API Service Workflow
Section One — The API Service/Endpoints
Create your CFC with a function or method that provides some service.
Let’s use a business case to demonstrate it better. Say, you’re a car dealer and you have many used car retailers that frequently want to check your used car inventory. So, our API service CFC is to provide used car info to your authorized retailers for their systems to make API calls to yours.
Once we have this basic functionality created, we move on to create a super thin gateway to let incoming API requests from our retailers to point to it.
Section Two — The API GateWay
This is unbelievably simple with Coldfusion!
As simple as one line of code, a simple cflocation tag pointing url to your actual API service CFC would do. So, you create this gateway cfm file and give it a name and share its URL with your authorized retailers.
Now, with this gateway in place, let’s say, Jack Daniel owns a used car shop and he wants to make an API call to this car dealer’s API for used card info he can simply point to this gateway. So, now we move on to
Section Three — The API Consumer(s) (Callers)
Jack Daniel could use Coldfusion or Php or .Net, you name it, any server side language to call the car API service via the gateway.
Let’s stick to Coldfusion for now.
Basically a simple cfhttp tag would get the job done.
Next, let’s talk about the all important subject of security.
Part Two — SECURITY
Let’s do a quick review, to be precise, listing the important factors for this matter and then we’ll address them one by one.
* Authorization and Authentication
* Data security during transmission
* Erect security “wall”
* Other security measures
For Authorization, we could use one level authorization or two levels of them.
For one level alone, oAuth token or JWT (Javascript Web Token)
For the second level authorization, we could use SMS code to further ensure the authorized user’s identity is authentic.
For Authentication of such token, we could store encrypted data of such token on the server side. For extra security and authentication, we could hash the token and store such data on the server, thus, we can compare the hashed value of the incoming API request/call’s token with the already hashed value stored on the server to authenticate the caller. This idea is sort of borrowed from ZKP (Zero Knowledge Proof)…
Now, let’s say, for some reason, a caller’s token has been compromised, so, we could add the second layer to further authenticate the user/caller.
This second layer of authorization using mobile phone’s SMS code and it works like this.
We set up a page for the authorized user to enter his/her token and then enter his/her mobile phone #, upon submission we’ll automatically send this user a 5 digital SMS code and this caller will use it as a part of the authorization process.
Next, for Data security during transmission
We need to implement SSL (Secure Socket Layer) to enable HTTPS protocol for the API call. And if possible, TLS (Transport Layer Security) as well.
Now, let’s dive into the ‘ Erect security “wall” ‘ topic
By creating a light gateway in the middle we insulate and protect the API’s service at the other end.
For ‘Other security measures’
We could set up quotas and throttling for incoming API calls.
Security Impact
With such security setup, even if your database on the server side is hacked, the hacker still won’t be able to use the stolen data effectively because the security “token” in the database are hashed.
I know this part is very dry, so, now let’s move onto the next one, the “meat and potato”, the real deal.
Part Three — Implementation (code level)
Ok, this is the fun part.
Let’s get to it, we start with the API service/endpoint creation. And before we dive into coding let’s re-visit our business goal, we are to create a service that would provide our auto inventory information to our authorized auto retail shops.
For readability, we’ll stick to CF tags. The following code is the base of CFC for our car API service.
<cfcomponent>
<cffunction name=”CarsInfo” access=”remote” returnFormat=”plain”>
<cfargument name=”make” type=”string” required=”false”>
<cfquery name=”getCars” datasource=”myCarDS”>
select *
from carsTBL
<cfif structKeyExists(arguments,”make”) AND arguments.make IS NOT “”>
where carMake = <CFQUERYPARAM VALUE=”#arguments.make#” CFSQLType=”CF_SQL_VARCHAR”>
</cfif>
</cfquery>
<cfreturn serializeJSON(getCars)>
</cffunction>
</cfcomponent>
And say, our API application root directory is “APIgateway”, we create a subdirectory called “v1” beneath it, name it “carInfoService.cfc”
and place it in the new directory, thus, its access path is now “/APIgateway/v1/carInfoService.cfc”.
Next, let’s create the API caller script/file using HTTP protocol.
<cfparam name=”URL.make” default=””>
<cfhttp
url=”http://localhost:8500/APIgateway/CarGateway.cfm?make=#URL.make#"
method=”get”
port=”8500"
result=”cars”>
<! — — attach api key or token to the calling — ->
<cfhttpparam type=”header” name=’authorization’ value=’js72ujdkjf30sod3au782jcf6cf2e3408095b8157ji293'>
</cfhttp>
Let’s name it “getCarInfo.cfm” and place it under “APIgateway” directory, thus, its access path is “/APIgateway/getCarInfo.cfm”.
And you would notice the cfhttp URL is pointing to gateway file named “CarGateway.cfm”, which means that the caller is not calling the /APIgateway/v1/carInfoService.cfc API endpoint directly. And you would also notice that the cfhttpparam passes the token along with the cfhttp call for authorization.
Now, let’s work on the CarGateway.cfm file.
<cfparam name=”URL.make” default=””>
<cflocation url=”http://localhost:8500/APIgateway/v1/carInfoService.cfc?method=CarsInfo&auth=#GetHttpRequestData().headers.authorization#&make=#URL.make#" addtoken=”no”>
And we place this Cargateway.cfm file beneath the “APIgateway” directory, thus, its access path is “/APIgateway/CarGateway.cfm”.
Ok, so far we’ve created all the base/skeleton code for the API, caller and gateway, the “car” dealer — retailer car info workflow is starting to take shape… but we’re way from being ready.
Now, let’s work on the security aspect of this API.
And let’s start with the CarGateway.cfm file,
First, without the api key or token passed on to the API service cfc from the gateway, authorization would be an empty word, thus, we put the following code at the top of the CarGateway.cfm to ensure that the caller includes authorization and doing so correctly:
<CFIF !StructKeyExists(GetHttpRequestData().headers,”authorization”)>
<cfabort>
</CFIF>
Now, the CarGateway.cfm becomes:
<CFIF !StructKeyExists(GetHttpRequestData().headers,”authorization”)>
<cfabort>
</CFIF>
<cfparam name=”URL.make” default=””>
<cflocation url=”http://localhost:8500/APIgateway/v1/carInfoService.cfc?method=CarsInfo&auth=#GetHttpRequestData().headers.authorization#&make=#URL.make#" addtoken=”no”>
And to implement it on the carInfoService.cfc end as well. To do that, we simply place the following code beneath the cfargument parameter:
<cfif !StructKeyExists(URL,”auth”)>
<cfreturn “Sorry, your header does not contain ‘authorization’.”>
<cfabort>
</cfif>
Thus, our carInfoService.cfc file now looks like this:
<cfcomponent>
<cffunction name=”CarsInfo” access=”remote” returnFormat=”plain”>
<cfargument name=”make” type=”string” required=”false”>
<cfif !StructKeyExists(URL,”auth”)>
<cfreturn “Sorry, your header does not contain ‘authorization’.”>
<cfabort>
</cfif>
<cfquery name=”getCars” datasource=”myCarDS”>
select *
from carsTBL
<cfif structKeyExists(arguments,”make”) AND arguments.make IS NOT “”>
where carMake = <CFQUERYPARAM VALUE=”#arguments.make#” CFSQLType=”CF_SQL_VARCHAR”>
</cfif>
</cfquery>
<cfreturn serializeJSON(getCars)>
</cffunction>
</cfcomponent>
All right, so, by now we’ve implemented a basic layer of security for the API service for this imaginary car dealer.
And let’s further assume or ask the question, what if it’s not car info but financial data or healthcare etc. sensitive data?
Then, as an API service provider, we want to beef up the API security up a notch.
Using mobile phone and subsequent dynamic SMS code is one technique for the extra security layer.
Let me show you how we can do it with Coldfusion and continue it with the said case for convenience.
We start with the API service file, carInfoService.cfc.
First we add an additional parameter, specifically, adding the following code beneath the current “make” functional parameter:
<cfargument name=”sms” type=”string” required=”false”>
This affects our previous code for the “token” authorization code. So, instead of
“
<cfif !StructKeyExists(URL,”auth”)>
<cfreturn “Sorry, your header does not contain ‘authorization’.”>
<cfabort>
</cfif>
“
We now replace the above code with something like the following:
“
<cfif !StructKeyExists(URL,”auth”)>
<cfreturn “Sorry, your header does not contain ‘authorization’.”>
<cfabort>
<cfelse>
<! — — run a query on the caller authorization credentials — ->
<cfquery name=”checkCaller” datasource=”myCarDS”>
select *
from mySecTBL
where myHV = <CFQUERYPARAM VALUE=”#hash(URL.auth)#” CFSQLType=”CF_SQL_CHAR”>
<cfif structKeyExists(URL,”sms”) AND URL.sms IS NOT “”>
and MYsms = <CFQUERYPARAM VALUE=”#URL.sms#” CFSQLType=”CF_SQL_CHAR”>
— this / another column would support another refined security parameter
</cfif>
</cfquery>
<cfif checkCaller.recordcount eq 0>
<cfreturn “Sorry, your api key is invalid and/or SMS code is invalid.”>
<cfabort>
</cfif>
</cfif>
“
Thus, if the caller passes both a “token” and a SMS code the API service will authenticate both. And we have two layers of security in place at the endpoint.
Let’s add them to the caller and the gateway files.
For the caller, getCarInfo.cfm file, we add the following code:
<cfparam name=”URL.sms” default=””>
and update the cfhttp url with the following:
CarGate.cfm?make=#URL.make#&sms=#URL.sms#
For the gateway file, CarGateway.cfm, we add similar code as we did with the caller. That is, adding
<cfparam name=”URL.sms” default=””>
and update the cflocation url to the following:
carInfoService.cfc?method=CarsInfo&auth=#GetHttpRequestData().headers.authorization#&make=#URL.make#&sms=#URL.sms#
Done!
Good to go!
We only give our gateway’s URL to our API consumers (callers), thus, our CFC-based API service is directly not exposed to them.
Now this API has two levels of security implemented. But of course you would need some third party for the SMS service. Alternative, you could use email or another measure.
As for the ‘Other security measures’ such as quotas and throttling, they are easy to implement as well. Basically creating some db table to capture/store incoming call and compare/check them upon request.
A few words on returned data format
By default, it’s json. But it’s easy to support XML data return as well. Since security is the prime focus for this writing, I didn’t dive into return data format.
Following screenshot is what we get when calling the car API service via the gateway with a parameter of “make=Ford”:
And if we call it by default we’ll see the following screenshot:
There’s one piece of critical data missing from the car data set. If in the real world we come across something like that we should relay it to relevant business stakeholders.
Observation
Last but not least, on top of everything that is discussed, we should strive to come up with an innovative security method as a secret layer of security.
Support for Future Automatic Upgrade
With this architecture, when you create and announce future release such as “v2” for your API service, you add a “v2” directory under “/APIgateway” directory and place your new service files there, then, simply change a single letter of “2” from the gateway file, that is,
from
“APIgateway/v1/carInfoService.cfc?”
to
“APIgateway/v2/carInfoService.cfc?”
in your CarGateway.cfm file, and you API upgrade to “v2” is now complete. Your API end users / callers do not need to do anything. How convenient!
RedHat considers API gateway very important, here’s what they say about it, “API gateways act as the major point of enforcement for API traffic”.
Hope you find this writing informative. And I believe similar code can be written with this architecture for php, .net etc. languages.
I welcome thoughts and feedback.