Deze vertaling is niet volledig. Help dit artikel te vertalen vanuit het Engels.

In dit artikel wordt ingegaan op wat er gebeurd wanneer de gebruiker een formulier verstuurd - waar gaan de gegevens naar toe en wat te doen als de gegevens er aan komen? Er wordt ook ingegaan op enkele veiligheidsaspecten in verband met het verzenden van formuliergegevens.

Voorafgaande kennis: Basis computergebruik,  inleiding tot HTML en basiskennis HTTP en programmering aan de kant van de server.
Doel: Begrijpen wat er gebeurd wanneer formuliergegevens verzonden worden met inbegrip van hoe de gegevens verwerkt worden door de server.

Waar gaan de gegevens naar toe?

Hier wordt besproken wat er gebeurd met de gegevens wanneer een formulier wordt verzonden.

Client/server architectuur

Het web is gebaseerd op een client/server architectuur die als volgt kan samengevat worden: een client (meestal een webbrowser) stuurt een verzoek naar een server (meestal een webserver zoals  Apache, Nginx, IIS, Tomcat, enz.) gebruik makend van het  HTTP protocol. De server beantwoordt het verzoek met hetzelfde protocol.

A basic schema of the Web client/server architecture

Aan de zijde van de client is een HTML formulier niet meer dan een gebruiksvriendelijke manier om een  HTTP verzoek te verzenden naar een server. Dit laat de gebruiker toe gegevens in te geven in het HTTP-verzoek.

Nota: Zie Eerste stappen in programmering van websites aan de kant van de server om een beter idee te krijgen van hoe de client-server architectuur werkt.

Aan de zijde van de client: bepalen hoe de gegevens te verzenden

Het <form> element bepaalt hoe de gegevens zullen verzonden worden. Alle attributen zijn aanwezig om het verzoek samen te stellen en te verzenden wanneer de gebruiker op "Verzenden" klikt. De twee belangrijkste attributen zijn action en method.

Het action attribuut

Dit attribuut bepaalt waar de gegevens naar toe zullen gezonden worden. De waarde moet een geldige URL zijn. Als dit attribuut niet opgegeven wordt, worden de gegevens verzonden naar de URL van de pagina waarop het formulier staat.

In volgend voorbeeld worden de gegevens verzonden naar een absolute URL — http://foo.com:

<form action="http://foo.com">

Hier wordt gebruik gemaakt van een relatieve URL  — de gegevens worden verzonden naar een andere URL op de server:

<form action="/somewhere_else">

Als er geen attributen gespecifieerd worden, worden de <form>gegevens verstuurd naar de pagina waarop het formulier staat:

<form>

Oudere pagina's gebruiken de volgende notatie om aan te geven dat de gegevens moeten verstuurd worden naar dezelfde pagina als waar het formulier op staat. Dit was nodig omdat tot HTML5 het action attribuut  vereist was. Dit is niet langer meer nodig.

<form action="#">

Nota: het is mogelijk om een URL te specifiëren die gebruik maakt van het HTTPS  (secure HTTP) protocol. Dan worden de gegevens samen met de rest van het verzoek versleuteld zelfs wanneer het formulier op een niet versleutelde pagina staat die met HTTP benaderd werd. Als het formulier daarentegen op een versleutelde pagina staat en er wordt een onversleutelde HTTP URL gevraagd met het action attributut, zullen alle browsers een veiligheidswaarschuwing tonen zodra de gebruiker gegevens tracht te verzenden omdat zij niet versleuteld zullen worden.

Het method attribuut

Dit attribuut bepaalt hoe de gegevens worden verzonden. Het HTTP protocol voorziet diverse methoden om een verzoek op te stellen. De twee meest gebruikte methoden zijn de GET en de POST methoden.

Om het verschil te kennen moet gekeken worden naar hoe HTTP werkt. Elke keer de gebruiker iets zoekt op het internet zendt de browser een verzoek naar een URL. Een HTTP verzoek bestaat uit twee delen: een header die metadata bevat over de browser en het eigenlijk bericht.

