Hoe maak je een geweldige api? een gids voor functionele prestatiebeveiliging

7 mrt. 2024 | by Ralph Van Der Horst

Hoe maak je een geweldige API? Een gids voor functionele prestatiebeveiliging

Hoe maak je een geweldige API: een gids voor iedereen die geïnteresseerd is

Waarom API’s maken, het belang van API’s

Een API (Application Programming Interface) fungeert als een menu voor software, waarin de beschikbare functies worden beschreven en hoe deze kunnen interageren. Het is essentieel om verschillende softwaresystemen soepel en efficiënt te laten communiceren. Een goed ontworpen API kan de functionaliteit en waarde van softwaretoepassingen aanzienlijk verbeteren door ze in staat te stellen naadloos gebruik te maken van externe diensten of gegevens.

Ben je klaar om je eigen API te maken, maar weet je niet waar je moet beginnen? Maak je geen zorgen! We staan voor je klaar met enkele eenvoudige stappen en voorbeelden om je te helpen een fantastische API te bouwen die veilig, snel en gebruiksvriendelijk is, zodat deze op de juiste manier kan worden ontwikkeld en getest.

Stap 1: Uw API functioneel plannen

  • Consistentie: uw API moet gemakkelijk te lezen en te begrijpen zijn. Gebruik duidelijke naamgevingsconventies en zorg voor een consistente indeling in je API om verwarring te voorkomen.
  • Passende acties: Gebruik HTTP-werkwoorden (GET, POST, PUT) op de juiste manier om de functionaliteit van uw API logisch te organiseren.
  • Uitgebreide dekking: zorg ervoor dat uw API alle acties omvat die gebruikers mogelijk nodig hebben, zodat deze echt nuttig is.
  • Gegevensintegriteit en beveiliging: Wees duidelijk en consistent in de gegevens die je API nodig heeft en levert, en zorg ervoor dat je gevoelige informatie niet onnodig blootstelt.
  • Duidelijke reacties: zorg ervoor dat je API duidelijke feedback geeft, zodat gebruikers weten wat het resultaat is van hun verzoeken of waarom een verzoek is mislukt. Voorbeeld: OpenAPI-specificatie

Toont een basisconfiguratie met behulp van de OpenAPI-standaard, die gemakkelijk te begrijpen is en ervoor zorgt dat uw API goed gedocumenteerd en testbaar is.

Een api-configuratie kan worden gegenereerd in de openapi-standaard, zodat deze gemakkelijk leesbaar en goed te begrijpen is

openapi: 3.0.3
info:
  title: SAP Accounts Payable API
  description: API for managing accounts payable, including vendor details and invoice entries.
  version: 1.0.0
servers:
  - url: http://sap.example.com/api/v1
paths:
  /vendors/{vendorId}:
    get:
      summary: Get vendor details
      operationId: getVendorById
      parameters:
        - name: vendorId
          in: path
          required: true
          description: Unique identifier of the vendor
          schema:
            type: string
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Vendor'
        '404':
          description: Vendor not found
  /invoices:
    post:
      summary: Create a new invoice entry
      operationId: createInvoice
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewInvoice'
      responses:
        '201':
          description: Invoice created successfully
        '400':
          description: Invalid input
components:
  schemas:
    Vendor:
      type: object
      properties:
        vendorId:
          type: string
          description: Unique identifier of the vendor
        name:
          type: string
          description: Name of the vendor
        email:
          type: string
          description: Email address of the vendor
    NewInvoice:
      type: object
      properties:
        vendorId:
          type: string
          description: Vendor associated with the invoice
        amount:
          type: number
          format: double
          description: Total amount of the invoice
        dueDate:
          type: string
          format: date
          description: Due date for the invoice payment

Dit voorbeeld bevat een standaard OpenAPI-specificatie voor een SAP Accounts Payable API, die bewerkingen omvat voor het beheren van leveranciersinformatie en factuurboekingen. De voorbeelden van contracttests laten zien hoe de functionaliteit van de API kan worden gevalideerd door middel van positieve en negatieve tests, waarbij ervoor wordt gezorgd dat de API zich onder verschillende omstandigheden gedraagt zoals verwacht. Deze aanpak helpt de betrouwbaarheid en integriteit van de API te behouden, wat cruciaal is voor financiële transacties zoals crediteurenbeheer.

