In moderne applicaties is het essentieel om betrouwbare meldingen te kunnen versturen, zowel via e-mail als SMS. Dit kan efficiënt worden beheerd door gebruik te maken van Celery, een geavanceerde taakverwerker, in combinatie met Twilio, een populaire dienst voor het versturen van SMS-berichten wereldwijd. In dit hoofdstuk leggen we uit hoe je deze technologieën kunt integreren voor het verwerken van meldingen zonder de prestaties van je applicatie in gevaar te brengen.

Wanneer een gebeurtenis in je applicatie plaatsvindt—zoals een gebruiker die zich registreert of een wachtwoord opnieuw instelt—wil je een notificatie versturen. Het is belangrijk om dit proces niet direct binnen de hoofdapplicatie te doen, omdat het anders de prestaties van de applicatie kan vertragen. In plaats daarvan kun je Celery gebruiken om de taak op de achtergrond uit te voeren, waardoor de gebruiker direct een reactie ontvangt zonder vertraging.

E-mailmeldingen via Celery

Het versturen van e-mailmeldingen wordt efficiënter wanneer je het proces asynchroon maakt. Met Celery kun je het versturen van e-mails in een wachtrij plaatsen, waarna een worker de taak verwerkt. Dit voorkomt dat de gebruiker lange wachttijden ervaart terwijl hun e-mail wordt verzonden. Wanneer een gebruiker zich registreert, wordt de taak voor het versturen van een welkomst-e-mail in de wachtrij geplaatst:

python
from fastapi import APIRouter from app.tasks import send_email_task router = APIRouter() @router.post("/register") def register_user(email: str): # ... registratie logica ... subject = "Welcome!" html_body = "<h1>Thank you for signing up!</h1>" send_email_task.delay(email, subject, html_body) return {"msg": "Registration complete. Please check your email."}

Door de delay() methode te gebruiken, wordt de taak in de Redis-wachtrij geplaatst, waar de Celery-worker deze oppikt en verwerkt zodra er capaciteit beschikbaar is. Dit zorgt ervoor dat de gebruiker direct een reactie ontvangt zonder dat de e-mailverwerking hun ervaring beïnvloedt.

Monitoring van Celery-taken

Het is cruciaal om de status van je Celery-taken te monitoren, vooral in een productieomgeving. Flower is een handig hulpmiddel om Celery-jobs in realtime te volgen. Hiermee kun je zien of een taak is voltooid, of er fouten zijn opgetreden, of dat er opnieuw geprobeerd wordt een taak uit te voeren. De Flower interface toont belangrijke informatie zoals actieve taken, geslaagde en gefaalde taken, en de status van herhalingen. Dit maakt het mogelijk om snel problemen op te lossen zonder door logbestanden te moeten zoeken.

Om Flower in te schakelen, gebruik je de volgende commando's:

bash
pip install flower celery -A app.celery_worker.celery_app flower --port=5555

Na het openen van http://localhost:5555 in je browser kun je de status van je taken monitoren en de resultaten in real-time volgen.

SMS-meldingen via Twilio

Naast e-mailmeldingen wordt SMS steeds vaker gebruikt vanwege de hogere zichtbaarheid en onmiddellijke bereikbaarheid. Twilio is een uitstekende keuze voor het versturen en volgen van SMS-berichten wereldwijd. Net als bij e-mailmeldingen, moeten SMS-meldingen ook op de achtergrond worden verwerkt om de prestaties van de applicatie niet te beïnvloeden. Dit kan worden gedaan door de SMS-verzendtaken in de Celery-wachtrij te plaatsen.

Voordat je SMS-berichten kunt versturen, moet je Twilio-gegevens configureren. Dit omvat je ACCOUNT_SID, AUTH_TOKEN, en een geregistreerd FROM_NUMBER. Deze gegevens moeten als omgevingsvariabelen worden opgeslagen voor beveiligingsdoeleinden.

python
import os TWILIO_ACCOUNT_SID = os.getenv("TWILIO_ACCOUNT_SID") TWILIO_AUTH_TOKEN = os.getenv("TWILIO_AUTH_TOKEN") TWILIO_FROM_NUMBER = os.getenv("TWILIO_FROM_NUMBER") TWILIO_API_URL = f"https://api.twilio.com/2010-04-01/Accounts/{TWILIO_ACCOUNT_SID}/Messages.json"