De GET methode

Met de GET methode vraagt de browser aan de server een bepaalde gegevensbron: "Hallo server ik wens die gegevensbron". In dit geval wordt het gevraagde gehecht aan de URL. De rest van het bericht is leeg.

Bekijken we het volgende formulier::

<form action="http://foo.com" method="get">
  <div>
    <label for="say">What greeting do you want to say?</label>
    <input name="say" id="say" value="Hi">
  </div>
  <div>
    <label for="to">Who do you want to say it to?</label>
    <input name="to" value="Mom">
  </div>
  <div>
    <button>Send my greetings</button>
  </div>
</form>

Als gevolg van de GET methode ziet men bij het verzenden de URL www.foo.com/?say=Hi&to=Mom verschijnen in de adresbalk.

De gegevens worden aan de URL gekoppeld als een reeks naam/waarde paren. Na het URL webadres komt een vraagteken (?) gevolgd door de naam/waarde paren, elk paar gescheiden door een ampersand (&). In dit geval versturen we twee eenheden gegevens naar de server:

  • say, dat de waarde Hi heeft
  • to, dat de waarde Mom heeft

Het HTTP-verzoek ziet er zo uit:

GET /?say=Hi&to=Mom HTTP/1.1
Host: foo.com

Nota: Dit voorbeeld is ook te vinden op GitHub — zie get-method.html (see it live also).

De POST methode

De POST methode is een beetje anders. Het is de methode die de browser gebruikt om te communiceren met de server wanneer hij een reactie vraagt op de inhoud van zijn HTTP-bericht: "Hallo server, kijk naar mijn bericht en zend mij informatie hierover". Als een formulier op deze manier wordt verzonden worden de gegevens in het bericht geplaatst en niet in de URL van het HTTP-bericht.

Nemen we een voorbeeld — dit is hetzelfde formulier als in de GET sectie hierboven, maar met het method attribuut ingesteld op post.

<form action="http://foo.com" method="post">
  <div>
    <label for="say">What greeting do you want to say?</label>
    <input name="say" id="say" value="Hi">
  </div>
  <div>
    <label for="to">Who do you want to say it to?</label>
    <input name="to" value="Mom">
  </div>
  <div>
    <button>Send my greetings</button>
  </div>
</form>

Als het formulier wordt verzonden met de POST methode worden er geen gegevens gekoppeld aan de URL en het HTTP verzoek ziet er als volgt uit, met de gegevens in het bericht:

POST / HTTP/1.1
Host: foo.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

say=Hi&to=Mom

Content-Length geeft de omvang van de inhoud aan en Content-Type geeft het type aan van de gegevensbron die naar de server wordt verzonden. Hier wordt later verder op ingegaan.

Nota: Dit voorbeeld kan ook gevonden worden op GitHub — zie post-method.html (see it live also).

HTTP-verzoeken bekijken

HTTP-verzoeken worden nooit getoond aan de gebruiker (om ze te zien moeten instrumenten gebruikt worden als Firefox Network Monitor of  Chrome Developer Tools). Bijvoorbeeld in de Chrome network tab:

Het enige wat getoond wordt is de aangeroepen URL. Bij een GET-verzoek staan de gegevens in de adresbalk maar bij een POST-verzoek is er niets te zien. Dit is belangrijk om twee redenen:

  1. Gebruik nooit de GET methode als een paswoord of andere gevoelige gegevens moeten verzonden worden om te voorkomen dat ze in de URL-balk getoond worden, wat erg onveilig is.
  2. Gebruik de POST methode als grote hoeveelheden gegevens moeten verzonden worden omdat sommige browsers de lengte van de URL begrenzen. Daarenboven begrenzen veel servers ook de lengte van de URL's die ze aanvaarden.

Aan de kant van de server: ontvangst van de gegevens

Onafhankelijk van de gebruikte methode van verzenden zal de server een reeks karakters ontvangen die hij zal ontleden in sleutel/waarde paren. De verdere afwikkeling is afhankelijk van het gebruikte ontwikkelingsplatform. Bijvoorbeeld de afhandeling van dubbele sleutels: meestal wordt de laatst ontvangen sleutel gebruikt.

