Logo
Lena Peters
2 Std vor
Die meisten Einsteiger tippen any, wenn ein Typ nicht sofort passt. Dabei schaltet genau das die Prüfung ab, die TypeScript leistet.

TypeScript erweitert JavaScript um statische Typen; der Compiler prüft die Typen und erzeugt daraus normales JavaScript. Wer ankommende Daten als any deklariert, verschenkt diesen Schutz vollständig.

🌐 Externe Daten aus dem Netz sind zur Laufzeit nicht typisiert. JSON ist das verbreitete Datenformat von Web-APIs: Objekte mit Schlüssel-Wert-Paaren, Listen, Zeichenketten, Zahlen sowie true, false und null. Das Backend eines anderen Anbieters kann jederzeit ein Feld weglassen, null statt einer Zahl liefern oder ein Objekt statt einer Liste schicken. Der Typ im eigenen Code weiß davon nichts.

🧩 any schaltet den Typprüfungspfad ab, unknown erzwingt ihn. Der Typ any akzeptiert jede Operation, auch solche, die zur Laufzeit scheitern. Der Typ unknown ist die typsichere Variante: Der Wert darf darin landen, aber jede Operation erfordert vorher eine Typprüfung (Narrowing, das schrittweise Einengen des Typs), sonst kompiliert der Code nicht.

🔒 Erst prüfen, dann typisieren – Schritt für Schritt. JSON.parse liefert in TypeScript any, das Ergebnis einer fetch-Abfrage muss ebenfalls selbst geprüft werden. Bewährt hat sich: Das Ergebnis sofort unknown zuzuweisen. Dann mit typeof primitive Werte prüfen, mit Array.isArray Listen erkennen und einzelne Felder gegen erwartete Typen vergleichen. Erst danach den Wert einem echten Typ wie User zuweisen.

any heißt nicht „irgendein Typ“, sondern „keine Prüfung mehr“. unknown heißt nicht „unbekannt und ungeeignet“, sondern „unbekannt und kontrolliert“.

📌 unknown statt any · Typprüfung mit Narrowing · JSON.parse liefert any

📚 Webentwicklung von Grund auf · Tag 6 von 30
#typescript #webdev #javascript
Lena Peters
5 Std vor
Ein Suchfeld sieht harmlos aus. Doch eine einzige Eingabe kann dort SQL ausführen, das der Entwickler nie geschrieben hat.

Wo Webentwickler das erste Mal SQL schreiben, passiert es: Sie bauen eine Abfrage aus Nutzereingaben zusammen. Injection steht in den OWASP Top 10 von 2021 auf Platz 3; Platz 1 ist Broken Access Control. Wer die Lücke nicht kennt, öffnet jedem Angreifer die Tür zur Datenbank.

Injection heißt: Eingabedaten werden vom Programm als Befehl gelesen, weil Befehl und Daten im selben Textfluss stehen. SQL ist die Abfragesprache relationaler Datenbanken, mit Befehlen wie SELECT, INSERT, UPDATE und DELETE. Vermischt eine Abfrage SQL-Text und Nutzereingabe, kann die Datenbank sie nicht mehr unterscheiden.

Ein Login-Formular zeigt es. Der Code hängt den Benutzernamen per String-Verkettung an einen SELECT-String an. Die Eingabe mit einem Hochkomma und einem immer wahren Vergleich beendet den Originaltext und fügt einen eigenen SQL-Befehl hinzu. Die Passwortprüfung fällt, weil die Bedingung für jede Zeile wahr ist.

Die wirksamste Abwehr sind parametrisierte Abfragen, auch Prepared Statements genannt. Der SQL-Text enthält nur Fragezeichen als Ersatz für die Werte. Diese Werte werden getrennt vom SQL-Text übertragen, deshalb liest die Datenbank sie nie als Teil des Befehls. String-Verkettung ist nie sicher, auch nicht mit Filtern.

Verwechselt wird Injection oft mit Validierungsfehlern. Validierung prüft, ob Daten erlaubt sind. Separation sorgt dafür, dass Daten überhaupt nicht als Befehl gelesen werden können.

Injection ist nicht ein Problem falscher Daten, sondern der Vermischung von Befehl und Daten. Getrennt übertragene Werte scheiden beides – und dokumentieren nebenbei, welche Eingaben Daten sind.

📌 SQL-Injection · String-Verkettung · Prepared Statements

📚 Webentwicklung von Grund auf · Tag 6 von 30
#WebSecurity #webdev #NodeJS
Lena Peters
8 Std vor
Viele Einsteiger schreiben einen Unit-Test – und der Test baut heimlich eine Datenbankverbindung auf. Der Test wird langsam, bricht ohne Netz und misst plötzlich viel mehr als die eine Funktion.

Viele denken, ein Test müsse „echt“ sein, also mit echter Datenbank und echten APIs, sonst sei er wertlos. Das klingt plausibel, weil Software am Ende ja wirklich laufen muss.

Tatsächlich prüft ein Unit-Test nur eine Einheit isoliert – meist eine einzelne Funktion oder Methode einer Klasse. Abhängigkeiten wie Datenbanken oder APIs werden dafür durch Test-Doubles ersetzt, also Nachahmungen, die festgelegte Werte liefern. Solche Tests laufen in Millisekunden und ganz ohne Netz. Das Zusammenspiel mit echten Nachbarn prüfen stattdessen Integrationstests, etwa gegen eine echte PostgreSQL-Instanz.

Der Irrtum entsteht, weil „Test“ sich erst einmal gleich anhört, egal welche Schicht er prüft. Die Testpyramide rät deshalb ausdrücklich: viele schnelle Unit-Tests, weniger Integrationstests und wenige End-to-End-Tests, also Tests über die gesamte Anwendung hinweg.

Ein Beispiel: Eine Funktion berechnet den Bruttopreis aus Nettopreis und Steuersatz. Ihr Unit-Test ruft sie mit konkreten Werten auf und vergleicht das Ergebnis mit dem erwarteten Wert, ganz ohne Datenbank. In Node.js reicht dafür seit Version 20 der eingebaute Test-Runner node:test.

„Woran man es erkennt:“ Läuft der Test offline in Millisekunden, prüft er eine Einheit. Berührt er Datenbank oder Netzwerk, ist er ein Integrationstest – und gegen eine gemockte Datenbank findet er keine SQL-Fehler, denn dafür gehört mindestens ein Test gegen die echte Datenbank-Engine dazu.

Unit heißt nicht „kleiner Test von irgendetwas“, sondern isolierte Prüfung einer Einheit mit Test-Doubles für alle Abhängigkeiten.

📌 Einheit isoliert · Test-Doubles · Testpyramide

📚 Webentwicklung von Grund auf · Tag 6 von 30
#testing #NodeJS #typescript #webdev
Lena Peters
11 Std vor
Geld verschwindet von einem Konto, kommt aber nie am anderen an. Genau dieser halbfertige Zustand verhindert eine Transaktion.

Mehrere SQL-Befehle können zu einer unteilbaren Einheit gebündelt werden. Das passiert überall dort, wo mehrere Schritte zusammengehören, etwa bei jeder Überweisung.

Zuerst beginnt die Transaktion mit BEGIN (dem Startbefehl der Einheit). Eine Überweisung von Konto A auf Konto B besteht aus zwei Befehlen. Der erste UPDATE-Befehl verringert den Kontostand von A.

Dann erhöht ein zweites UPDATE den Kontostand von B. SQL ist die Abfragesprache relationaler Datenbanken wie PostgreSQL; UPDATE ändert darin bestehende Zeilen. Beide Befehle gehören zwingend in dieselbe Transaktion.

Danach entscheidet der Abschluss. Läuft alles fehlerfrei, macht COMMIT alle Änderungen dauerhaft sichtbar. Schlägt ein Schritt fehl, etwa weil Konto B nicht existiert, verwirft ROLLBACK alles. Die Datenbank steht dann so da, als wäre nichts geschehen.

Zuletzt ein Blick auf den Einsteigerirrtum: Viele setzen jeden SQL-Befehl einzeln ab und vertrauen darauf, dass die Datenbank die Zusammengehörigkeit von selbst erkennt. Das Gegenteil ist der Fall. Ohne BEGIN führt die Datenbank jeden Befehl sofort eigenständig aus; dieses Verhalten heißt Autocommit. Das Geld wäre dann schon von A abgebucht, ohne dass B je etwas erhielt.