Voorzichtig bijwerken: Als je wijzigingen aanbrengt in je API, doe dat dan op een manier die de API niet kapot maakt voor gebruikers. U hebt mogelijk verschillende versies (zoals v1, v2) voor grote wijzigingen, u kunt pact gebruiken voor schijncontroles om te voorkomen dat wijzigingen in de productie worden doorbroken. Pact heb ik uitgelegd in een andere blog op mijn website

Geef nuttige fouten: Als er iets misgaat, moet je API op een eenvoudige manier uitleggen wat er is gebeurd. Dit helpt gebruikers bij het oplossen van hun verzoeken.

Hoe kun je die functionele controles instellen? Door de documentatie zorgvuldig te documenteren en impliciet te valideren, zouden die fouten er functioneel uit moeten zien als

  1. Validatiefouten

Wanneer de gebruiker gegevens verstrekt die niet voldoen aan de validatiecriteria (er ontbreekt bijvoorbeeld een verplicht veld of een waarde valt buiten het toegestane bereik).

  • Ongeldig foutbericht: „Validatie mislukt.”
  • Goede foutmelding: „Validatiefout: het veld ’e-mail’ is verplicht en moet een geldig e-mailadres zijn.”
  1. Verificatie- en autorisatiefouten

Wanneer een gebruiker niet is geverifieerd (aangemeld) of geen toestemming heeft om toegang te krijgen tot een bepaalde bron of een bepaalde actie uit te voeren.

  • Ongeldig foutbericht: „Toegang geweigerd.”
  • Goede foutmelding: „Authenticatiefout: u moet aangemeld zijn om toegang te krijgen tot deze bron.” of „Autorisatiefout: u hebt geen toestemming om deze actie uit te voeren.”
  1. Bron niet gevonden

Wanneer de gevraagde bron (bijvoorbeeld een specifiek item of eindpunt) niet bestaat.

  • Ongeldig foutbericht: „Niet gevonden.”
  • Goede foutmelding: „Het gevraagde item met ID 12345 is niet gevonden. Controleer of de ID juist is en probeer het opnieuw.”
  1. Snelheidsbeperking

Wanneer een gebruiker in korte tijd te veel aanvragen heeft gedaan.

  • Ongeldig foutbericht: „Verzoeklimiet overschreden”.
  • Goede foutmelding: „Je hebt je aanvraaglimiet van 100 verzoeken per uur overschreden. Wacht alstublieft even voordat u nieuwe aanvragen indient.”
  1. Interne serverfouten

Als er iets misgaat aan de serverzijde dat de gebruiker niet direct kan herstellen.

  • Ongeldig foutbericht: „Interne serverfout”.
  • Goede foutmelding: „Er is een onverwachte fout opgetreden aan onze kant. We zijn op de hoogte gebracht en werken aan een oplossing. Probeer het later opnieuw.”
  1. Afhankelijkheidsfouten

Wanneer uw API afhankelijk is van externe services of systemen die momenteel niet beschikbaar zijn of niet werken.

  • Ongeldig foutbericht: „Service niet beschikbaar.”
  • Goede foutmelding: „Onze service voor het omrekenen van valuta is momenteel niet beschikbaar. We zijn op de hoogte van het probleem en werken eraan om de service zo snel mogelijk te herstellen. Probeer het later opnieuw.”
  1. Fouten in de gegevensindeling

Wanneer het formaat van de verstrekte gegevens (bijvoorbeeld JSON, XML) onjuist of onjuist is.

  • Ongeldig foutbericht: „Ongeldig verzoek.”

  • Goede foutmelding: „De hoofdtekst van uw aanvraag bevat ongeldige JSON. Corrigeer dit en probeer het opnieuw.” Voor elk type fout is het belangrijk om:

  • Geef duidelijk aan wat het probleem is.

  • Geef indien mogelijk begeleiding of suggesties om het probleem op te lossen.

  • Gebruik een beleefde en constructieve toon. .