Om een SMS te versturen, gebruiken we HTTPX voor asynchrone verzoeken. Hier is een herbruikbare functie die een SMS verzendt via Twilio:

python
import httpx
from config import TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER, TWILIO_API_URL async def send_sms_via_twilio(to_number: str, body: str, status_callback_url: str = None): data = { "From": TWILIO_FROM_NUMBER, "To": to_number, "Body": body, } if status_callback_url: data["StatusCallback"] = status_callback_url async with httpx.AsyncClient() as client: resp = await client.post( TWILIO_API_URL, data=data, auth=(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN) ) resp.raise_for_status() return resp.json()

De StatusCallback URL is essentieel voor het bijhouden van de leverstatus van de SMS. Wanneer Twilio een statusupdate heeft (zoals "afgeleverd" of "mislukt"), stuurt het een POST-verzoek naar deze URL.

Herhalingsmechanismen en monitoring van SMS-taken

Net als bij e-mailmeldingen is het belangrijk om een herhalingsmechanisme in te stellen voor SMS-taken. Wanneer een poging om een SMS te versturen mislukt (bijvoorbeeld door een tijdelijke netwerkfout), zal Celery automatisch proberen de taak opnieuw uit te voeren. Dit kan tot vier keer gebeuren, met een vertraging van 40 seconden tussen elke poging.

De Celery-taak om SMS te versturen kan er als volgt uitzien:

python
from app.celery_worker import celery_app
from app.sms_utils import send_sms_via_twilio import asyncio @celery_app.task(bind=True, max_retries=4, default_retry_delay=40)
def send_sms_task(self, to_number, message, status_callback_url=None):
try: loop = asyncio.get_event_loop() result = loop.run_until_complete( send_sms_via_twilio(to_number, message, status_callback_url) ) return result except Exception as exc: raise self.retry(exc=exc)

Wanneer een SMS-melding zoals een 2FA-code of een waarschuwing moet worden verstuurd, wordt deze taak via Celery verwerkt, wat de reactietijd van de hoofd-API versnelt.

Webhooks voor het bijhouden van de leverstatus van SMS

Twilio biedt webhooks waarmee je real-time meldingen ontvangt over de status van een SMS-bericht (bijvoorbeeld "afgeleverd", "mislukt", etc.). Door een route in FastAPI in te stellen die de webhooks van Twilio ontvangt, kun je de status van elke SMS volgen:

python
from fastapi import APIRouter, Request, status from fastapi.responses import JSONResponse router = APIRouter() @router.post("/webhooks/twilio-sms-status")
async def twilio_sms_status_webhook(request: Request):
data =
await request.form() message_sid = data.get("MessageSid") message_status = data.get("MessageStatus") to_number = data.get("To") print(f"Twilio SMS: {message_sid} to {to_number} is now {message_status}") return JSONResponse({"status": "received"}, status_code=status.HTTP_200_OK)

Deze route registreert de status van elk SMS-bericht dat door Twilio wordt verzonden, zodat de applicatie de resultaten kan opslaan of verder verwerken.

Betrouwbaarheid en schaalbaarheid van meldingen

Door deze aanpak te volgen, kun je een uiterst betrouwbare en schaalbare meldingsarchitectuur opzetten. Celery en Twilio zorgen voor een robuuste verwerking van zowel e-mails als SMS-berichten. Alle meldingen worden efficiënt verwerkt in de achtergrond, wat de prestaties van je applicatie niet beïnvloedt. Bovendien zorgen automatische herhalingen en gedetailleerde monitoring ervoor dat je altijd inzicht hebt in de status van elke taak, waardoor je snel kunt reageren op problemen.

Het is belangrijk om deze processen voortdurend te monitoren en te testen, vooral naarmate de applicatie groeit. Het uitbreiden van de meldingsinfrastructuur met andere kanalen (zoals pushmeldingen) of het aanpassen van de logica voor gebruikersvoorkeuren kan de algehele gebruikerservaring verder verbeteren.