Diese Alles-oder-nichts-Eigenschaft heißt Atomicität. Sie ist eine der vier Grundeigenschaften, die zuverlässige Datenbanksysteme garantieren.

Transaktion heißt nicht „mehrbefehliges Skript“, sondern unteilbare Einheit: erst COMMIT macht dauerhaft, ROLLBACK nimmt alles zurück.

📌 BEGIN · COMMIT · ROLLBACK

📚 Webentwicklung von Grund auf · Tag 5 von 30
#PostgreSQL #backend #webdev
Lena Peters
14 Std vor
Ein Kommentarfeld sieht aus wie ein Feld für Text. Für den Browser ist jede Ausgabe aber erst einmal Teil der Seite, den er liest, nicht reiner Text.

Genau hier werden Eingabe und Ausgabe verwechselt. Viele prüfen, was hereinkommt, und halten die Seite dann für sicher. Ob Cross-Site Scripting (XSS) entsteht, entscheidet sich aber erst bei der Ausgabe.

Eingabefilterung prüft Daten, bevor der Server sie annimmt. Sie blockt etwa HTML-Zeichen im Kommentarfeld oder lehnt verdächtige Werte ab. Das Problem: Ein Filter kann nie alle Fälle kennen. Gibt jemand statt eines Kommentars ein script-Element mit JavaScript ein, entscheidet nicht der Filter, sondern erst die Ausgabe, ob es läuft.

Ausgabekodierung, auch Escaping genannt, greift dort, wo der Schaden entsteht. Sie ersetzt Zeichen mit Sonderbedeutung durch ungefährliche Kürzel. So wird < zu &lt;, und der Browser zeigt spitze Klammern als Text an, statt sie als Tag zu lesen.

Filterung schätzt die Eingabe ein, Kodierung entschärft die Ausgabe. Weil dieselbe Eingabe in HTML-Text, Attribut, URL oder JavaScript-String je eigene Kodierungsregeln braucht, schützt nur kontextgerechtes Escaping an jedem Ausgabeort.

„Faustregel:" Eingaben validieren, um fehlerhafte Daten abzuweisen; für Sicherheit aber jede Ausgabe im passenden Kontext kodieren. Eine Content Security Policy, die vorgibt, von wo Skripte geladen dürfen, ergänzt das als zweite Verteidigungslinie.

XSS heißt nicht, dass jemand den Server knackt, sondern dass fremdes Skript im Browser der Opfer läuft – etwa mit deren Sitzungs-Cookies. Geschlossen wird die Lücke durch Kodierung am Ausgabeort, nicht durchs Filtern allein.

📌 Eingabe prüfen · Ausgabe kodieren · Kontext beachten

📚 Webentwicklung von Grund auf · Tag 5 von 30
#WebSecurity #webdev #javascript
Lena Peters
17 Std vor
Ein dicker Ratgeber hat hinten ein Register: Trotz tausend Seiten steht der gesuchte Begriff sofort. Eine Datenbanktabelle ohne Index hat dieses Register nicht – und liest stattdessen Seite für Seite.

Das Register listet Begriffe alphabetisch, jeweils mit einer Seitenzahl daneben. Wer „Mutation“ sucht, blättert nicht, sondern springt direkt zur angegebenen Seite. Neu aufgenommene Begriffe müssen sich an der richtigen Stelle einreihen. Deshalb bleibt das Register sortiert, egal wie oft das Buch sich füllt.

Fachlich ist der Index eine sortierte Zusatzstruktur neben der Tabelle. Er speichert die Werte einer Spalte in sortierter Form, jeweils mit einem Verweis auf die zugehörige Zeile. Der Standard-Indextyp ist der B-Tree, der Gleichheit und Bereichsvergleiche sowie Sortierung unterstützt. Die Datenbank steigt darin wie im Lexikon von Verweis zu Verweis ab, statt alle Zeilen zu lesen.

Ohne Index führt die Datenbank bei einer Abfrage mit WHERE einen Sequential Scan aus: Sie prüft jede Zeile einzeln. In einer Bestelltabelle mit Millionen Zeilen gehört daher ein Index auf die Kundennummer, den Fremdschlüssel, der auf den Primärschlüssel verweist, der jede Zeile der Kundentabelle eindeutig identifiziert. Mit EXPLAIN zeigt PostgreSQL den Ausführungsplan der Abfrage, mit EXPLAIN ANALYZE wird sie zusätzlich ausgeführt und zeigt dann tatsächliche Zeiten und Zeilenzahlen.

Das Bild hat eine Grenze: Ein Buchregister wird einmal gesetzt, ein Index muss bei jedem Schreiben gepflegt werden. Jeder zusätzliche Index verlangsamt INSERT, UPDATE und DELETE und belegt Speicherplatz.

Ein Index heißt nicht „automatisch schneller“, sondern „schneller finden bei gezielten Fragen“. Er gehört auf Spalten, nach denen häufig gefiltert, sortiert oder verbunden wird, nicht auf jede Spalte.

📌 Sortierte Zusatzstruktur · B-Tree · Schreibkosten

📚 Webentwicklung von Grund auf · Tag 5 von 30
#PostgreSQL #backend #webdev
Lena Peters
20 Std vor
Zwei Kunden heißen Müller, und nach einem UPDATE trägt plötzlich der falsche Kunde die neue Adresse. Das Symptom kennt fast jeder aus dem ersten Datenbankprojekt.

In der Tabelle bestellungen stehen Name und Adresse des Kunden in jeder Zeile kopiert. Nach einer Änderung stimmen manche Zeilen, andere nicht, und niemand weiß, welche Kopie die richtige ist.

In einer relationalen Datenbank wie PostgreSQL liegen Daten in Tabellen aus Zeilen und Spalten. Jede Zeile braucht einen Primärschlüssel, also eine Spalte, deren Wert genau einmal vorkommt, oft eine fortlaufende ID, die die Datenbank beim Einfügen vergibt. Ohne eindeutige Kennung wüsste die Datenbank bei zwei inhaltsgleichen Zeilen nicht, welche gemeint ist; ein UPDATE oder DELETE könnte die falsche Zeile treffen. Der Fremdschlüssel löst das Kopierproblem: Er speichert nicht die Daten selbst, sondern nur den Verweis auf den Primärschlüssel der anderen Tabelle.

In der Tabelle kunden bekommt jeder Kunde eine eigene ID. Die Tabelle bestellungen enthält statt Name und Adresse nur die Spalte kunden_id mit dem Wert 7. Ändert sich der Kundenname, wird er genau einmal in kunden geändert, und alle Bestellungen sehen automatisch den neuen Wert. Die Datenbank prüft dabei die Gültigkeit: Eine Bestellung mit einer kunden_id, die es gar nicht gibt, wird mit einem Fehler abgewiesen.

So prüft man es: Nachschauen, ob jede Tabelle einen Primärschlüssel hat und ob zusammengehörige Daten über Fremdschlüssel verbunden sind. Kopierte Kundendaten in jeder Bestellzeile sind ein Alarmzeichen.

Der Fehler heißt nicht schlampige Pflege, sondern fehlende Schlüssel. Wer eindeutig kennzeichnet und nur verweist, speichert jede Information genau einmal.

📌 Primärschlüssel · Fremdschlüssel · Verweis statt Kopie

📚 Webentwicklung von Grund auf · Tag 5 von 30
#PostgreSQL #backend #webdev
Lena Peters
23 Std vor
Die meisten Einsteiger speichern Daten wie in einem Tabellenblatt: überallhin geschrieben, jede Zeile anders. Dabei gewinnt eine Datenbank gerade dadurch, dass sie ein festes Gerüst vorgibt.

Nur mit festen Spalten kann die Datenbank Daten prüfen, vergleichen und verbinden. Ohne dieses Gerüst wären Verweise zwischen Datensätzen blind.

🌐 Eine relationale Datenbank wie PostgreSQL legt Daten in Tabellen aus Zeilen und Spalten ab. Jede Zeile ist genau ein Datensatz, etwa ein Kunde mit Name und E-Mail-Adresse. Jede Spalte ist ein Feld dieses Datensatzes, mit einem festen Namen und Datentyp wie Text oder Zahl.