Voorbeeld: Raw PHP

PHP biedt enkele globale objecten om de gegevens te benaderen. Het volgend voorbeeld gebruikt de POST methode en toont de inhoud gewoon aan de gebruiker. Die beslist wat er verder mee moet gebeuren: gewoon tonen, opslaan in een gegevensbank of op een andere manier verwerken.

<?php
  // The global $_POST variable allows you to access the data sent with the POST method by name
  // To access the data sent with the GET method, you can use $_GET
  $say = htmlspecialchars($_POST['say']);
  $to  = htmlspecialchars($_POST['to']);

  echo  $say, ' ', $to;
?>

Dit voorbeeld toont een pagina met de gegevens die we verzonden hebben. Dit wordt getoond in ons php-example.html voorbeeldbestand. Dit bevat hetzelfde voorbeeld als hierboven met een  post methode en als  action : php-example.php. Bij verzending worden de formuliergegevens verzonden naar  php-example.php, waar de PHP code staat uit bovenstaand voorbeeld. Bij uitvoering toont de browser: Hi Mom.

Nota: Dit voorbeeld zal niet werken wanneer het in een lokale browser geladen wordt omdat browsers geen PHP-code kunnen interpreteren. Dus bij het verzenden biedt de browser alleen aan om het PHP-bestand te downloaden. Het zal dus slechts lopen door middel van een of andere PHP-server.  Goede opties voor het lokaal testen van PHP zijn MAMP (Mac en Windows) en AMPPS (Mac, Windows, Linux).

Voorbeeld: Python

Volgend voorbeeld toont het gebruik van Python om hetzelfde te doen: tonen van verzonden gegevens op een webpagina. Dit maakt gebruik van het  Flask framework om sjablonen te tonen, versturen van formuliergegevens, enz. (zie python-example.py).

from flask import Flask, render_template, request
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def form():
    return render_template('form.html')

@app.route('/hello', methods=['GET', 'POST'])
def hello():
    return render_template('greeting.html', say=request.form['say'], to=request.form['to'])

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

De twee sjablonen waarnaar gerefereerd wordt in de code zijn de volgende:

  • form.html: hetzelfde formulier als hierboven in de  The POST method sectie maar met als  action : {{ url_for('hello') }}. (Dit is een Jinja2 sjabloon dat in HTML is maar dat de Pythoncode kan aanroepen die loopt op de webserver die aangegeven wordt in de accolades.  url_for('hello') wil in principe zeggen "stuur door naar /hello wanneer het formulier verzonden wordt".)
  • greeting.html: deze sjabloon bevat een regel die twee datavelden weergeeft die doorgegeven werden toen het bericht binnenkwam. Dit wordt gedaan via de  hello() functie hierboven die loopt wanneer naar de  /hello URL genavigeerd wordt.

Nota: Deze code zal ook nu weer niet lopen als die gewoon in een browser ingelezen wordt. Python werkt enigszins anders dan PHP. Om deze code lokaal te laten lopen moet  Python/PIP geïnstalleerd worden, dan Flask d.m.v. pip3 install flask. Daarna moet het mogelijk zijn het voorbeeld te laten lopen door middel van  python3 python-example.py en dan naar localhost:5000 te gaan in de browser.

Andere talen en omgevingen

Er zijn vele andere technologieën aan de serverkant voor behandeling van formulieren met inbegrip van Perl, Java, .Net, Ruby, enzovoorts. Kies wat u het beste ligt. In dit verband moet gezegd worden dat het niet de gewoonte is om rechtstreeks in deze omgevingen te werken omdat dit niet altijd eenvoudig is. Het is veel gemakkelijker te werken met een van de volgende fraaie toepassingen die werken met formulieren veel aangenamer maken, zoals:

Zelfs met deze omgevingen is het niet altijd eenvoudig om met formulieren te werken. Maar het is nog altijd eenvoudiger dan alle functionaliteit zelf vanaf nul te moeten schrijven. Het zal in elk geval sneller gaan.