Stap 2: Uw API zo snel veilig en krachtig maken (NFR-testen)

Beveiligingspraktijk 1) Keep It Safe: Uw API moet controleren wie de API probeert te gebruiken. Je kunt wachtwoorden, tokens (zoals een geheime sleutel) of andere methoden gebruiken om ervoor te zorgen dat alleen de juiste mensen toegang hebben.

components:
  securitySchemes:
    OAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://sap.finance.com/oauth2/authorize
          tokenUrl: https://sap.finance.com/oauth2/token
          scopes:
            read:invoices: "Read access to invoices"
            write:invoices: "Write access to invoices"

Of gebruik basisauth (tegenwoordig niet de voorkeursoplossing in prod maar om goed te testen)

RequestBody en reacties vind je hier

import requests
from requests.auth import HTTPBasicAuth

# SAP service URL
url = 'http://sap.example.com/api/v1/invoices'

# Your credentials
username = 'your_username'
password = 'your_password'

# Make a GET request with Basic Authentication
response = requests.get(url, auth=HTTPBasicAuth(username, password))

# Check if the request was successful
if response.status_code == 200:
    # Process the response data (assuming JSON response)
    data = response.json()
    print(data)
else:
    print(f'Failed to retrieve data. Status code: {response.status_code}')
import requests

# The URL of the API you're calling
url = 'http://example.com/api/data'

# Your API key
api_key = 'your_api_key_here'

# The header where the API key is expected (commonly 'Authorization' or 'X-API-Key')
headers = {
    'Authorization': f'Bearer {api_key}',
    # or if the API expects a custom header, it might look like
    # 'X-API-Key': api_key
}

# Make the request
response = requests.get(url, headers=headers)

# Check if the request was successful
if response.status_code == 200:
    # Process the response data (assuming JSON response)
    data = response.json()
    print(data)
else:
    print(f'Failed to retrieve data. Status code: {response.status_code}')

Voorkom overbelasting: Stel limieten in voor hoe vaak iemand je API om iets kan vragen. Dit helpt je API soepel te laten werken zonder te crashen.

from flask import Flask, jsonify
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

# Initialize Flask app
app = Flask(__name__)

# Initialize Limiter
limiter = Limiter(
    app,
    key_func=get_remote_address,  # Use the remote address of the client as the rate limiting key
    default_limits=["100 per hour", "20 per minute"]  # Global rate limits
)

# Apply rate limits to specific routes
@app.route("/api/data")
@limiter.limit("10 per minute")  # More restrictive rate limit for this endpoint
def get_data():
    return jsonify({"message": "This is rate limited API data."})

if __name__ == "__main__":
    app.run(debug=True)

Prestatiepraktijk 1) Strategie voor snelheidslimieten: De snelheidslimieten die u instelt („100 per uur”, „20 per minuut”) moeten gebaseerd zijn op de capaciteit van uw server en het verwachte gebruikspatroon van uw API. Mogelijk moet u deze waarden aanpassen op basis van gebruiksgegevens uit de echte wereld. Het is mogelijk om tarieflimieten aan te passen: U kunt tarieflimieten instellen die globaal, per route of zelfs dynamisch van toepassing zijn op basis van het type gebruiker dat de aanvraag doet. Overschrijding van de verwerkingssnelheid: Wanneer een klant de snelheidslimiet overschrijdt, wordt gewoonlijk een antwoord van 429 Too Many Requests geretourneerd.

Beveiligingspraktijk 2) Beveiliging en eerlijkheid: Hoewel snelheidsbeperking cruciaal is voor de bescherming van uw API, moet u ervoor zorgen dat deze eerlijk wordt geïmplementeerd en legitieme gebruikers niet onbedoeld de toegang tot uw service blokkeert. Overweeg om geavanceerdere snelheidsbeperkingen in te voeren op basis van gebruikersaccounts of API-sleutels voor een betere controle. Dus gebruiker b kan 200 aanvragen, een seconde aanvragen, enz., of de geolocatie-instellingen gebruiken.