🧩 Ein Primärschlüssel identifiziert jede Zeile eindeutig, etwa über eine Spalte id. Ein Fremdschlüssel verweist auf den Primärschlüssel einer anderen Tabelle und verbindet zusammengehörige Daten. So hängt eine Tabelle bestellungen mit der Spalte kunde_id an kunden.id: Jede Bestellung gehört zu genau einem Kunden.

🔒 Die Spalten gelten für alle Zeilen und werden vorab festgelegt. Eine neue Zeile fügt Daten hinzu, nie eine neue Spalte – das Gerüst ist starr und dadurch verlässlich. Weil jedes Feld Namen und Typ hat, kann die Datenbank Eingaben prüfen, Werte vergleichen und Verbindungen sicher auflösen.

Das Ganze nennt man Datenbankschema: die Beschreibung aller Tabellen und Spalten, die vor den Daten entsteht. Tabellen sind dabei keine Tabellenkalkulation: Es gibt keine bedeutsame Zeilenreihenfolge, Verweise laufen immer über Schlüssel. Das Grundgerüst heißt nicht flexibel, sondern vorhersehbar – und genau das macht Ordnung erst möglich.

📌 Tabelle · Zeile und Spalte · Schlüssel

📚 Webentwicklung von Grund auf · Tag 5 von 30
#PostgreSQL #backend #webdev
Lena Peters
1 Tag vor
TypeScript kennt den Typ einer Variable oft besser, als die Deklaration vermuten lässt. Nach einer einzigen Prüfung im Code verschwinden Fehler, die vorher rot unterstrichen waren.

Einsteiger begegnen dem bei Union-Typen, also Typangaben wie string | number, die mehrere Typen erlauben. Wer sie nicht kennt, wundert sich, warum der Zugriff auf length verboten ist. Der Compiler weiß ja nicht, welcher der beiden Typen gerade drinsteckt.

Das Einengen dieses Typs heißt Narrowing: Der Compiler verengt die Typangabe durch Prüfungen, die der Code ohnehin ausführt. Er analysiert den Kontrollfluss; nach einer bestandenen Prüfung gilt im Block nur der eingegrenzte Typ, im else-Zweig der Rest.

Typisch ist der typeof-Operator, der zur Laufzeit eine Zeichenkette wie "string" liefert. Beispiel: eine Funktion laenge mit Parameter value vom Typ string | number. Sie prüft zuerst mit if, ob typeof value den Wert "string" ergibt; nur dann liefert sie value.length, sonst value.toString().length. Ohne die Prüfung wäre der Zugriff auf length ein Fehler, weil number diese Eigenschaft nicht hat. instanceof prüft, ob ein Objekt von einer Klasse abstammt, und gibt deren Methoden frei. Literalvergleiche wie if (value === "admin") grenzen auf genau diesen Wert ein, besonders bei diskriminierten Unions mit einem Feld wie kind.

Narrowing wird gern mit Validierung verwechselt, also der echten Prüfung ankommender Daten. JSON.parse gibt den Typ any zurück; eine Behauptung wie as User unterdrückt nur die Compilerwarnung. Typen existieren nur zur Übersetzungszeit, im erzeugten JavaScript ist nichts davon übrig.

Narrowing ist keine Prüfung, sondern ein Schluss aus Prüfungen. Es ersetzt die Validierung externer Daten nicht, es setzt sie voraus.

📌 typeof · instanceof · Kontrollfluss

📚 Webentwicklung von Grund auf · Tag 5 von 30
#typescript #javascript #webdev
Lena Peters
1 Tag vor
TypeScript verspricht Sicherheit, doch nach dem Übersetzen ist davon nichts mehr da. Wer das nicht weiß, vertraut Daten, die nie geprüft wurden.

TypeScript übersetzt den Code nach JavaScript, und dabei verschwinden alle Typangaben. Der Vorgang heißt Kompilieren, also das Übersetzen des Quelltexts in ausführbaren Code, und er läuft bei jedem Build.

Zuerst schreibt man Typen wie const user: User in den Code. Eine Typangabe ist eine Zusicherung an den Compiler, also das Übersetzungsprogramm: Sie sagt „vertrau mir“, statt einen Wert zu prüfen. Der Compiler sieht nur die Deklaration, nie die echten Daten.

Dann kommt Daten von außen: von einer Web-API über das Netzwerk, aus einer Datei oder von Nutzern. JSON ist dabei das verbreitete Format, mit Schlüssel-Wert-Paaren, Listen, Zeichenketten, Zahlen sowie true, false und null. Zur Laufzeit, also während das Programm läuft, weiß der Code nichts mehr von Typen.

Danach verwandelt JSON.parse die ankommende Zeichenkette in ein Objekt. In TypeScript gibt die Methode den Typ any zurück, und any schaltet die Typprüfung für diesen Wert ab. Schreibt man as User dazu, unterdrückt das nur eine Compilerwarnung: Die Daten bleiben ungeschützt.

Zuletzt prüft man selbst, bevor man einen Typ vertraut: Ist der Wert ein Objekt, existieren die erwarteten Felder, passen ihre Typen? Ein Typtest wie typeof x === "string" sichert das Ergebnis ab. Besser noch: eine kleine Prüffunktion, die unknown entgegennimmt und erst nach bestandener Prüfung einen konkreten Typ zurückgibt.

Eine Typangabe heißt nicht „geprüft“, sondern „behauptet“. Was zur Laufzeit ankommt, muss zur Laufzeit geprüft werden.

📌 Typen zur Übersetzungszeit · JSON.parse gibt any · unknown plus Prüffunktion

📚 Webentwicklung von Grund auf · Tag 5 von 30
#typescript #javascript #webdev
Lena Peters
1 Tag vor
JavaScript läuft im Browser – und TypeScript landet dort ebenfalls, aber nur als übersetztes Ergebnis. Der Unterschied liegt darin, was vor dem Start geprüft wird.

Die Verwechslung ist naheliegend: TypeScript sieht fast aus wie JavaScript und erzeugt am Ende auch genau das. Doch TypeScript ist eine Sprache mit statischen Typen, das heißt, der Compiler prüft schon vor dem Start, welche Art von Wert in jeder Variable steckt. JavaScript dagegen entscheidet Typen erst zur Laufzeit, also während das Programm läuft.

JavaScript ist die Skriptsprache, die das Verhalten einer Seite im Browser steuert. Der Browser führt den Code unmittelbar aus, ohne dass vorher ein Compiler die Typen prüft. Ein Beispiel: „5“ + 5 ergibt den String „55“, weil die Zeichenkette den Typ der Verknüpfung bestimmt. Der Fehler fällt erst beim Nutzer auf.

TypeScript ist JavaScript, erweitert um Typangaben wie string für Zeichenketten, number für Zahlen und boolean für Wahrheitswerte. Der Compiler prüft alle Typen und erzeugt daraus normales JavaScript. Eine Signatur wie function alter(pruefe: string): number sagt beim Lesen, was hineingehört und was herauskommt; ein Interface namens Nutzer mit den Feldern name und alter meldet fehlende Felder vor dem Start.

TypeScript wirft die Typangaben beim Übersetzen vollständig weg. In der ausgegebenen Datei stehen keine Typen mehr, und der Browser führt gewöhnliches JavaScript aus.

Faustregel: In TypeScript entwickeln, um Fehler früh im Editor zu sehen – aber nie erwarten, dass der Browser dadurch geschützt wird. Typen sind ein Werkzeug der Entwicklungszeit, kein Schutz zur Laufzeit.

TypeScript macht also nicht den Code im Browser sicherer, sondern fängt Typfehler vor dem Start ab, statt dass sie beim Nutzer auftreten. Wer das trennt, nutzt beide Sprachen richtig.

📌 Statische Typen · Compiler · Interface

📚 Webentwicklung von Grund auf · Tag 5 von 30
#typescript #javascript #webdev
Lena Peters
1 Tag vor
npm funktioniert wie eine Einkaufsliste, die der Lieferdienst selbst abruft. Dabei schreibt niemand die Bausteine selbst, die das Projekt braucht.

Ein Bauherr plant ein Haus und notiert: sechs Ziegelsteine, eine Tür, vier Wasserhähne. Er legt keine echten Ziegel auf den Tisch, nur die Liste. Der Lieferdienst liest sie und liefert passende Teile ans Grundstück. Das fertige Lager auf dem Grundstück entsteht aus der Liste.

