In this multi-part secure development guidance series, learn how to develop a lightweight (and secure) REST API using Python and a third-party OAuth service. As we go through this series, it will contain several articles containing a sample project and architecture diagrams that can help you get a good understanding of how to write a secure REST API from scratch using Python.
Let's get some of the background stuff out of the way first. I've used APIs in the past that advertise as RESTful but are everything but that. I want to make sure my readers get the gist of REST before we start making one! If you already know all of this stuff, you can jump to part two of this series (coming soon).
- Introduction to REST (this article)
- Common Acrhitecture Patterns
- Requirements Guidance (The OWASP API Security Project)
- Python Environment Setup and Secrets Management
- Setting up Data Models and Interacting with Databases
- Testing tools
As much as I'd like to get into AWS Lambda and explaining security around "serveress", we'll save that for another series.
REST is an acronym for REpresentational State Transfer (REST), an architectural guide for creating data services. It is used to help us create scalable, performant, and reliable services to represent our data and our workflows. When this new architecture was introduced by Roy Fielding in 2000, it was introduced around the same time that HTTP 1.1 had become standardized, and much of the REST framework was optimized as part of the HTTP 1.1 roll out.
REST started becoming popular in 2005 and rapidly accelerated to make up about 70% of most web APIs in 2014. Today, it's rare to see SOAP APIs in use unless you're in an enterprise environment where upgrading back-end services is not cost efficient.
REST has six guiding principles:
- Client-Server model - keeps the user interface away from the data storage parts
- Stateless - Session state is kept entirely on the client - In the realm of security, we would typically rely on the server to provide the session context. But since we're stateless, we need to be creative in how we manage a state-like behavior, maintain security, and not fall into REST anti-patterns! Yikes! Since session state needs to kept strictly client-side, we will be cautious and utilize tokens which contain encrypted payloads between the user interface and the API
- Cacheable - The data within a response may be implicitly or explicitly labeled as cachable or non-cachable. If a request is cacheable, then the client must be able to reuse that data later, and has the option to request new data as necessary. Again, in the realm of security, we need to take care that sensitive data is not stored client-side past the time the token expires.
- Uniform interface - the principle of generality simplifies the interface and increases the visibility of interactions between components
- Layered system - these types of systems are built on top of hierarchical layers which should not be able to see past the immediate layer in which they are interacting
- Code on demand (optional) - by default We will prefer to not run code on demand in our secure REST interface because it introduces another layer of uncertainty and scrutiny. While it is convenient there is a possibility that this could be used for malcontent.
If you're interested to see an example of a non-HTTP REST interface, check out the Constrained Application Protocol (RFC 7252), which is meant for constrained devices (low power, low memory CPUs, e.g. 8-bit micro controllers) on constrained networks (lossy connections).
A good REST API will have resources as endpoints, and resource methods (in most cases, HTTP verbs) that will describe the actions that an API consumer can make on those resources. These are commonly mapped to objects in programming, or entities in databases. Typically, this means that resources should be addressed just with their name, and not with any actions associated with them. A good resource will have three attributes:
A rule of thumb that I like to follow: keep the API deployed on a separate container as the rest of your application. This will allow you to give the service name a unique hostname in the FQDN instead of relying on your app to route to a separate 'subfolder'. This will allow you to update the API independently from the rest of the application, and will allow you to create multiple apps (web, mobile, thick client, etc.) to access the data.
A good resource route will tell a consumer of the API will be of the following format, replacing the parameters in the handlebars with your own:
An endpoint is the URL, including the service name, that hosts access to specific resources. Some use this interchangeably when they mean to say route or resource. In my material, if I mention "endpoint", please be mindful I mean "service endpoint" and not "route" or "resource". While most of the time we're hosting these REST APIs over HTTP, it is also possible to use other protocols like WebSockets. You can have multiple service endpoints with the same resources; you can also have multiple resources at the same endpoints. Sometimes the endpoints appear as application gateways and route to other microservices behind a load balancer/reverse proxy.
Now that I've explained some of the principles of REST, we'll talk a bit more about the different layers and architectures commonly used. Start thinking about the terms Transport Layer, Middleware, Service Layer, Data Layer, Separation of Concerns, and what they mean to you.
Until next time!