Prestatiepraktijk 2) Caching: Overweeg om bepaalde gegevens tijdelijk op te slaan, zodat je API sneller antwoorden kan geven. Dit maakt je API efficiënter. Hieronder een voorbeeld aan de klantzijde

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Caching with localStorage</title>
</head>
<body>
    <button id="fetchDataBtn">Fetch Data</button>
    <div id="dataDisplay"></div>

    <script>
        const fetchDataBtn = document.getElementById('fetchDataBtn');
        const dataDisplay = document.getElementById('dataDisplay');

        const fetchData = async () => {
            const cacheKey = 'apiData';
            const cacheTimeKey = 'apiDataTime';
            const cacheDuration = 60000; // Cache duration in milliseconds (e.g., 60000ms = 1 minute)

            // Check if we have cached data and it's within our cache duration
            const cachedData = localStorage.getItem(cacheKey);
            const cachedTime = localStorage.getItem(cacheTimeKey);
            if (cachedData && cachedTime && new Date().getTime() - cachedTime < cacheDuration) {
                console.log('Using cached data');
                displayData(JSON.parse(cachedData));
                return;
            }

            console.log('Fetching new data');
            // Replace 'https://jsonplaceholder.typicode.com/posts' with the actual API URL
            const response = await fetch('https://jsonplaceholder.typicode.com/posts');
            if (!response.ok) {
                throw new Error(`API request failed with status ${response.status}`);
            }
            const data = await response.json();

            // Update cache with new data
            localStorage.setItem(cacheKey, JSON.stringify(data));
            localStorage.setItem(cacheTimeKey, new Date().getTime());

            displayData(data);
        };

        const displayData = (data) => {
            // Just display the first item for demonstration
            if (data && data.length > 0) {
                dataDisplay.innerText = `Post #1: ${data[0].title}`;
            } else {
                dataDisplay.innerText = 'No data found';
            }
        };

        fetchDataBtn.addEventListener('click', fetchData);
    </script>
</body>
</html>

Beveiligingspraktijk 3) Voorbeeld van SQL-injectie

from flask import Flask, request
import sqlite3

app = Flask(__name__)

@app.route('/api/search', methods=['GET'])
def search():
    keyword = request.args.get('keyword')  # User-provided search keyword

    # Dangerous: Constructing SQL directly from user input
    query = f"SELECT * FROM products WHERE name LIKE '%{keyword}%'"

    conn = sqlite3.connect('database.db')
    cursor = conn.cursor()
    cursor.execute(query)
    results = cursor.fetchall()

    return {'results': results}

if __name__ == '__main__':
    app.run()

Het beveiligingslek misbruiken:

Een aanvaller kan de zoekfunctie gebruiken om SQL-opdrachten te injecteren door een speciaal ontworpen sleutelwoord op te geven:


%'; DROP TABLE products; -- 

Wanneer deze wordt opgenomen in de API-aanvraag, wordt de geconstrueerde SQL-query:

SELECT * FROM products WHERE name LIKE '%%'; DROP TABLE products

katten; –%'

Welke tools kun je gebruiken om de functionaliteit, prestaties en beveiliging van API’s te testen?

Functionaliteitstesten

  • Postman: Postman is een van de meest populaire API-testtools en is geweldig voor handmatige en geautomatiseerde tests. Het maakt het mogelijk om eenvoudig API-tests te maken, te delen en uit te voeren en ondersteunt verschillende soorten API-verzoeken, omgevingen en authenticatiemethoden.
  • SoapUI: Hoewel het aanvankelijk gericht was op SOAP-services, ondersteunt SoapUI nu ook RESTful API’s. Het is geschikt voor functionele tests en biedt ook functies voor beveiligings- en prestatietests. SoapUI maakt testautomatisering mogelijk en kan worden geïntegreerd in CI/CD-pijplijnen.
  • Katalon Studio: een uitgebreide tool die zowel API- als UI-testen ondersteunt. Katalon Studio is ontworpen voor testers van alle niveaus en kan worden geïntegreerd met andere tools en frameworks zoals JIRA, QTest, Kobiton en Jenkins voor CI/CD-pipelines.
  • Jest is een geweldig JavaScript-testraamwerk met een focus op eenvoud, dat taken ondersteunt zoals het testen van eenheden, en in combinatie met Supertest wordt het een krachtig hulpmiddel voor het testen van HTTP API’s.
  • Rest Assured is een Java DSL voor het vereenvoudigen van het testen van op REST gebaseerde services. Het ondersteunt de notatie van de gegeven/wanneer/dan-test, die zeer leesbaar is en aansluit bij moderne testpraktijken.