Node.js ist die Laufzeitumgebung, die JavaScript außerhalb des Browsers ausführt. Die Liste ist package.json, das zentrale Dokument jedes Node-Projekts. npm, der Paketmanager von Node.js, liest diese Datei und lädt aus dem zentralen Paketarchiv, der Registry, die nötigen Bausteine herunter. Sie landen im Ordner node_modules, der alle installierten Pakete samt deren eigenen Abhängigkeiten enthält.

Eine Abhängigkeit (Dependency) ist ein fremdes Programmpaket, das der eigene Code zum Laufen braucht, etwa Express für HTTP-Anfragen. In package.json steht unter dependencies der Eintrag "express": "^4.18.0". Das Caret vor der Zahl erlaubt jede 4er-Version ab 4.18.0; die drei Ziffern 1.2.3 bedeuten major.minor.patch – brechende Änderung, neue Funktion, Fehlerbehebung.

Das Bild hat eine Grenze: Eine Einkaufsliste nennt Zutaten meist exakt, "^4.18.0" ist ein erlaubter Bereich. Ohne die Sperrdatei package-lock.json kann npm auf einem Rechner 1.2.3 wählen, auf einem anderen 1.9.0. Die Sperrdatei friert die tatsächlich gewählten Versionen ein.

Deshalb gilt: package.json und package-lock.json gehören in die Versionsverwaltung (Git), node_modules nicht. Die Liste reicht, um überall dieselben Bausteine neu zu bestellen.

📌 Abhängigkeit · Versionsbereich · Sperrdatei

📚 Webentwicklung von Grund auf · Tag 4 von 30
#NodeJS #javascript #webdev
Lena Peters
2 Tage vor
Der Code hängt an einer await-Zeile – oder scheint es zu tun. Dabei wartet dort nur eine einzige Funktion, nicht das ganze Programm.

Symptom kennen viele aus ihren ersten Webprojekten: Eine Variable enthält nach einem Serveraufruf nichts Brauchbares. Stattdessen steckt darin ein Objekt namens Promise, obwohl man doch die Daten erwartet hatte.

Hinter dem Symptom steht ein Grundbegriff: das Promise, ein Behälter für ein Ergebnis, das erst später eintrifft. JavaScript wartet nicht gern: Läuft eine Anfrage an einen Server, gibt der Aufruf sofort das Promise zurück – noch ohne Inhalt. Die Event-Loop, also die zentrale Verwaltung, die Aufgaben nacheinander abarbeitet, läuft währenddessen munter weiter. Würde der Code wirklich anhalten und warten, stünde die gesamte Anwendung still. Das Promise ist der Trick, beides zu vermeiden: Aufrufer bekommen sofort etwas in der Hand, die Daten folgen später.

Konkret: In einer mit async markierten Funktion, die immer ein Promise zurückgibt, schreibt man const antwort = await fetch("/artikel/42"); fetch ist der Browser-Befehl für eine HTTP-Anfrage. Erst nach Ankunft der Serverantwort liefert await das Antwortobjekt mit den Daten. Wichtig: await pausiert allein die umgebende Funktion; andere Aufgaben, etwa weitere eingehende HTTP-Anfragen, laufen weiter. async und await sind also keine Paralleltechnik, sondern eine lesbare Schreibweise für Promise-Ketten.

So prüft man es: Nach dem Aufruf den Inhalt der Variablen ausgeben lassen. Steht dort ein Promise statt der Daten, fehlt await. Und die await-Zeile in try und catch packen, damit eine Ablehnung des Promise, also ein fehlgeschlagenes Ergebnis, nicht als unbehandelter Fehler durchgeht.

await heißt nicht „Programm anhalten“, sondern „nur diese Funktion pausieren, bis das Promise ein Ergebnis liefert“.

📌 Promise · await · Event-Loop

📚 Webentwicklung von Grund auf · Tag 4 von 30
#javascript #typescript #webdev
Lena Peters
2 Tage vor
Die meisten denken, ein Server brauche für jede Anfrage einen eigenen Thread. Dabei kommt Node.js mit einem einzigen Hauptthread aus und steht trotzdem nie still.

Verantwortlich dafür ist die Event-Loop, eine laufende Schleife im Hauptthread. Sie prüft, ob abgeschlossene Hintergrundvorgänge Ergebnisse geliefert haben, und ruft dann die zugehörigen Funktionen auf.

🌐 Eine gestartete Lese- oder Netzwerkoperation gibt den Hauptthread sofort frei. Das nennt man asynchron: Das Programm wartet nicht, sondern arbeitet weiter und kommt später zurück, wenn das Ergebnis bereitliegt. Ein Callback ist eine Funktion, die als Argument mitgegeben und beim Eintritt des Ereignisses aufgerufen wird. Bei await verbirgt der Compiler genau dieses Muster hinter einer scheinbar linearen Schreibweise.

🧩 „Asynchron“ heißt nicht „parallel“. Es gibt nur einen Hauptthread, der nacheinander arbeitet. Liest ein Server eine Datei mit fs.readFile, kann er währenddessen weitere HTTP-Anfragen annehmen; mit fs.readFileSync stehen alle anderen Anfragen still. Der Fortschritt entsteht durch Weiterarbeiten während des Wartens, nicht durch gleichzeitige Ausführung.

🔒 Eine blockierte Event-Loop ist kein Leistungsproblem, sondern ein Stillstand. Auch JSON.parse und JSON.stringify sind synchron; sehr große Dokumente halten die Schleife für die Dauer der Verarbeitung an. Dann steht jede Anfrage still, weil schlicht nichts mehr ausgeführt wird. Node.js empfiehlt daher, CPU-intensive Arbeit in Worker Threads auszulagern, eigene Threads mit eigenem Event-Loop und Nachrichtenaustausch.

Asynchron heißt nicht „schneller rechnen“, sondern „weiterarbeiten, während gewartet wird“.

📌 Event-Loop · Asynchron · Hauptthread

📚 Webentwicklung von Grund auf · Tag 4 von 30
#NodeJS #javascript #webdev
Lena Peters
2 Tage vor
Viele halten ein JWT (JSON Web Token) für einen versiegelten Umschlag. Dabei sind Header und Payload für jeden lesbar, der das Token besitzt.

Einem JWT begegnet man als Header der Form Authorization: Bearer, gefolgt vom Token, sobald eine API nach einem Login verlangt. Wer nicht weiß, was darin steht, legt unbemerkt Geheimnisse in den Payload offen.

Ein JWT ist ein Zugriffstoken aus drei Teilen, die mit Punkten verbunden sind: Header, Payload und Signatur. Alle drei sind Base64url-kodiert, also in druckbare Zeichen umgewandelt – das ist eine Kodierung, keine Verschlüsselung, und lässt sich ohne Schlüssel sofort zurückübersetzen.

Der Header ist ein JSON-Objekt mit Metadaten, vor allem dem Signaturverfahren im Feld alg. Die Payload ist ebenfalls JSON und enthält Claims, also Angaben wie Nutzerkennung oder Ablaufzeitpunkt. Der Claim exp gibt den Ablaufzeitpunkt als Unix-Zeit an, iat die Ausstellungszeit. Das Token an den Punkten trennen und die ersten beiden Teile zurückübersetzen: Nutzerkennung und Rollen stehen im Klartext. Die Signatur entsteht, weil der Aussteller Header und Payload mit einem nur ihm bekannten Schlüssel signiert. Ändert jemand die Payload, passt die Signatur nicht mehr – der Prüfer verwirft das Token.

Verwechselt wird das mit Verschlüsselung: Die Signatur macht kein Geheimnis aus dem Inhalt, sie belegt nur, dass genau dieser Aussteller genau diesen Inhalt erzeugt hat. Der Prüfer gibt nach RFC 8725 den Algorithmus fest vor und lehnt none ab.

Ein JWT ist kein Briefumschlag, sondern eine unterschriebene Postkarte: Inhalt offen lesbar, Absender beglaubigt. Deshalb gehören Passwörter und Geheimnisse nie in die Payload.

📌 Base64url · Claims · Signatur

📚 Webentwicklung von Grund auf · Tag 4 von 30
#WebSecurity #api #backend #webdev
Lena Peters
2 Tage vor
Viele Einsteiger halten JavaScript für eine Browsersprache. Dabei läuft dieselbe Sprache auch auf Servern und Build-Werkzeugen.

