paint-brush
Best Practices for API Security: JavaScript and Python Examples by@katshidev
174 reads

Best Practices for API Security: JavaScript and Python Examples

by Nathan KatshiNovember 19th, 2024
Read on Terminal Reader
tldt arrow

Too Long; Didn't Read

Discover comprehensive APIs and security best practices through detailed examples in JavaScript, React, and Python. In this article, you will learn how combining these practices can significantly harden your applications.
featured image - Best Practices for API Security: JavaScript and Python Examples
Nathan Katshi HackerNoon profile picture


As we closed Cybersecurity Awareness Month three weeks ago, it is crucial to underscore the importance of secure coding practices. In this article, I highlight some basic but often overlooked API security best practices, backed by examples in JavaScript and Python.

APIs Overview

An API (Application Programming Interface) is a set of protocols and rules that allow different software applications to communicate and exchange data with each other. APIs enable seamless integration of various systems. Think of an API like a waiter in a restaurant. Just as a waiter takes your order, delivers it to the kitchen, and brings your meal back to you, an API takes a request from one application, sends it to the server, and returns the server’s response to the requesting application.


Different types of API exist, based on their architectures and use cases. Some common API types are:

  • REST (Representational State Transfer): REST APIs are based on standard HTTP protocols and are widely used for many web services. They offer simplicity and ease of use, making them popular for many programs.


  • GraphQL (Graph Query Language): GraphQL APIs allow clients to request exactly the data they need, making data retrieval more efficient. It’s a query language allowing for more flexible data interactions.


  • SOAP (Simple Object Access Protocol): SOAP APIs are protocol-based and use XML for messaging. They provide a more rigid structure and are often used in enterprise-level applications.


  • RPC (Remote Procedure Call): RPC APIs allow clients to execute code on a remote server. This technique is often used to perform functions over the network.

Risks Linked to the Use of APIs

Using APIs can expose countless security risks, especially if poorly implemented and managed. Some of the risks are broken object-level authorization and user authorization, excessive data exposure, lack of resources and rate limiting, and insufficient logging and monitoring.

Addressing these risks involves implementing robust security measures.

API Security Best Practices

API security measures can be implemented in various ways, depending on API use and application requirements. The measures implemented are language-independent. Core concepts are the same, but the syntax may slightly differ. In the examples below, we will use the URL https://api.example.com/data


1. Use HTTPS over HTTP

Using the secure version of the HTTP protocol ensures data transmitted between the client and server are kept private and protected against eavesdropping and man-in-the-middle attacks.


  • JavaScript (React)

    fetch('https://api.example.com/data', { 
      method: 'GET', 
      headers: { 'Content-Type': 'application/json',
      }, 
    });
    


  • Python

    import requests
    
    response = requests.get('https://api.example.com/data') print(response.status_code)
    


2. Use API Keys in HTTP Headers API keys are useful for authenticating and authorizing API requests, ensuring that only legitimate clients can access the API data.


  • JavaScript (React)

    fetch('https://api.example.com/data', { 
    method: 'GET', 
    headers: { 
    'Content-Type': 'application/json', 
    'Authorization': Bearer ${apiKey}, 
    }, 
    });
    


  • Python

    import requests
    
    headers = { 
    'Content-Type': 'application/json', 
    'Authorization': f'Bearer {api_key}' 
    } 
    response = requests.get('https://api.example.com/data', headers=headers) 
    print(response.status_code)
    


3. Implement OAuth 2.0

OAuth 2.0 provides secure and scalable authorization for accessing resources, allowing third-party applications to access user data without exposing credentials.


  • JavaScript (React)

    import axios from 'axios';
    
    axios.get('https://api.example.com/data', { 
    headers: { 'Authorization': Bearer ${accessToken}, 
    }, 
    });
    


  • Python

    import requests
    
    response = requests.get('https://api.example.com/data', headers={'Authorization': f'Bearer {access_token}'}) 
    print(response.status_code)
    


4. Implement Input Validation

Proper input validation ensures that only properly formatted data is accepted by the API, protecting against injection attacks and malformed input. In the examples below, I use the DOMPurify library, which is one of the most powerful and widely trusted in the development community for its robust and reliable features, notably for preventing XSS (Cross-Site Scripting) attacks.


  • JavaScript

    import DOMPurify from 'dompurify';
    
    function sanitizeInput(input) { 
    return DOMPurify.sanitize(input); }
    
    const userInput = '<script>alert("XSS Attack!")</script>'; 
    const sanitizedInput = sanitizeInput(userInput); 
    console.log(sanitizedInput); // Outputs: ""
    


In this example, DOMPurify removes the <script> tag from the user input, making it safe to use in your application.


While DOMPurify is specifically for JavaScript, similar functionality can be achieved in Python using libraries like Bleach


  • Python

    import bleach
    
    def sanitize_input(input): 
        return bleach.clean(input)
    
    user_input = '<script>alert("XSS Attack!")</script>' 
    sanitized_input = sanitize_input(user_input) 
    print(sanitized_input)  # Outputs: ""
    


5. Use Rate LimitingRate limiting controls the number of requests a client can make to an API within a specified time frame, protecting against DoS(Denial of Service) attacks.


  • JavaScript (Express)

    const rateLimit = require('express-rate-limit');
    
    const apiLimiter = rateLimit({ 
    windowMs: 15 * 60 * 1000, // 15 minutes 
    max: 100, // limit each IP to 100 requests per windowMs });
    
    app.use('/api/', apiLimiter);
    


  • Python (Flask)

    from flask import Flask 
    from flask_limiter import Limiter 
    from flask_limiter.util import get_remote_address
    
    app = Flask(name) 
    limiter = Limiter(app, key_func=get_remote_address)
    
    @app.route('/api/data') 
    @limiter.limit("100 per 15 minutes") 
    
    def get_data(): 
        return "Data"
    


6. Secure Sensitive Data

Securing sensitive data ensures data are stored and transmitted securely, and protected from unauthorized access. One of the techniques consists of storing API keys in an environment variable to avoid exposing it in the source code.


  • JavaScript (React)

a. Create a .env file in the root folder

API_KEY=your_api_key_here


b. Use the API key in the code

import React from 'react';

function fetchData() {
  const apiKey = process.env.API_KEY;

  fetch('https://api.example.com/data', {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${apiKey}`,
    },
  })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
}

# Rest of the code here


  • Python In Python, you can use the dotenv package to load the API key from an environment variable.


a. Create a .env file

API_KEY=your_api_key_here


b. Install the dotenv package.

pip3 install python-dotenv


c. Use the API key in the code.

import os
from dotenv import load_dotenv
import requests

load_dotenv()

api_key = os.getenv('API_KEY')

headers = {
    'Content-Type': 'application/json',
    'Authorization': f'Bearer {api_key}'
}

response = requests.get('https://api.example.com/data', headers=headers)

print(response.json())


7. Enable Cross-Origin Resource Sharing Properly - CORS (Cross-Origin Resource Sharing) allows you to control which domains can access your API, protecting against unauthorized cross-origin requests.


  • JavaScript (React)

    const cors = require('cors'); app.use(cors());
    


  • Python

    from flask_cors import CORS
    app = Flask(name) CORS(app)
    

Summary

Insecurity costs more than security. By combining some of these security measures, you will improve the overall application robustness and prevent various potential threats. Implementing HTTPS, API keys, OAuth 2.0, input validation, rate limiting, secure data handling, and proper CORS configuration are all critical steps toward safeguarding your APIs.


Ensuring that these measures are in place will not only protect your data and systems but also instill confidence in your users about the security of your applications.