Prestatietesten

  • JMeter: een open-source tool die is ontworpen voor het testen van belastingen en het meten van prestaties. Oorspronkelijk ontwikkeld voor webtoepassingen, kan JMeter ook RESTful- en SOAP-API’s testen. Het ondersteunt een breed scala aan tests, waaronder belasting-, stress- en spike-tests.
  • Gatling: Gatling is een andere open-source tool gericht op het testen van belastingen. Het staat bekend om zijn hoge prestaties en is ontworpen voor gebruiksgemak. Gatling biedt gedetailleerde prestatierapporten en ondersteunt integratie met CI/CD-tools.
  • LoadRunner: een uitgebreide tool voor het testen van belastingen van Micro Focus die een breed scala aan technologieën ondersteunt die verder gaan dan API’s, waaronder web- en mobiele apps. LoadRunner simuleert duizenden gebruikers om de prestaties van API’s onder zware belasting te testen en biedt gedetailleerde analyses.
  • Locust; Een open source-tool voor Python, waarvoor ik op grote schaal een kleine cdk-setup heb gemaakt

Beveiligingstests

  • OWASP ZAP (Zed Attack Proxy): een open-source beveiligingstool die speciaal is ontworpen voor het testen van de beveiliging van webapplicaties, die kan worden uitgebreid om API’s te testen op kwetsbaarheden zoals SQL Injection, Cross-Site Scripting (XSS) en meer.
  • Burp Suite: een populaire tool voor het testen van de beveiliging van webapplicaties, ook effectief voor het testen van de beveiliging van web-API’s. Het biedt geautomatiseerde scans en hulpmiddelen voor handmatige penetratietesters om beveiligingsproblemen te identificeren en te exploiteren.
  • Postman: Naast functionele tests kan Postman worden gebruikt voor basisbeveiligingstests, zoals het controleren op de juiste authenticatie, versleuteling en SQL-injectiekwetsbaarheden door kwaadaardige API-verzoeken te maken.
  • API Security.io: een API-beveiligingsscanner in de cloud die REST- en GraphQL-API’s scant op beveiligingsproblemen. Het controleert op veelvoorkomende beveiligingsproblemen en verkeerde configuraties.

Combinatie van gereedschappen

Sommige tools bieden functionaliteit voor meer dan één type test:

  • SoapUI: biedt functionaliteit voor functionele, prestatie- en basisbeveiligingstests van API’s.
  • Katalon Studio: ondersteunt functionele, prestatie- (via integratie met JMeter) en basisbeveiligingstests.

Conclusie

Het creëren van een geweldige API vereist een zorgvuldige planning, een focus op beveiliging en prestaties, en een toewijding aan bruikbaarheid en duidelijke communicatie. Door de richtlijnen in dit document te volgen, kunt u een API ontwerpen die niet alleen krachtig en efficiënt is, maar ook gemakkelijk te gebruiken en te integreren is voor ontwikkelaars in hun applicatie.

by Ralph Van Der Horst

arrow right
back to blog

share this article

Relevant articles

AI gegenereerde cursus die gebruik maakt van Chat gpt

AI gegenereerde cursus die gebruik maakt van Chat gpt

Door AI gegenereerde cursus die gebruikmaakt van Chat gpt

Door AI gegenereerde cursus die gebruikmaakt van Chat gpt

Chat GPT en Testdatacreatie

19 mrt. 2024

Chat GPT en Testdatacreatie