Die Annahme klingt plausibel: JavaScript entstand für Webseiten, und dort sieht man es auch. Wer Buttons, Formulare und Animationen kennt, verbindet die Sprache fest mit Browser und Frontend.

Tatsächlich ist der Browser nur eine Laufzeitumgebung – also das Programm, das die Sprache überhaupt ausführen kann. Es enthält die Sprach-Engine und Bibliotheken, über die der Code sein Betriebssystem erreicht. Die Sprache selbst definiert die ECMAScript-Norm, und jede Umgebung legt fest, was sie darf. Node.js ist eine solche Umgebung und führt JavaScript außerhalb des Browsers aus, etwa auf einem Server. Sie ergänzt die Sprache um Module für Dateizugriffe und um eines, das einen HTTP-Server startet, der Anfragen entgegennimmt und antwortet.

Der Irrtum entsteht, weil Einsteiger JavaScript fast ausschließlich im Frontend erleben. Dort laufen die Sprache und der Browser scheinbar immer gemeinsam auf.

Ein kleines Skript zeigt den Unterschied: Unter Node.js liest es eine Datei ein und zählt Wörter. Genau dieses JavaScript hat vorher vielleicht einen Button im Browser verarbeitet. Wird daraus ein Server, beantwortet er etwa die Anfrage GET /artikel/42 mit einem Datensatz im JSON-Format.

Woran man es erkennt: Läuft Code ohne geöffnetes Browserfenster, ist er nicht im Frontend, sondern im Backend unterwegs. Frontend und Backend sind keine Ordner, sondern Lauforte derselben Sprache.

JavaScript heißt nicht „die Browsersprache“, sondern eine Sprache mit wechselnden Lauforten. Wer Werkzeuge wie npm nutzt, fährt bereits Node.js, ohne es zu bemerken.

📌 Laufzeitumgebung · Node.js · Frontend und Backend als Lauforte

📚 Webentwicklung von Grund auf · Tag 4 von 30
#javascript #NodeJS #webdev
Lena Peters
2 Tage vor
Wer die Eintrittskarte in der Hand hat, kommt rein. Genau so prüft ein Server ein Bearer-Token – und das hat Folgen.

Erklärt wird hier, wie eine authentifizierte Anfrage über das Netz läuft. Die Situation kennt jeder, der einmal eine API oder eine App mit Login benutzt hat.

Zuerst sendet der Client beim Einloggen seine Zugangsdaten, etwa E-Mail und Passwort, an den Server. Der prüft sie und stellt im Erfolgsfall ein Zugriffstoken aus, also eine Art digitale Eintrittskarte. HTTP selbst ist zustandslos: Jede Anfrage steht für sich, und der Server erkennt einen Nutzer nur wieder, wenn Daten wie ein Token mitgesendet werden.

Dann schickt der Client das Token bei jeder weiteren Anfrage im Authorization-Header mit, nach dem Schema „Authorization: Bearer“ gefolgt vom Tokenwert. Bearer ist Englisch für Inhaber. Der Server prüft nur, ob ein gültiges Token vorliegt – nicht, wer die Anfrage eigentlich stellt.

Danach entscheidet der Server allein auf Basis dieses Headers. Wer das Token besitzt, darf damit arbeiten, wie ein Gast mit Garderobenmarke. Ein solches Token ist meist zeitlich begrenzt; anders als ein Passwort läuft es also bald ab.

Zuletzt kommt die Übertragung: Sie muss über TLS laufen, also über die verschlüsselte Variante von HTTP namens HTTPS. Nur dann ist der Header vor dem Mitlesen geschützt. Über offenes HTTP kann jeder im Netzpfad, etwa in einem offenen WLAN, das Token abfangen und anschließend selbst verwenden. Der Besitz allein reicht ja aus.

Bearer heißt nicht „berechtigte Person“, sondern „Inhaber“. Ohne HTTPS trägt jede Anfrage das Token offen im Klartext mit.

📌 Bearer-Token · Authorization-Header · TLS

📚 Webentwicklung von Grund auf · Tag 4 von 30
#HTTP #WebSecurity #webdev #api
Lena Peters
2 Tage vor
Authentifizierung klärt, wer jemand ist. Autorisierung klärt, was diese Person darf.

Beide laufen direkt hintereinander ab und landen oft in einem Topf namens „Login“. Dabei können sie unabhängig scheitern – und der Server antwortet dann mit unterschiedlichen Fehlercodes.

Bei der Authentifizierung gleicht der Server eine Angabe wie Passwort oder Token (ein Ausweis-Datenstück aus Buchstaben und Zahlen) mit seinen gespeicherten Daten ab. Daraus ermittelt er eine Identität, etwa die Nutzer-ID 42. Das ist der erste Schritt: Ohne feststehende Identität kann keine Rechteprüfung folgen.

Bei der Autorisierung prüft der Server für jede einzelne Anfrage, ob diese Identität die angefragte Aktion ausführen darf. Konkret: Nutzer 42 meldet sich mit Passwort an. Beim Aufruf von DELETE /artikel/7 prüft der Server zusätzlich, ob 42 die Rolle Redakteur hat. Ohne diese Rolle folgt der Statuscode 403.

Scheitert die Authentifizierung, antwortet der Server mit 401 (nicht authentifiziert). Scheitert nur die Autorisierung, folgt 403 (verboten) – die Identität ist bekannt, aber die Rechte fehlen.

Weil HTTP zustandslos ist – jede Anfrage steht für sich –, muss jede Anfrage die Identität erneut belegen, etwa durch ein Cookie, ein Name-Wert-Paar, das der Server setzt und der Browser zurücksendet, mit einer Sitzungs-ID als Verweis auf die serverseitig gespeicherte Anmeldung. Der Server authentifiziert also bei jeder Anfrage von Neuem. Und weil der Browser-Seite nicht vertraut werden kann, gehört jede dieser Prüfregeln zusätzlich ins Backend.

Faustregel: Erst die Identität feststellen, dann je Anfrage die Rechte prüfen – beide Prüfungen niemals nur im Browser.

Authentifizierung heißt nicht „eingeloggt und alles erlaubt“, sondern nur „erkannt“. Was jemand darf, entscheidet die Autorisierung – separat und bei jedem Zugriff neu.

📌 Identität · Rechte · 401 vs. 403

📚 Webentwicklung von Grund auf · Tag 4 von 30
#WebSecurity #backend #HTTP
Lena Peters
2 Tage vor
Ein Bauplan beschreibt das Haus, bevor ein Stein gelegt wird. Eine API-Beschreibung funktioniert wie so ein Bauplan: Sie legt zuerst fest, was gebaut wird.

„API-Beschreibung“ heißt hier: eine Beschreibung der Schnittstelle, über die Programme Daten austauschen. Der Bauherr schaut auf den Plan und weiß: Hier ist die Tür, dort ein Fenster. Erst danach beginnt das Handwerkerteam. Bei Software verhält es sich genauso. Alle Beteiligten lesen dieselbe Zeichnung und stimmen sie ab, bevor irgendein Code entsteht.

Fachlich ist dieser Plan die OpenAPI-Beschreibung, eine maschinenlesbare Beschreibung in YAML, einem Textformat mit Einrückungen, oder JSON. Statt Räume stehen darin Pfade wie /bestellungen; statt Türen stehen Operationen wie GET oder DELETE. Statt Maße beschreibt ein Schema jeden Wert, etwa dass eine Bestellnummer eine Zeichenkette im Format einer UUID sein muss. Ergänzt werden Sicherheitschemata, die festlegen, wie sich ein Client beim Server ausweisen darf.

Konkret: Der Pfad /bestellungen/42 wird mit DELETE gelöscht, /bestellungen mit GET gelesen, nicht als /deleteUser. Wer Design-first arbeitet, entwirft diese Beschreibung zuerst und lässt Server-Gerüst, Client und Request-Validierung daraus erzeugen. Der Server prüft dann jede eingehende Anfrage gegen das Schema.

„Das Bild hat eine Grenze:“ Ein Bauplan ist nach dem Bau oft eingemottet, eine API-Beschreibung bleibt lebendig. Wird sie nicht gepflegt, lügt sie, und der erzeugte Code folgt der Wahrheit nicht mehr.