Nota: Het is buiten het bestek van dit artikel om elke taal of omgeving aan de serverkant te bespreken. Bovenstaande links zijn een leidraad en misschien is het nuttig er zelf wat dieper op in te gaan.

Speciaal: verzenden van bestanden

Verzenden van bestanden met HTML-formulieren is speciaal. Bestanden zijn binaire gegevens, terwijl andere gegevens uit gewone tekst bestaan. Omdat HTTP een tekstprotocol is, zijn er speciale vereisten om binaire gegevens te behandelen.

Het enctype attribuut

Dit attribuut laat toe de waarde van Content-Type in te stellen van de HTTP-hoofding die meegestuurd wordt wanneer het formulier verzonden wordt. Deze hoofding is zeer belangrijk omdat die de server vertelt welke soort gegevens zullen doorgezonden worden.  Standaard is de waarde application/x-www-form-urlencoded. In mensentaal betekent dit: "Dit zijn formuliergegevens die gecodeerd zijn in URL parameters."

Om bestanden te versturen moeten er drie stappen ondernomen worden:

  • Zet het method attribuut op POST omdat bestandsinhoud niet in URL parameters kan geplaatst worden
  • Zet de waarde van enctype op multipart/form-data omdat de gegevens in meerder delen zal gesplitst worden, één voor elk bestand, plus nog een deel voor de tekstgegevens die vervat zijn in het formulier (als er tekst in het formulier aanwezig is).
  • Sluit één of meerdere bestandselecties (File picker) in om de gebruiker toe te laten de bestanden te kiezen die hij wil uploaden.

Bijvoorbeeld:

<form method="post" enctype="multipart/form-data">
  <div>
    <label for="file">Choose a file</label>
    <input type="file" id="file" name="myFile">
  </div>
  <div>
    <button>Send the file</button>
  </div>
</form>

Nota: Sommige browsers ondersteunen het multiple attribuut van het <input> element, dat toelaat meerdere bestanden te kiezen om te uploaden in slechts één <input> element. Hoe de server deze bestanden behandelt is afhankelijk van de technologie die gebruikt wordt op de server. Zoals hiervoor vermeld zal het gebruik van een omgeving het leven aangenamer maken.

Waarschuwing: Veel servers beperken de bestandsgrootte voor HTTP-verzoeken om misbruik te voorkomen. Het is belangrijk deze limiet te controleren bij de administrator van de server voordat een bestand verzonden wordt.

Veiligheidsoverwegingen

Elke keer er gegevens verstuurd worden naar een server moet aan veiligheid gedacht worden. HTML formulieren zijn de meest kwetsbare plaatsen voor aanvallen. De problemen komen nooit van de HTML formulieren zelf maar wel van de manier waarop de server met gegevens omgaat.

Al naar gelang de toepassing zijn er wel bekende veiligheids problemen die men kan tegenkomen:

XSS en CSRF

Cross-Site Scripting (XSS) en Cross-Site Request Forgery (CSRF) zijn wel bekende vormen van aanvallen wanneer gegevens getoond worden die een gebruiker terugstuurt naar een gebruiker of naar een andere gebruiker.

XSS laat aanvallers toe een script toe te voegen aan de zijde van de client aan webpagina's die bekeken werden door andere gebruikers. Een kwetsbaar cross-site script kan gebruikt worden door aanvallers om toegangscontrole te omzeilen zoals de  politiek van dezelfde bron. Het effect van deze aanvallen kan gaan van vervelend tot een ernstig veiligheidsrisico.

CSRF aanvallen zijn gelijk aan  XSS aanvallen in zo verre dat zij op dezelfde manier starten, door aan de kant van de client een script te injecteren in webpagina's, maar hun doel is anders. CSRF aanvallers trachten machtigingen aan te passen naar hogere niveaus (zoals een webadministrator) om een actie te ondernemen die anders niet zou lukken (bijvoorbeeld gegevens verzenden naar een niet-vertrouwde gebruiker).

