A web application I'm developing will be a Single Page Application (SPA) that will interact with a REST API backend, through jQuery.ajax()
calls.
The SPA and API will both be served over an https/TLS connection. The API will be served from a subdomain of the SPA domain:
SPA: example.org
API: api.example.org
... and will respond with the appropriate CORS headers:
Access-Control-Allow-Origin: example.org
Access-Control-Allow-Methods: GET, POST, etc. // whatever applicable to the requested resource
Access-Control-Allow-Headers: Accept, Authorization, Content-Type
Upon logging into the SPA the user (an organisation) will be served its unique associated sha1 API-key
(either in a cookie or as a global javascript variable), that the SPA will use for interacting with the API, for the duration of the login session of the user. The SPA will issue this API-key
in each request to the API in an Authorization
header:
Authorization: MyAppsApi apikey=<API-key>
The REST API's persistence storage will be XML based. I haven't decided on an actual storage mechanism vendor yet (considering using eXistdb, at the moment). In this early stage of development, however, I'm simply using PHP's DOMDocument
and DOMXPath
, with no concurrent read/write capabilities.
The REST API will furthermore dynamically generate XPath queries, based on the received request-URI's path. Communication between the SPA and API will probably be done in JSON, though.
Consider this example XML document:
<?xml version="1.0" encoding="utf-8"?>
<organisations>
<organisation id="1">
<apiKey>some hex sha1 digest</apiKey>
<products>
<product id="1">
<parts>
<part id="1">
<subParts>
<subPart id="1">
...
</subPart>
</subParts>
</part>
</parts>
</product>
</products>
</organisation>
<organisation id="2">
<apiKey>another hex sha1 digest</apiKey>
<products>
...
</products>
</organisation>
</organisations>
Currently, this document is validated by a custom XSD schema.
The REST API will first determine if an <organisation>
node with the issued <apiKey>
exists before interacting further with the XML. If the <organisation>
node is found, it will be used as the context node for any further XPath queries.
The request-URI paths will be restricted by the following regex pattern:
~\G(/(?<collection>[a-z]+)(?:/(?<resourceId>\d+))?)(?:(?=(?1))|/?$)~
allowing only /<loweralpha>+(/<digit>+)?
segments
Consider these example request-URI paths and their dynamically generated XPath:
/products/1 => .//products/*[@id="1"]
/parts/1 => .//parts/*[@id="1"]
/products/1/parts/1 => .//products/*[@id="1"]/parts/*[@id="1"]
As you can see, they will be relative to the <organisation>
context node.
Considering that I haven't fully investigated the typical workings of XML backends yet, it may very well turn out that my above XML setup is flawed to begin with, in that I should create an XML document per organisation, mitigating the risk of accessing nodes that do not belong to the <organisation>
context node.
However, do you see any inherent flaws in this current set-up?
In my current set-up I am mostly concerned about the dynamic XPath querying that could turn out to be too risky. Perhaps an adversary is able to sneak in XPath axes, somehow? But I'm interested to hear about any other possible flaws as well.
Thank you.
PS.: perhaps I should have clarified more what the risks are, that I am most concerned with:
- Can an adversary somehow obtain the API-key of an(other) organisations?
- Can an adversary somehow manipulate content of an(other) organisations?