Schnittstelle zuerst beschreiben heißt: Der Plan ist der Vertrag, nicht der Zufall. Code, der ihm entspringt, folgt ihm.

📌 Design-first · maschinenlesbar · Vertrag

📚 Webentwicklung von Grund auf · Tag 4 von 30
#api #REST #webdev
Lena Peters
2 Tage vor
Die Antwort der API kommt zurück, aber das Frontend zeigt nichts an. Der Grund ist oft ein einziges falsches Komma im JSON.

Das Symptom: Der Browser meldet einen Fehler beim Auslesen der Daten. Die Anfrage selbst war erfolgreich; nur der Text der Antwort lässt sich nicht in ein Objekt umwandeln.

Dahinter steckt JSON, das JavaScript Object Notation, ein reines Textformat und der verbreitete Standard für Daten von Web-APIs. JSON kennt Objekte mit Schlüssel-Wert-Paaren, Listen, Zeichenketten, Zahlen sowie true, false und null. Der Browser fragt etwa per GET /artikel/42 einen Datensatz an, und der Server antwortet im Body, dem Teil der Antwort mit den eigentlichen Nutzdaten. Ein Header wie Content-Type: application/json kündigt dabei das Format an.

Ein typischer Fehler: einfache statt doppelter Anführungszeichen oder ein Komma nach dem letzten Paar. Beides macht das JSON ungültig, und der Empfänger verweigert das Lesen. Korrekt verschachtelt kombiniert eine Antwort beides: ein Objekt mit Metadaten wie der Gesamtzahl 2 sowie eine Liste in eckigen Klammern mit zwei Artikeln, den Nummern 42 und 43 und den Titeln „Neu“ und „Alt“ – Objekte und Listen lassen sich beliebig ineinander verschachteln.

So prüft man es: Die Antwort in einen JSON-Validator kopieren, der ungültige Stellen markiert. In JavaScript prüft JSON.parse den Text und meldet die Stelle, an der etwas fehlt; JSON.stringify wandelt ein Objekt wieder in Text um.

Wer null sieht, sollte zudem wissen: Das heißt, ein Wert fehlt bewusst – anders als die Zahl 0 oder eine leere Zeichenkette, bei denen ein Wert vorhanden ist.

JSON heißt nicht „irgendein Textformat“, sondern Objekte, Listen und Werte in exakter Schreibweise – doppelte Anführungszeichen, kein Schlusskomma.

📌 JSON-Objekt · Listen · null statt 0

📚 Webentwicklung von Grund auf · Tag 3 von 30
#webdev #api #javascript #frontend
Lena Peters
3 Tage vor
Die meisten Einsteiger bauen Aktionen in den Pfad ein: /getOrders, /deleteUser. Dabei sagt der Pfad nur, um welches Ding es geht – die Aktion steckt in der HTTP-Methode.

Eine saubere REST-Schnittstelle braucht weniger Adressen und ist leichter zu lesen. Wer Ressourcen und Methoden trennt, kann jede Adresse mit vier Methoden nutzen.

🌐 Eine REST-Schnittstelle benennt Dinge, keine Tätigkeiten. REST ist ein Stil, Web-Schnittstellen über HTTP zu bauen, das Übertragungsprotokoll des Webs. Eine Ressource ist dabei ein benanntes Ding: ein Kunde, eine Bestellung oder die Liste aller Bestellungen. Eine HTTP-Anfrage besteht aus Methode, Ziel, Headern und optionalem Body; der Pfad im Ziel adressiert die Ressource, etwa /bestellungen.

🧩 Der Pfad benennt Substantive, die Methode wirkt darauf. GET /bestellungen liest die Liste, GET /bestellungen/42 liest genau eine Bestellung, POST /bestellungen legt eine neue an, DELETE /bestellungen/42 löscht sie. Dieselbe Adresse kann je nach Methode Verschiedenes bedeuten: Der Pfad /bestellungen/42 bleibt gleich, nur die Wirkung ändert sich. PUT /bestellungen/42 ersetzt dagegen die Daten dieser Bestellung.

🔒 Verben im Pfad wie /deleteUser verwirren, weil sie die Ordnung brechen. Der Server müsste für jede Aktion eine eigene Adresse verwalten, und GET /deleteUser würde beim bloßen Aufruf löschen. Mit Substantiv und Methode gelten vier klare Verben für alle Ressourcen; der Server antwortet mit Statuscode und meist JSON, dem Datenformat mit Schlüssel-Wert-Paaren.

REST heißt nicht „Befehle in URLs schreiben“, sondern „Dinge benennen und mit Methoden darauf wirken“.

📌 Ressourcen · Substantive im Pfad · HTTP-Methoden

📚 Webentwicklung von Grund auf · Tag 3 von 30
#REST #api #HTTP #webdev
Lena Peters
3 Tage vor
Viele setzen Cache-Control: no-cache, wenn sie „nicht speichern“ meinen. Genau dann speichert der Browser die Antwort trotzdem.

Ein Cache ist eine gespeicherte Kopie einer HTTP-Antwort, die wiederverwendet wird, statt sie jedes Mal neu vom Server zu laden. Ob und wie lange eine gespeicherte Kopie wiederverwendet werden darf, steuert der Server über den Header Cache-Control in der Antwort selbst. Wer no-cache falsch versteht, verschickt versehentlich vertrauliche Daten in Caches – oder wundert sich, dass die Rückfrage trotzdem kommt.

no-cache heißt nicht „nicht speichern“, sondern „vor jeder Wiederverwendung beim Server validieren“. Die Antwort darf also im Cache liegen. Bevor der Browser sie erneut anzeigt, muss er aber beim Server nachfragen, ob sie sich verändert hat.

Konkret läuft diese Nachfrage als bedingter Request: Der Browser sendet einen gespeicherten ETag – einen vom Server vergebenen Prüfwert für eine Darstellung – im Header If-None-Match mit. Ist die Ressource unverändert, antwortet der Server nur mit 304 Not Modified und leerem Body. Der Browser nutzt dann seine gespeicherte Kopie weiter. Eine HTML-Seite mit no-cache und ETag bleibt so immer aktuell, während nur wenige Bytes über die Leitung gehen. Wer dagegen aktuelle Daten ohne Rückfrage zulassen will, setzt max-age=N: Dann darf der Cache die Antwort N Sekunden lang ausliefern, ohne zu fragen.

Verwechselt wird no-cache vor allem mit no-store. Letzteres verbietet jede Speicherung und gehört zu persönlichen Daten wie Bankauszügen oder Sitzungsdaten. no-cache hält eine Seite dagegen aktuell, ohne sie jedes Mal komplett neu zu laden – ein Geschwindigkeitsgewinn, kein Nachteil.

no-cache ist nicht „nicht speichern“, sondern „speichern, aber vorher fragen“. Wer nichts gespeichert haben will, schreibt no-store.

📌 Cache-Control · no-cache · no-store

📚 Webentwicklung von Grund auf · Tag 3 von 30
#webdev #HTTP #backend #performance
Lena Peters
3 Tage vor
Viele denken, der Browser hole jede Seite komplett neu vom Server. Dabei ist das zweite Laden einer Seite oft viel schneller – und das liegt selten am schnellen Internet, sondern am Zwischenspeicher.

Viele Einsteiger glauben, der Browser hole jedes Bild und jede Seite komplett neu vom Server. Das klingt plausibel, weil man ja jedes Mal eine Adresse aufruft. Auch verwechselt man den Cache, also den Zwischenspeicher, gern mit dem Cookie, der einen Nutzer bei künftigen Anfragen identifiziert.

Tatsächlich ist ein Cache eine gespeicherte Kopie einer HTTP-Antwort, die wiederverwendet wird, statt sie jedes Mal neu zu laden. Speichern können mehrere Stellen zwischen Browser und Server: der Browser selbst, ein Proxy als Vermittler im Netz und ein CDN, ein Netz aus Servern näher beim Nutzer. Ob und wie lange gespeichert werden darf, steuert der Server über den Header Cache-Control – etwa mit max-age, das die Sekunden ohne Rückfrage festlegt.

Der Irrtum entsteht, weil das Speichern unsichtbar im Hintergrund passiert. Man sieht nur, dass die Seite dargestellt wird, nicht woher sie kommt.