Hoe Content Security Policy (CSP) de beveiliging van je applicatie versterkt

Content Security Policy (CSP) is een krachtige beveiligingsmaatregel die de integriteit van een webpagina waarborgt door een aantal strikte regels af te dwingen voor het laden van externe bronnen zoals scripts, stijlen, afbeeldingen, en meer. Het doel van CSP is het minimaliseren van de kans op aanvallen zoals Cross-Site Scripting (XSS) en andere code-injectieaanvallen door alleen betrouwbare bronnen toe te staan. Dit gebeurt via het gebruik van HTTP-headers die de browser instrueren welke bronnen mogen worden geladen en uitgevoerd.

In de meeste gevallen, bijvoorbeeld bij API’s en beheerderspanelen, geldt een uniforme en strikte CSP die voor alle routes van toepassing is. Echter, wanneer zowel API- als HTML-routes vanuit dezelfde applicatie worden geserveerd, kan het nuttig zijn om verschillende CSP-beleidsregels in te stellen, afhankelijk van het pad van de aanvraag. Dit kan bijvoorbeeld door middel van middleware die het CSP-beleid dynamisch aanpast, zoals weergegeven in de onderstaande code:

python
CSP_POLICY = ( "default-src 'none'; " "script-src 'self'; " "style-src 'self'; " "img-src 'self' data:; " "font-src 'self'; " "connect-src 'self' https://api.trusted.com; " "object-src 'none'; " "frame-ancestors 'none';" ) class ContentSecurityPolicyMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
response: Response =
await call_next(request) response.headers['Content-Security-Policy'] = CSP_POLICY return response app = FastAPI() app.add_middleware(ContentSecurityPolicyMiddleware)

Met deze middleware geregistreerd, zal elke uitgaande HTTP-respons automatisch de CSP-header bevatten, waardoor de browser direct de regels afdwingt bij elke pagina-lading. Dit is een essentieel aspect van de beveiliging van een webtoepassing, omdat het voorkomt dat kwaadaardige inhoud wordt geladen van onbetrouwbare bronnen.

De zichtbaarheid van CSP is het duidelijkst wanneer het de uitvoering van bepaalde content blokkeert. De browser zal weigeren om inhoud uit te voeren of weer te geven die in strijd is met het beleid, en zal duidelijke fouten tonen in de ontwikkelaarconsole. Dit maakt het voor ontwikkelaars gemakkelijker om CSP-breuken te identificeren en te verhelpen.

Inline Scripts en Stijlen

Een van de belangrijkste aspecten van CSP is het blokkeren van inline scripts en stijlen. Standaard verhindert de CSP-policy (script-src 'self') het uitvoeren van alle inline scripts, inclusief JavaScript-eventattributen in HTML-elementen zoals onclick of onload. Dit is een belangrijke maatregel om Cross-Site Scripting (XSS) aanvallen te voorkomen, waarbij aanvallers schadelijke scripts injecteren in de webpagina. Wanneer een pagina een inline script probeert uit te voeren, logt de browser een CSP-fout en wordt het script niet uitgevoerd.

Bijvoorbeeld, als een HTML-pagina het volgende bevat:

html
<script>alert('XSS');</script>

Zal de browser dit blokkerende script niet uitvoeren, aangezien het in strijd is met de CSP-regel.

Toestaan van Inline Scripts

Het is mogelijk om inline scripts toe te staan door de 'unsafe-inline' waarde toe te voegen aan het script-src gedeelte van het CSP-beleid. Bijvoorbeeld:

python
CSP_POLICY = (
"default-src 'none'; " "script-src 'self' 'unsafe-inline'; " # ... )

Hoewel dit technisch gezien mogelijk is, wordt het ten zeerste afgeraden, tenzij het absoluut noodzakelijk is voor legacy code of andere onvermijdelijke situaties. Het toestaan van inline scripts opent namelijk de deur naar potentiële XSS-aanvallen, waardoor de voordelen van CSP worden verminderd. Het is veel veiliger om alle scripts extern te laden en te controleren op de integriteit van de geladen bestanden via hashing of het gebruik van Subresource Integrity (SRI).

Beperkingen en Voorzichtigheid

Bij het instellen van een strikte CSP moet je zorgvuldig afwegen welke bronnen daadwerkelijk nodig zijn en deze expliciet toestaan. Te veel permissies, zoals het toestaan van inline scripts of het opnemen van te veel externe domeinen, kunnen de beveiliging verzwakken. Anderzijds, een te strikte policy kan ervoor zorgen dat legitieme inhoud of functionaliteiten niet goed werken. Het is dus belangrijk om de CSP regelmatig te testen en ervoor te zorgen dat de juiste balans tussen beveiliging en functionaliteit wordt bewaard.

Net zoals bij andere beveiligingsmaatregelen, is het belangrijk om te realiseren dat CSP slechts één onderdeel is van een breder beveiligingsbeleid. Het gebruik van HTTPS, correcte inputvalidatie, veilige opslag van gevoelige gegevens, en een gedegen authenticatie- en autorisatiebeleid zijn net zo cruciaal voor de algehele veiligheid van een webtoepassing. CSP kan aanvallen zoals XSS helpen blokkeren, maar het is niet de enige barrière tegen kwaadwillende aanvallen.

Het gebruik van een goed gedefinieerde Content Security Policy is een fundamenteel onderdeel van moderne webbeveiliging. Het vereist zorgvuldige planning en implementatie, maar het biedt aanzienlijke voordelen in het beschermen van gebruikers en hun gegevens tegen veel voorkomende webaanvallen. Het is belangrijk om niet alleen de basisregels te begrijpen, maar ook om doorlopend te monitoren en te testen op nieuwe mogelijke kwetsbaarheden.

Hoe werkt het registratie- en inlogproces met een tijdslimiet voor bevestigingslinks in een webapplicatie?

Bij het ontwikkelen van een registratie- en inlogsysteem voor een webapplicatie is het essentieel om zowel de veiligheid van gebruikersaccounts als de gebruiksvriendelijkheid van het systeem te waarborgen. Een veelgebruikte methode voor het beveiligen van nieuwe gebruikersaccounts is door middel van een bevestigingsmail, die alleen geactiveerd kan worden door de eigenaar van het opgegeven e-mailadres. Dit proces zorgt ervoor dat gebruikers hun e-mailadres verifiëren en hun account activeren voordat ze toegang krijgen tot de applicatie. Het implementeren van zo'n systeem bestaat uit verschillende stappen, die hieronder in detail worden besproken.

Het registreren van een nieuwe gebruiker en het versturen van een bevestigingsmail

Wanneer een nieuwe gebruiker zich registreert, gebeurt dit doorgaans door het invullen van een formulier met hun e-mailadres en wachtwoord. Na het indienen van dit formulier, wordt het wachtwoord eerst gehasht met een veilige hashingfunctie, zoals bcrypt, om te voorkomen dat het wachtwoord in platte tekst in de database wordt opgeslagen. Vervolgens wordt het account gemarkeerd als inactief, omdat de gebruiker zijn e-mailadres nog moet bevestigen. Op dat moment wordt er een achtergrondtaak gestart die een bevestigingsmail naar het opgegeven e-mailadres stuurt.

Deze bevestigingsmail bevat een unieke link die een tijdelijke token bevat, welke gekoppeld is aan het ID van de gebruiker. Het doel van dit token is om de identiteit van de gebruiker te verifiëren en ervoor te zorgen dat alleen de eigenaar van het opgegeven e-mailadres het account kan activeren. Dit token wordt gegenereerd met behulp van een veilige serializer die het token en de bijbehorende gebruikers-ID coderen. Het token heeft een beperkte geldigheid, meestal 30 minuten, zodat de gebruiker binnen die tijd moet bevestigen.

Het genereren en verifiëren van het token

De tokenverificatie is een essentieel onderdeel van dit proces. Zodra de gebruiker op de bevestigingslink in de e-mail klikt, wordt het token in de URL meegegeven naar de server. De server gebruikt dit token om de identiteit van de gebruiker te controleren en te bevestigen dat het nog geldig is. Als het token geldig is, wordt het account van de gebruiker geactiveerd. Zo wordt het voor een kwaadwillende persoon onmogelijk om de accountregistratie te voltooien, zelfs als ze toegang hebben tot de e-mail van de gebruiker, zonder het juiste token te hebben.

Het verzenden van de bevestigingsmail op de achtergrond

Om de gebruikerservaring niet te vertragen, wordt de e-mail met de bevestigingslink in de achtergrond verzonden. Dit gebeurt met behulp van een achtergrondtaak, bijvoorbeeld via de BackgroundTasks-functionaliteit van FastAPI. Door de taak op de achtergrond uit te voeren, kunnen andere API-verzoeken zonder vertraging worden verwerkt, wat vooral belangrijk is voor de snelheid en betrouwbaarheid van de applicatie.

Het verifiëren van het gebruikersaccount

Wanneer de gebruiker de bevestigingslink in hun e-mail klikt, komt hij terecht op een endpoint dat de geldigheid van het token controleert. Dit endpoint decodeert het token en controleert of het overeenkomt met een bestaand gebruikers-ID in de database. Als het token correct is en de gebruiker nog niet geactiveerd is, wordt hun account geactiveerd. Indien er een fout optreedt, bijvoorbeeld als het token verlopen is of het account al geactiveerd is, zal de server een foutmelding terugsturen, zodat de gebruiker weet wat er mis is.

Beveiligde login en wachtwoordherstel

Naast de registratie en het bevestigen van een account is het van cruciaal belang om een beveiligde login- en wachtwoordherstelmechanisme te implementeren. Het wachtwoord van de gebruiker wordt gehasht en opgeslagen met behulp van een beveiligde hashfunctie, zoals bcrypt. Deze functie is speciaal ontworpen om brute-force aanvallen tegen te gaan door de hashingprocedure opzettelijk langzamer te maken.

Wanneer een gebruiker probeert in te loggen, wordt het ingevoerde wachtwoord vergeleken met de opgeslagen gehashte versie in de database. Als het wachtwoord overeenkomt, wordt een JWT (JSON Web Token) gegenereerd en aan de gebruiker verstrekt. Dit token wordt gebruikt om de identiteit van de gebruiker te verifiëren bij toekomstige verzoeken aan beschermde API-eindpunten.

Het genereren en gebruiken van JWT's

Een JWT is een digitale handtekening die de identiteit van de gebruiker en de vervaldatum van het token bevat. Het gebruik van een JWT zorgt ervoor dat de gebruiker na het inloggen toegang heeft tot beschermde bronnen zonder telkens opnieuw hun inloggegevens in te voeren. Het token bevat de gebruikers-ID, e-mailadres en de vervaldatum, en wordt door de server geverifieerd bij elk verzoek om ervoor te zorgen dat de gebruiker nog steeds geauthenticeerd is.

Het verifiëren van het JWT-token wordt meestal gedaan door middel van een OAuth2-systeem, waarbij het token wordt opgehaald uit de Authorization-header van het verzoek. De server decodeert het token en haalt de gegevens van de gebruiker op om hun identiteit te bevestigen. Alleen als het token geldig is, krijgt de gebruiker toegang tot de gevraagde gegevens.

Het belang van tokenverificatie en veilige loginmethodes

De belangrijkste takeaway uit dit systeem is het belang van veilige tokenverificatie en het gebruik van betrouwbare wachtwoord-hashingmethoden. Een token biedt een manier om de identiteit van een gebruiker te verifiëren zonder dat er elke keer opnieuw inloggegevens moeten worden ingevoerd. Tegelijkertijd zorgt de hashing van wachtwoorden ervoor dat zelfs als een database wordt gecompromitteerd, de daadwerkelijke wachtwoorden niet in gevaar komen.

Het proces van het versturen van bevestigingsmails en het verifiëren van tokens is slechts een deel van de totale beveiliging van een systeem. Het is essentieel dat een applicatie niet alleen deze stappen uitvoert, maar ook zorgt voor andere beveiligingslagen, zoals het beschermen van API's tegen ongeautoriseerde toegang, het versleutelen van gegevens en het toepassen van strikte toegangscontrole. Alleen door alle onderdelen van het systeem goed te beveiligen, kan de privacy van de gebruikers worden gewaarborgd.