XSS aanvallen profiteren van het vertrouwen dat een gebruiker heeft in een website terwijl CSRF aanvallen profiteren van het vertrouwen dat een website heeft in haar gebruikers.

Om zulke aanvallen te voorkomen moet een server altijd de gegevens controleren die een gebruiker er naar toe stuurt. Indien de gegevens moeten getoond worden moet er op gelet worden dat de HTML inhoud die van de gebruiker komt niet getoond wordt  Daarom moeten de gegevens van de gebruiker verwerkt worden zodat ze niet letterlijk gepresenteerd worden. Praktisch alle omgevingen die vandaag op de markt zijn hebben minimaal een filter die de HTML <script>, <iframe> en <object> elementen filtert uit de gegevens die de gebruikers versturen. Dit verkleint het risico maar schakelt het niet helemaal uit.

SQL injectie

SQL injectie is een type aanval die probeert ingrepen uit te voeren op de database van de website. Dit vereist een SQL-aanvraag in de hoop dat de server ze uitvoert (meestal doordat de server de gegevens van de gebruiker tracht op te slaan). 

De gevolgen kunnen zeer erg zijn, gaande van verlies van gegevens tot de controle over een hele website infrastructuur door toe-eigenen van machtigingen. Dit is een zeer ernstige bedreiging en gegevens van een gebruiker mogen nooit zo maar direct opgeslagen worden zonder validering (bijvoorbeeld door gebruik van  mysql_real_escape_string() op een PHP/MySQL systeem).

HTTP header injectie en email injectie

Deze aanvallen kunen gebeuren wanneer de applicatie HTTP headers  of e-mails opstelt uit de gegevens van een gebruikersformulier. Deze zullen geen onmidellijke schade aanrichten of invloed hebben op de gebruikers maar zij vormen een open deur voor dieper liggende problemen zoals het kapen van sessies of phishing aanvallen.

Deze aanvallen gebeuren meestal in stilte en wijzigen servers in een zombie.

Wees wantrouwig: vertrouw uw gebruikers nooit

Wat te doen tegen deze bedreigingen? Dit gaat te ver voor dit artikel maar er zijn enkele regels die men in acht moet nemen. De belangrijkste regel is: vertrouw nooit uw gebruikers, uzelf inbegrepen. Zelfs een betrouwbare gebruiker kan gehackt zijn.

Alle gegevens die de server bereiken moeten gecontroleerd worden. Altijd. Geen enkele uitzondering:

  • Potentieel gevaarlijke karakters moeten geweerd worden. Welke karakters dat zijn is afhankelijk van de context waarin de aangewend worden en het platform dat gebruikt wordt. Alle talen gebruikt aan de serverkant hebben deze funktie in zich.
  • Begrens de hoeveelheid inkomende gegevens tot het meest noodzakelijke.
  • Sla geüploade bestanden op in de zanbak (sla ze op op een andere server en maak ze alleen toegankelijk door een ander subdomein, of beter nog, door een volledig andere domeinnaam).

Veel/de meeste problemen worden voorkomen door deze drie regels op te volgen, maar het is altijd een goed idee de veiligheid te laten onderzoeken door een competente derde. Ga er niet van uit dat je alle mogelijke problemen overzien hebt.

Nota: Het artikel over Website beveiliging van de serverkant gaat dieper in op bovenstaande bedreigingen en potentiële oplossingen.

Besluit

Formuliergegevens verzenden is simpel maar een applicatie beveiligen is niet zo eenvoudig. Maar denk er aan dat het niet de front-end ontwikkelaar is die verantwoordelijk is voor het veiligheidsmodel. Verderop in deze cursus wordt geleerd dat validatie aan de zijde van de client mogelijk is maar de server kan deze validatie niet vertrouwen omdat hij niet weet wat er zich afspeelt aan de zijde van de client.

Zie ook

Volgende bronnen gaan dieper in op de beveiliging van websites:

Documentlabels en -medewerkers

 Aan deze pagina hebben bijgedragen: Tonnes, coenegrachts
 Laatst bijgewerkt door: Tonnes,