Ein konkretes Beispiel: Beim ersten Laden einer Seite muss jede Ressource vom Ursprungsserver geholt werden. Beim zweiten Besuch sendet der Browser das ETag in If-None-Match zurück; ist die Darstellung unverändert, antwortet der Server mit 304 Not Modified ohne Body – die Seite lädt spürbar schneller.

Woran man es erkennt: In der Netzwerk-Ansicht der Browser-Entwicklerwerkzeuge zeigen manche Einträge statt 200 OK einen 304 Not Modified. Der Server bestätigt damit nur: Die gespeicherte Kopie ist noch gültig.

Cache heißt nicht „Speicher für Nutzerdaten“, sondern „Vorrat an Antworten“. Wer Anfragen ersparen will, steuert das mit Cache-Control – nicht mit Cookies.

📌 Cache-Control · Zwischenspeicher · 304 Not Modified

📚 Webentwicklung von Grund auf · Tag 3 von 30
#HTTP #webdev #performance
Lena Peters
3 Tage vor
JavaScript kann Bilder von fremden Servern laden, aber nicht deren pixelgenaue Daten auslesen. Dieser Unterschied ist die Same-Origin-Policy – und sie erklärt, warum fetch-Aufrufe scheitern können, obwohl die Anfrage beim Server ankam.

Der Browser vergleicht bei jeder Antwort, ob sie von derselben Origin (der Kombination aus Schema, Host und Port) stammt wie die Seite, deren JavaScript sie lesen will. Das begegnet Einsteigern sofort, sobald Frontend und Backend getrennt laufen.

Zuerst lädt der Browser die Seite, etwa eine gewöhnliche Dokumentseite unter https. JavaScript auf dieser Seite ruft dann Daten ab, zum Beispiel von einer API unter einem anderen Host. Die Anfrage geht ganz normal raus; der Server antwortet, ohne zu prüfen, wer sie liest.

Dann zählt der Browser Schema, Host und Port, nicht den Pfad: Zwei Adressen wie /seite1 und /api/daten auf demselben Host mit demselben Port teilen sich eine Origin. Aber ein API-Host und ein www-Host derselben Domain sind verschiedene Hosts, und derselbe Host mit abweichendem Port wie 8443 ist eine andere Origin. Ein fehlender Port bedeutet den Standardport, also 443 bei https.

Danach hält der Browser die Antwort zurück, wenn die Origins verschieden sind. Der Browser schickt die Anfrage, gibt die Antwort aber nicht an JavaScript weiter, solange der Server sie nicht per CORS (einem Header-Mechanismus zur Freigabe) erlaubt. Bilder und Stylesheets fremder Origins erscheinen trotzdem, weil kein Skript ihren Inhalt liest.

Zuletzt gilt die Grenze nur im Browser. curl (ein Kommandozeilenwerkzeug für HTTP-Anfragen) sendet und liest ohne Sandbox alles; die Same-Origin-Policy schützt den Server nicht, nur die Seite im Browser.

Gleiche Origin heißt also nicht gleicher Host, und fremde Origin heißt nicht blockierte Anfrage – sondern unlesbare Antwort.

📌 Origin · Same-Origin-Policy · CORS

📚 Webentwicklung von Grund auf · Tag 3 von 30
#WebSecurity #webdev #javascript
Lena Peters
3 Tage vor
Die meisten denken, eine Webadresse sei ein einziger unteilbarer String. Dabei vergleicht der Browser nur drei Bestandteile, um Herkünfte zu unterscheiden.

Die Verwechslung ist naheliegend: URL und Origin sehen fast gleich aus, und die Origin ist der vordere Teil der URL. Wer beides gleichsetzt, wundert sich später, warum eine Anfrage blockiert wird.

Die URL (Uniform Resource Locator, eine komplette Webadresse) besteht aus Schema, Host, optionalem Port, Pfad, optionaler Query nach dem Fragezeichen und optionalem Fragment nach der Raute. Sie benennt eine konkrete Ressource wie eine Seite oder ein Bild. Schema https, Pfad /orders und Query id=7 zeigen vier dieser Bestandteile in einer Adresse.

Die Origin besteht nur aus Schema, Host und Port; der Rest zählt nicht mit. Der Browser vergleicht zwei Adressen, indem er genau diese drei Teile gegenüberstellt; eine Abweichung ergibt eine andere Origin. Zwei Adressen mit gleichem Host sind darum verschieden, sobald der Port abweicht (Standardport gegenüber 8443) oder das Schema (http gegenüber https).

Pfad, Query und Fragment dürfen beliebig abweichen, ohne die Origin zu ändern. Ein fehlender Port bedeutet zudem den Standardport des Schemas – bei https der 443, bei http der 80; eine Adresse mit und ohne diese 443 hat dieselbe Origin.

Faustregel: Beim Aufrufen einer Ressource die volle URL angeben, beim Prüfen von Herkunft und Zugriff nur Schema, Host und Port vergleichen. Auch Unterdomänen wie api und www desselben Namens sind verschiedene Hosts und damit verschiedene Origins; die Same-Origin-Policy, eine Browser-Regel, gibt JavaScript nur Antworten derselben Origin frei, fremde Origins brauchen eine CORS-Freigabe des Servers.

Die Origin heißt nicht Adresse, sondern Herkunft: Schema, Host und Port – und sonst nichts.

📌 Origin · Same-Origin-Policy · URL-Bestandteile

📚 Webentwicklung von Grund auf · Tag 3 von 30
#webdev #WebSecurity #HTTP
Lena Peters
3 Tage vor
HTTPS funktioniert wie ein Geldtransporter mit beglaubigtem Fahrer. Der Wagen schützt die Ladung auf der Fahrt, und der Dienstausweis belegt, dass der Fahrer wirklich beim Unternehmen angestellt ist.

Der Transporter fährt durch offenes Gelände. Beobachter sehen den Wagen, erkennen aber keinen Inhalt. Wird eine Kiste unterwegs aufgebrochen, fällt der Eingriff sofort auf.

Technisch ist HTTPS HTTP über TLS, also das normale Web-Protokoll über eine verschlüsselte Verbindung. Der TLS-Handshake, die Verbindungsaufnahme, einigt sich auf einen Schlüssel und prüft das Zertifikat: ein digitaler Ausweis, den eine Zertifizierungsstelle als anerkannte Vertrauensinstanz signiert hat. Der Browser vergleicht den Domainnamen im Zertifikat mit dem Host der URL, also dem Domainnamen in der Adresse; danach laufen alle Anfragen und Antworten als Chiffretext, also unlesbarer Geheimtext.

Beim Aufruf ist das Schema der URL der Teil vor dem Doppelpunkt: Es sagt dem Client, welches Protokoll er nutzt. Aus http wird damit https: verschlüsselte Übertragung mit Geheimhaltung, statt Klartext. Das Schloss im Browser bedeutet also nicht „sicherer Server“, sondern belegt nur zwei Eigenschaften des Wegs: verschlüsselte Übertragung und geprüfte Identität.

Das Bild hat eine Grenze: Der Transporter schützt weder das Lager noch die Ware nach der Abladung. Nach dem Entschlüsseln liegen die Daten auf dem Server im Klartext vor; Angriffe im Backend liegen außerhalb der Zuständigkeit der Verbindung.

HTTPS heißt nicht „dieser Server ist gut“, sondern „dieser Weg ist verschlüsselt, und dieser Ausweis passt zur Domain“.

📌 TLS-Verschlüsselung · Zertifikat · Schloss-Symbol

📚 Webentwicklung von Grund auf · Tag 3 von 30
#WebSecurity #HTTP #webdev
Lena Peters
3 Tage vor
Das Login ist nach dem Neustart des Browsers weg – und niemand weiß, warum. Einsteigern passiert das oft mit dem allerersten Cookie.

Nach dem Schließen und Wiederöffnen des Browsers ist der Nutzer abgemeldet. Beim Klick durch die Seiten ist dagegen alles normal.

