On the server, is there any way to differentiate between an internal & external REST API request?
Why?
The reason I want to distinguish the two origins, is because I may, based on the advice, given by respondents, want to return a different data set, depending on who is trying to make the request.
Summary
My definition of internal maybe incorrect. In this instance, 'internal' means a request made from an XHTTP request from the same domain as the page processing the request.
An external call might be a user creating a Curl request from another domain.
For instance:
http.service.ts
INTERNAL ANGULAR 6 REQUEST
fetchLogin(formData: any): Observable<any> {
let req = null;
let headers = null;
headers = {
reportProgress: false,
headers: new HttpHeaders({
'email': formData['email'],
'password': formData['password']
})
};
req = new HttpRequest('POST', this.restApiUrl + this.restApiUrlEndpoint + '/oauth/', '', headers);
return this.http.request(req)
.map( (data) => {
return 'body' in data ? data['body'] : null;
})
.pipe(
catchError(this.handleError)
);
}
template.cfm
EXTERNAL COLDFUSION REQUEST
<cfset httpUrl = request.restApiUrl & request.restApiUrlEndpoint & "/oauth/">
<cfhttp url="#httpUrl#" method="post" result="result" timeout="30">
<cfhttpparam type="header" name="email" value="foo@bar.com" />
<cfhttpparam type="header" name="password" value="foo" />
</cfhttp>
Please understand that I have simplified these 2 code snippets to keep things clear.
When the request hits the server, how can I tell which request has come via XHTTP and which has been sent via CFHTTP [Curl]?
I am using Taffy.io REST API framework, so here is a simplified method, inside a 'resources' CFC, that I might use to process the request:
resources/oauthMember.cfc
<cfcomponent extends="taffy.core.resource" taffy_uri="/oauth">
<cffunction name="post">
<cfset var local = StructNew()>
<cfset local.data['email'] = "">
<cfset local.data['password'] = "">
<cfset local.requestBody = getHttpRequestData().headers>
<cftry>
<cfset local.data['email'] = Trim(local.requestBody['email'])>
<cfset local.data['password'] = Trim(local.requestBody['password'])>
<cfcatch>
</cfcatch>
</cftry>
...processing code
<cfreturn representationOf(local.data) />
</cffunction>
</cfcomponent>
Adding an extra header to one of the calls is not viable, because this can easily be spoofed.
Any ideas?
Environment
Windows 2008R2 Lucee 4.5 IIS7+
In short, you can’t trust anything in coming request in public REST API data. Anything in request content/headers can be constucted by curl or custom program. You, probably, may have some trust in socket Source IP address, if your web server configured accordingly and has direct access to source connection address from socket itself. This is rare case nowadays, as usually web servers now located behind load balancers, firewalls with NAT tunnels, etc. But even in that case, Source IP address probably may be used only for some sort of whitelisting. Moreover, server that has this kind of access today, may lose it tomorrow, when your application may need load balancer to scale. Also note that user may have a HTTP proxies on a source path. So, using source IP as API criteria looks like bad practice.
Good practice is to create public API that is invariant to caller. API calls should concentrate on REST request body and headers provided to check for theirs validity, acceptability and security. Indeed, many API calls should be done in sequence like “login” -> sessionToken -> “API calls with sessionToken” -> “logout with sessionToken” (token invalidated). In that case important data (user Id, role, security context, etc) stored on server attached to sessionToken somehow. Note: many API architects do not recommend to store sessionToken in cookie, as it may simplify CSRF attacks (if no other countermeasures provided).
What you and probably many others want is to have “private” API to implement your web site functionality itself and “public” API to your customers. That’s a bit different story.
Public API you document, publish (at least to customers), promise to keep unchanged during long period of time.
Private API you may change at any moment and you may design it in a way that is comfortable to you. But if your site is live, anyone can still scan his own traffic and construct similar requests with curl (or something alike). You may, however, do some tricks to made abuser’s life harder, like issue some kind of JavaScript-calculated page tokens, use short-lived request chains etc, but this is not eliminated threat completely. So API cannot relay on that “nobody can construct such request” if all data for the request may be obtained from page and traffic by legitimate user.
Resume: Better to have public API for customers and private API to site itself and do not mix them.