Overview
APIs are software intermediaries that allow applications to communicate with each other. Building a secure API requires implementing multiple layers of protection including rate limiting, input validation, CORS configuration, and proper error handling.
A production API requires additional security measures beyond this guide including authentication, HTTPS encryption, content security policies, and comprehensive logging.
Setting Up a Secure Flask API
Installation Requirements
Install the necessary security packages:
Basic API Structure
Here’s a secure Flask API implementation with rate limiting and CORS protection:
from flask import Flask
from flask import request
from flask import jsonify
import database_management as dbHandler
from flask_cors import CORS, cross_origin
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
api = Flask(__name__)
cors = CORS(api)
api.config["CORS_HEADERS"] = "Content-Type"
limiter = Limiter(
get_remote_address,
app=api,
default_limits=["200 per day", "50 per hour"],
storage_uri="memory://",
)
@api.route("/", methods=["GET"])
@limiter.limit("1/second", override_defaults=False)
def get_film():
film = dbHandler.get_random_film()
# For security data is validated on entry
if request.args.get("like") and request.args.get("like").isdigit():
film_id = request.args.get("like")
api.logger.critical(
f"You have liked the film id={film_id}"
) # debugging statement only
dbHandler.record_like(film_id)
# For security data is validated on entry
if request.args.get("dislike") and request.args.get("dislike").isdigit():
film_id = request.args.get("dislike")
api.logger.critical(
f"You have disliked the film id={film_id}"
) # debugging statement only
dbHandler.record_dislike(film_id)
return jsonify(film), 200
@api.route("/add_film", methods=["POST", "HEAD"])
@limiter.limit("1/second", override_defaults=False)
def add_film():
data = request.get_json()
info = dict(request.headers)
api.logger.critical(f"User {info}")
api.logger.critical(f"Has added the movie {data}")
dbHandler.add_film(data)
return data, 201
if __name__ == "__main__":
api.run(debug=True, host="0.0.0.0", port=3000)
Security Features Explained
Rate Limiting
Rate limiting protects your API from DoS attacks and excessive demand:
limiter = Limiter(
get_remote_address,
app=api,
default_limits=["200 per day", "50 per hour"],
storage_uri="memory://",
)
@api.route("/", methods=["GET"])
@limiter.limit("1/second", override_defaults=False)
def get_film():
# Route handler code
Rate limiting is essential for public APIs to prevent abuse and maintain availability for legitimate users.
CORS Configuration
Cross-Origin Resource Sharing (CORS) allows your API to be accessed by other domains:
cors = CORS(api)
api.config["CORS_HEADERS"] = "Content-Type"
For production APIs, configure CORS to allow only specific trusted domains instead of allowing all origins.
Validate all input data before processing:
# Validate that the parameter is a digit before processing
if request.args.get("like") and request.args.get("like").isdigit():
film_id = request.args.get("like")
dbHandler.record_like(film_id)
Database Management
Implement secure database operations in a separate module:
from flask import jsonify
def get_random_film():
# driver response only, to be implemented
return {"id": 1, "name": "Frozen", "studio": "Disney"}
def record_like(film_id):
# to be implemented
return
def record_dislike(film_id):
# to be implemented
return
def add_film(data):
# verify and sanitise JSON to be implemented
# Add film to database to be implemented
return
Validate Input
Always validate and sanitize JSON data before adding to the database
Use Parameterized Queries
Never construct SQL queries using string concatenation. Use parameterized queries:cur.execute('SELECT * FROM users WHERE username == ? AND password == ?', (username, password))
Implement Error Handling
Handle database errors gracefully without exposing sensitive information
API Endpoints
| API Call | Result |
|---|
http://127.0.0.1:3000/ | Returns a random movie from the database as JSON with response code 200 |
http://127.0.0.1:3000/?like=123 | Records a like for film_id “123” if the id exists, returns response code 200 |
http://127.0.0.1:3000/?dislike=456 | Records a dislike for film_id “456” if the id exists, returns response code 200 |
http://127.0.0.1:3000/add_film | Adds a film entry to the database if JSON is valid, returns response code 201 |
Production Security Requirements
This example is for development environments. Production APIs require:
Authentication & Authorization
Implement user authentication and role-based access control
HTTPS Encryption
Use HTTPS for all API communication to encrypt data in transit
Content Security Policy
Enforce HTTPS communication through CSP headers
Comprehensive Logging
Log all HEAD, POST, and GET requests for security analysis
Defensive Data Handling
Implement input validation, data sanitization, and exception handling
Testing Your API
curl -X GET http://127.0.0.1:3000/
- Thunder Client: VSCode extension for API testing
- Postman: Standalone application for comprehensive API testing
- Browser: Use the included index.html file for simple GET/POST/HEAD testing
Best Practices
Key Takeaways:
- Always validate input data on entry
- Use rate limiting to prevent abuse
- Configure CORS appropriately for your use case
- Log security-relevant events
- Never expose sensitive data in error messages
- Use parameterized queries to prevent SQL injection
Additional Resources