Ein Cookie ist ein Name-Wert-Paar, das der Server mit dem Header Set-Cookie setzt und das der Browser bei passenden Anfragen im Header Cookie zurücksendet. Header sind Zeilen, die bei HTTP Daten zwischen Server und Browser austauschen; HTTP selbst ist zustandslos, jede Anfrage steht für sich. Der Server erkennt Nutzer also nur anhand mitgesendeter Werte wie Cookies, etwa als Schlüssel in seiner Session-Tabelle. Ohne Expires und Max-Age entsteht ein Sitzungscookie: Der Browser verwirft es beim Schließen, kann die Sitzung aber wiederherstellen. Mit Expires, einem konkreten Ablaufdatum, oder Max-Age, einer Lebensdauer in Sekunden, entsteht ein dauerhaftes Cookie, das Neustarts überlebt; ist beides gesetzt, gewinnt Max-Age.

Set-Cookie: sessionid=abc123; Path=/; HttpOnly setzt ein Sitzungscookie, das beim Beenden des Browsers verschwindet. Fügt man Max-Age=3600 an, bleibt es eine Stunde lang bestehen. Der Nutzer bleibt dann auch nach einem Neustart angemeldet – bis die Frist abläuft.

So prüft man es: Im Browser-DevTools unter Application die Cookies der Seite ansehen und die Spalten Expires und Max-Age prüfen. Sitzungscookies zeigen dort „Session“ statt eines Datums.

Sitzungscookie meint hier nur die Speicherung im Browser, nicht eine Server-Session; auch ein dauerhaftes Cookie kann einen Sitzungsschlüssel tragen, den der Server jederzeit für ungültig erklärt. Expires und Max-Age sind also die Schalter für die Lebensdauer, nicht für den Inhalt.

📌 Sitzungscookie · Max-Age · dauerhaftes Cookie

📚 Webentwicklung von Grund auf · Tag 3 von 30
#webdev #HTTP #WebSecurity #backend
Lena Peters
3 Tage vor
Die meisten Anfänger denken, der Server müsse das Cookie abfragen oder der Nutzer müsse es eintippen. Dabei übernimmt der Browser den Versand des Cookies, eines kleinen Name-Wert-Paars, vollständig automatisch.

HTTP, das Protokoll hinter Webseiten, ist zustandslos – jede Anfrage steht für sich. Ohne zurückgesendete Daten wüsste der Server nicht, wer fragt. Cookies füllen genau diese Lücke.

🌐 Ein Cookie ist ein Name-Wert-Paar, das der Server setzt. In der Antwort steht etwa: Set-Cookie: sessionid=abc123; Path=/; HttpOnly. Vor dem Semikolon stehen Name und Wert, die Attribute dahinter steuern Speicherung und Versand. Path=/ bedeutet: zu jedem Pfad dieses Hosts mitsenden.

🧩 Der Browser schickt das Cookie automatisch mit, ohne eigenes Programm. Passt die angefragte Adresse zum Cookie, fügt der Browser den Header Cookie: sessionid=abc123 in die Anfrage ein. Die Webanwendung muss nichts dafür tun. Derselbe Datensatz heißt in der Antwort Set-Cookie, in der Anfrage Cookie – zwei Header, ein Inhalt. Nachvollziehen lässt sich das in den Entwicklerwerkzeugen: im Netzwerk-Tab bei einer Antwort den Set-Cookie-Header suchen und bei der nächsten Anfrage an denselben Host den Cookie-Header mit demselben Wert vergleichen.

🔒 HttpOnly schützt den Cookie vor JavaScript im Browser. JavaScript kann ihn dann nicht auslesen, und ein XSS-Angriff (böses Skript auf der Seite) stiehlt ihn nicht direkt. Ohne Expires oder Max-Age gilt das Cookie nur bis zum Schließen des Browsers. Das nennt man Sitzungscookie; der Browser kann die Sitzung aber wiederherstellen.

Ein Cookie heißt nicht Abfrage durch den Server, sondern automatischer Rückversand durch den Browser. Der Server erkennt Nutzer wieder, indem er den Wert etwa in seiner Session-Tabelle nachschlägt.

📌 Name-Wert-Paar · Set-Cookie · automatischer Rückversand

📚 Webentwicklung von Grund auf · Tag 2 von 30
#webdev #HTTP #backend
Lena Peters
4 Tage vor
Der Browser zeigt den Login an, doch der Server kennt den Nutzer nicht. Dabei ist das kein Fehler: HTTP vergisst absichtlich.

Wer das erste Mal eine Anmeldung baut, stolpert fast immer hier. Wer nicht weiß, dass HTTP zustandslos ist, wundert sich, warum der Nutzer nach einem Klick wieder ausgeloggt ist.

Zustandslos heißt: Jede HTTP-Anfrage (die Nachricht, die der Browser an den Server schickt) steht für sich allein. Sie besteht aus Methode, Ziel, Headern und optionalem Body. Das Protokoll kennt keine Verbindung zwischen zwei Anfragen. Aus Sicht des Servers ist jede Anfrage die erste. Zustandslos heißt also nicht, dass der Server kein Gedächtnis haben darf. Nur das Protokoll selbst verknüpft nichts.

Die Erinnerung liefert der Client mit: eine Kennung, die bei jeder Anfrage mitgeschickt wird. Nach dem Login antwortet der Server etwa mit dem Header Set-Cookie: session=abc123. Der Browser speichert dieses Name-Wert-Paar und sendet es bei passenden Anfragen im Header Cookie: session=abc123 zurück. Der Server findet dazu die Sitzung mit dem Login-Zustand. Fehlt die Kennung, etwa weil Cookies gelöscht wurden, gilt die nächste Anfrage als die eines unbekannten Besuchers. Eine Alternative zum Cookie ist ein Token, eine Zeichenkette, die der Client beim Aufruf einer API selbst in einen Header setzt. Auch sie wirkt nur, weil sie jedes Mal erneut mitgesendet wird.

Verwechselt wird Zustandslosigkeit gern mit fehlender Authentifizierung, der Frage, wer jemand ist. Beides ist unabhängig: Auch nach erfolgreichem Login bleibt jede Anfrage für sich. Die Kennung muss trotzdem überall mitgeschickt werden.

HTTP ist nicht vergesslich, sondern absichtlich gedächtnislos. Die Sitzung entsteht erst durch die Kennung, die jede Anfrage mitbringt.

📌 Zustandslos · Cookie · Session-ID

📚 Webentwicklung von Grund auf · Tag 2 von 30
#webdev #HTTP #backend
Lena Peters
4 Tage vor
Wer eine Bestellung per GET auslöst, riskiert Doppelkäufe ohne jede Absicht. Dabei sagt der Standard für HTTP längst, welche Methode was darf.

Viele Einsteiger halten GET und POST für zwei gleichwertige Türen: Hauptsache, die Anfrage kommt beim Server an. Das klingt plausibel, weil beide Daten transportieren können.

Tatsächlich unterscheidet RFC 9110, die aktuelle HTTP-Spezifikation, die Methoden nach zwei Eigenschaften. Sicher heißt: Die Methode verändert den Zustand des Servers nicht; GET, HEAD, OPTIONS und TRACE sind sicher. Idempotent heißt: Mehrere identische Anfragen wirken wie eine einzige; das gilt für PUT, DELETE und alle sicheren Methoden. POST und PATCH fallen heraus: Ein wiederholter POST kann eine zweite Ressource anlegen, etwa eine doppelte Bestellung.

Der Irrtum entsteht, weil HTML-Formulare nur GET und POST anbieten. Wer keine anderen Methoden kennt, wählt nach Syntax statt nach Bedeutung.

Eine Bestellung per GET zu senden endet im Browser-Cache: GET-Anfragen dürfen vorabgeladen oder zwischengespeichert werden; das kann unbeabsichtigte Ausführungen auslösen. Korrekt ist POST für die Erzeugung; wer POST wiederholbar machen will, sendet zusätzlich einen Idempotenz-Schlüssel. Das ist ein eindeutiger Wert, etwa eine UUID, im Request-Header; der Server speichert Schlüssel und Antwort und liefert bei Wiederholung dieselbe Antwort zurück.

Woran man es erkennt: Verändert die Anfrage etwas auf dem Server, reicht GET nie aus. Regel: Lesen mit GET, Erzeugen mit POST, vollständiges Ersetzen mit PUT.

Sicher heißt nicht wirkungslos, sondern nebenwirkungsfrei. Idempotent heißt nicht wirkungslos, sondern ohne Doppelwirkung.

📌 Sichere Methoden · Idempotenz · Idempotenz-Schlüssel

📚 Webentwicklung von Grund auf · Tag 2 von 30
#HTTP #REST #webdev #backend