Programmierung

Lektion I: Das Ziel (Befehle an die Maschine)

Lassen Sie sich auf eine kleine Einführung in die Welt der Programmierung ein. Keine Sorge, es wird nicht zu abstrakt oder gar zu schwierig, wenn sie einen Fahrkartenautomaten bedienen können, eine Waschmaschine oder gar ein Handy, dann haben sie die ersten Grundbegriffe bereits verstanden.

Eine Programmiersprache ist eine Aneinanderreihung von Anweisungen, die Schritt für Schritt ausgeführt werden. Während Sie an einem Automaten alle Befehle manuell der Reihe nach eingeben müssen, wird ein Programm automatisch ausgeführt. Das ist der Hauptunterschied.

In unserem Tutorial führen wir Sie sanft in die wichtigsten Arten von Befehlen ein, wir gehen dabei besonders auf Aspekte der Sicherheit ein und Sie können unten im Eingabebereich des Editors das Gelernte selbst ausführen und anpassen. Der Einfachheit wegen (und weil man sich nun mal für einen Einstieg entscheiden muss) haben wir uns hier für die Programmiersprache Python entschieden, vom Grundprinzip her ähneln sich die Sprachen aber sehr, so dass sie ihr erlerntes Wissen auch schnell in anderen Umgebungen einsetzen können.

Lektion II: Der digitale Sandkasten (Sandbox)

Natürlich brauchen wir bei einem Tutorial einen Bereich, in dem wir das Erlernte ausprobieren können. Und natürlich darf dieser Bereich nicht kompliziert zu installieren sein. Und sicher sollte er auch sein. Gerade wenn man sich am Anfang noch etwas unsicher ist, möchte man kein Risiko eingehen, evtl. ein System zu blockieren, etwas zu löschen oder zu beschädigen.

Mit anderen Worten, wir wollen eine Spielwiese auf der wir uns unbekümmert austoben können. Für genau solch einen Raum hat sich unter Programmierern der Begriff Sandbox etabliert.

Sicherheits-Prinzip: Alles, was im unteren Editor eingegeben wird, verlässt den lokalen Browser nicht. Das heisst, nach einem Refresh ist alles weg und es wird auch nichts ins Internet bzw. an die Server von Medias in Res übertragen. Somit kann kein System beschädigt werden!

Lektion III: Hallo Welt!

Beginnen wir mit dem klassischen Einstieg, mit dem heute alle (namhaften ;-) ) Programmierkurse beginnen.

Exkurs: Erstmalig is die Verwendung des Ausdrucks "Hello, World!" 1967 in der Dokumentation der Programmiersprache BCPL (Basic Combined Programming Language) belegt. Die eigentliche Tradition, Tutorials so beginnen zu lassen, beginnt allerdings erst später.
1973 verwendet Brian Kernighan die Worte "hello" und "world" in der Einführung zur Programmiersprache B bei den Bell Laboratories und ein Jahr später schrieb er als Beteiligter bei der Entwicklung der Sprache C ein "Hello World!"-Programm um die Funktionsweise der neuen Programmiersprache zu demonstrieren.
Der Durchbruch kam mit dem Buch "The C Programming Language", das er gemeinsam mit Dennis Ritchie 1978 veröffentlichte.

Was also 1967 als kleines Textbeispiel in einer Sprachdokumentation begann, ist mittlerweile zu einem Ritual geworden, mit dem bis heute fast jeder Programmierer seine Karriere beginnt.

Beginnen wir also in dieser Tradition und tippen den Befehl

print("Hallo Welt!")

in das Eingabefenster unserer Sandbox ein und starten wir das Programm mit dem Button "Ausführen".

Wenn wir alles richtig gemacht haben, sollte nun im Ausgabefenster die Begrüßung "Hallo Welt" erscheinen.

Lektion IV: Variablen (Regalfächer für allerhand Kram)

Bringen wir nun ein wenig mehr Dynamik in unser Programm und schreiben wir unsere erste Befehlsabfolge, also zwei Anweisungen, die aufeinander folgen.

Zuerst definieren wir eine Variable. Stellen wir uns Variablen wie Regalfächer vor, in die wir beliebige Inhalte ablegen können und die wir zur leichten Auffindbarkeit mit frei wählbaren Namen kennzeichnen.

Exkurs: Es gibt Programmiersprachen, die erwarten eine genaue Beschreibung eines solchen Regalfachs, also einer Variablen, bevor sie verwendet werden kann. In dem Fall muss gesagt werden, dass eine Variable mit Namen xy angelegt werden soll und dass nur Daten eines bestimmten Formates (meistens Typ oder Datentyp genannt) darin abgelegt werden dürfen. Beispielsweise die Variable Name vom Typ Text oder Geburtstag vom Typ Datum.

In unserem Fall, in Python, ist das einfacher. Wir verwenden Variablen einfach da, wo wir sie brauchen. In diesem Fall schreiben wir einfach name = "Platzhalter" und schon weiß das System, es soll eine Textvariable bereitstellen und der Text "Platzhalter" soll initial dort abgespeichert sein. Dass ein Text zugewiesen wird, erkennt das System an den Anführungszeichen.

Interessant für Mathematiker dürfte hier die Gleichung sein: name = xy ist hier nämlich nicht mathematisch zu verstehen, sondern als Zuweisung. Was rechts neben dem Gleichheitszeichen steht wird links der Variablen zugewiesen. Man könnte so auch eine Variable auf die andere kopieren (text1 = text2) oder das Ergebnis einer Rechnung: ergebnis = 5 + 6. In letzterem Fall stünde in der Variable "ergebnis" eine 11.

Um sich das Ergebnis auch ansehen zu können, müssen wir es im zweiten Schritt ausgeben. Wir fügen also noch den bekannten Befehl zur Ausgabe an, dieses Mal aber kombiniert mit dem Inhalt der Variable "name". Das Ergebnis können Sie sich ansehen, indem sie folgendes Programm abtippen, ändern sie den Platzhalter in der ersten Zeile nach belieben und sehen Sie, wie sich die Ausgabe anpasst.

name = "Platzhalter"
print(f"Hallo {name}!")

Dem genauen Beobachter wird im print-Befehl das f in der Klammer bemerkt haben. Dieses bedeutet "formatiert" und weist darauf hin, dass in den geschweiften Klammern eine Variable zu finden ist. Probieren Sie den Befehl gern auch einmal ohne dieses f aus.

Exkurs Datensicherheit: Es wurde im ersten Exkurs bereits erwähnt, dass es Sprachen gibt, die eine genaue Initialisierung von Variablen mit Typ erwarten. Python (und eine Reihe anderer Sprachen) nehmen dem Programmierer die Arbeit ab und erkennen die Typen selbst.

Das ist einerseits eine Erleichterung, erfordert aber auf der anderen Seite besondere Aufmerksamkeit, damit jemand durch bewusste Eingabe von falschen Werten, z.B. einen Text wo eine Zahl erwartet wird, kein ungewolltes Verhalten provozieren kann.

Lektion V: Konstanten (Fest definierte Werte)

In der vorigen Lektion haben wir Variablen kennengelernt, Regalfächer, deren Inhalt regelmäßig geändert werden kann. Es gibt aber Situationen, in denen wir keine Änderungen wünschen.

Die meisten Programmiersprachen kennen für diesen Zweck Konstanten. Den Wert der Zahl Pi (3.14159...) werden wir sicher nicht ändern wollen, ebenso wenig die Mehrwertsteuer, den Namen unserer Anwendung oder eine feste URL zu einem Server.

Python kennt allerdings keine erzwungenen Konstanten, so dass sich Entwickler mit einer breit akzeptierten Konvention helfen. Variablen, die als Konstante gedacht sind, werden komplett in Großbuchstaben geschrieben; sie können also geändert werden, dürfen es aber nicht, wenn man die Regeln sorgfältiger Programmierung einhalten möchte. Konstanten in Python sehen also wie folgt aus:

MWST = 0.19
MAX_VERSUCHE = 3
APP_NAME = "Medias in Res"


Probieren Sie es aus. Legen Sie eine Konstante für die Mehrwertsteuer an und berechnen Sie damit den Bruttopreis eines Artikels:

MWST = 0.19
nettopreis = 100
bruttopreis = nettopreis * (1 + MWST)
print(f"Bruttopreis: {bruttopreis} Euro")

Exkurs Datensicherheit: Gerade bei sicherheitskritischen Werten (maximale Anzahl von Anmeldeversuchen, Timeouts, ...) ist die Konstanten-Konvention nicht nur Kosmetik. Sie signalisiert: Dieser Wert wurde bewusst so festgelegt und darf im laufenden Betrieb nicht verändert werden.
Viele Sicherheitslücken entstehen genau da, wenn solche Werte zur Laufzeit überschrieben werden.

Lektion VI: Rechenoperationen – Das Rechenwerk

Jeder Computer, so komplex er auch erscheinen mag, führt im Kern einfache Rechenoperationen aus, Millionen davon pro Sekunde. Auch die Programme, die wir schreiben, kommen selten ohne Arithmetik aus. Python beherrscht dazu selbstverständlich die vier Grundrechenarten, bietet darüber hinaus aber noch einige weitere nützliche Werkzeuge.

Die Grundoperatoren sind schnell erklärt:

print(10 + 3)    → Addition: ergibt 13
print(10 - 3)    → Subtraktion: ergibt 7
print(10 * 3)    → Multiplikation: ergibt 30
print(10 / 3)    → Division: ergibt 3.333...
print(10 // 3)   → Ganzzahldivision: ergibt 3
print(10 % 3)    → Modulo (Rest): ergibt 1
print(10 ** 3)   → Potenz: ergibt 1000

Besonders die letzten drei Zeilen sind interessant. Die Ganzzahldivision (//) schneidet alle Nachkommastellen ab, was nützlich ist, wenn man z.B. wissen möchte, wie viele vollständige Stunden in 200 Minuten stecken.
Der Modulo-Operator (%) liefert den Rest einer Division: So lässt sich mit ihm u.a. prüfen, ob eine Zahl gerade oder ungerade ist.
Die Potenz (**) erspart uns verschachtelte Multiplikationen.

Geben Sie folgendes Programm in die Sandbox ein und spielen Sie mit den Werten:

minuten = 200
stunden = minuten // 60
rest = minuten % 60
print(f"{minuten} Minuten sind {stunden} Stunden und {rest} Minuten.")

Exkurs Datensicherheit: Rechenoperationen klingen harmlos, sind es aber nicht immer. Ein klassisches Angriffsmuster ist der sogenannte Integer Overflow: Wenn eine Zahl größer wird, als der zugeteilte Speicher fassen kann, springt sie auf einen sehr kleinen (oder negativen) Wert zurück. In sicherheitskritischen Systemen, etwa bei der Berechnung von Speichergrößen oder Zugriffsgrenzen, kann das zu gefährlichen Lücken führen. Python schützt uns hier weitgehend, da es Ganzzahlen automatisch unbegrenzt groß werden lässt. In Sprachen wie C oder Java ist das anders: Dort ist Vorsicht geboten.

Ein historisches Beispiel macht Gefahren, wie sie durch einen Integer Overflow geschehen können, besonders deutlich:
Der Absturz der Ariane-5-Rakete nur 40 Sekunden nach dem Start verursachte Kosten von rund 370 Millionen Dollar. Die Software versuchte, einen riesigen Geschwindigkeitswert aus einem präzisen 64-Bit-Fließkomma-Register in eine zu kleines mit nur 16-Bit zu pressen. Der daraus resultierende Overflow legte die Navigationssysteme lahm, woraufhin die Rakete aufgrund aerodynamischer Kräfte auseinanderbrach und gesprengt werden musste.

Lektion VII: Bedingungen (If/Else)

Bisher liefen unsere Programme wie auf einer geraden Schiene, Befehl für Befehl, von oben nach unten durch. In vielen Fällen benötigt man jedoch Entscheidungen, die ähnlich wie bei Weichen in einem Schienennetz den Zug je nach Kontext in eine andere Richtung lenken. In der Programmierung wird diese Aufgabe durch die If-Anweisung umgesetzt.

Die Grundstruktur ist denkbar einfach:

alter = 17
if alter >= 18:
print("Zutritt gewährt.")
else:
print("Zutritt verweigert.")

Beachten Sie hier besonders die Einrückung: In Python gehört alles, was nach dem Doppelpunkt eingerückt ist, als Block zum jeweiligen Zweig. Das ist keine Frage des Stils, sondern der Syntax: Falsches Einrücken erzeugt Fehler!

Wollen wir mehr als zwei Möglichkeiten abbilden, kommt elif (kurz für „else if") ins Spiel:

punkte = 74
if punkte >= 90:
print("Ausgezeichnet.")
elif punkte >= 60:
print("Bestanden.")
else:
print("Nicht bestanden.")

Ändern Sie den Wert von punkte und beobachten Sie, wie das Programm reagiert.

Exkurs Datensicherheit: Bedingungen entscheiden, ob bestimmte Teile eines Programms ausgeführt werden und unter welchen Bedingungen. Validierungen von Benutzereingaben und viele andere Kontrollen basieren auf If-Anweisungen. Entsprechend gefährlich können Fehler an genau dieser Stelle sein.

Historisch ist hier vor allem der Vorfall mit dem Therac-25 zu erwähnen. Dabei handelte es sich um einen Linearbeschleuniger, der seinen Dienst zwischen 1982 und 1987 in Kliniken der USA und Kanada zur Strahlentherapie eingesetzt wurde. Ein Softwarefehler, eine fehlerhafte Abfrage von schnellen Tastatureingaben, stellte systemintern eine "Weiche" zwischen harmloser Teststrahlung und hochenergetischer Elektronenstrahlung nicht rechtzeitig um. Der Programmfluss wurde in den Freigabe-Zweig geleitet, obwohl die mechanische Schutzvorrichtung noch nicht in Position war. Dies führte zu massiven Überdosierungen, wodurch mehrere Patienten ihr Leben verloren oder schwer verletzt wurden.

Lektion VIII: Listen (Arrays)

Bisher haben unsere Variablen immer genau einen Wert gespeichert. Doch was, wenn wir eine ganze Sammlung verwalten möchten? Eine Liste von Benutzernamen, eine Reihe von Messwerten, die Stationen einer Route...
Für diesen Zweck gibt es in Python Listen.

Eine Liste wird mit eckigen Klammern angelegt, die einzelnen Werte werden durch Kommata getrennt:

werkzeuge = ["Hammer", "Schraubenzieher", "Zange"] print(werkzeuge[0])  # → gibt "Hammer" aus print(werkzeuge[2])  # → gibt "Zange" aus
Beachten Sie: Die Zählung beginnt bei 0, nicht bei 1. Das ist eine äußerst wichtige Konvention und anfangs oft Quelle von Verwirrung.

Oft weiß man am Anfang eines Programms noch gar nicht, welche Daten man später in der Liste benötigt. In diesem Fall erstellt man eine leere Liste, um sie später, beispielsweise mit einer Schleife (Lektion X) zu befüllen:

meine_liste = []  # → Eine leere Liste wird geschmiedet meine_liste.append("Erster Eintrag")
Listen sind veränderbar. Wir können Elemente hinzufügen, entfernen oder ersetzen:

werkzeuge.append("Säge")        # → fügt hinten an werkzeuge.remove("Hammer")      # → entfernt einen Eintrag werkzeuge[0] = "Bohrmaschine"   # → ersetzt den ersten Eintrag print(len(werkzeuge))           # → gibt die aktuelle Länge der Liste aus
Probieren Sie, eine eigene Liste anzulegen und diese schrittweise zu verändern.

Exkurs Datensicherheit: Die Zählung ab 0 klingt wie ein akademisches Detail, ist es aber nicht. Sogenannte Off-by-one-Fehler (auf Deutsch etwa: "um eins daneben") gehören zu den häufigsten Programmierfehlern überhaupt und haben in der Vergangenheit zu ernsthaften Sicherheitslücken geführt. Greift ein Programm auf einen Bereich zu, der außerhalb der Liste liegt, kann das im besten Fall einen Absturz, im schlimmsten Fall das Auslesen von fremdem Speicher verursachen. Python wirft in solchen Fällen einen kontrollierten Fehler – andere Sprachen sind da weniger gnädig.

Listen können nicht nur Werte sequentiell nebeneinander platzieren, es können auch wie in einem Regal oder einer Tabelle Spalten und Zeilen vorkommen. In der Programmierung nennt man solche Strukturen mehrdimensionale Listen, also eine Liste, die selbst wieder Listen enthält.

Sehen wir uns das am Beispiel eines Stundenplans an:

stundenplan = [
["Montag", "Mathematik", "Geschichte"],
["Dienstag", "Physik", "Latein"],
["Mittwoch", "Informatik", "Sport"]
]

Jede innere Liste ist eine Zeile, jeder Eintrag darin eine Spalte. Der Zugriff erfolgt mit zwei Indizes: Der erste wählt die Zeile, der zweite die Spalte. Beide beginnen wieder bei 0:

print(stundenplan[0][0])  # → "Montag" print(stundenplan[1][2])  # → "Latein" print(stundenplan[2][1])  # → "Informatik"
Man liest die Indizes am besten von außen nach innen: stundenplan[2] ist zunächst die dritte Zeile (Mittwoch), und [1] davon ist der zweite Eintrag dieser Zeile (Informatik).

Geben Sie den Stundenplan in die Sandbox ein und versuchen Sie, einen eigenen hinzuzufügen; etwa eine vierte Zeile für Donnerstag. Oder ersetzen Sie die Schulfächer durch etwas anderes: Städte und ihre Sehenswürdigkeiten, Zutaten und ihre Mengen, Spieler und ihre Punkte.

Exkurs: Es sind natürlich nicht nur zweidimensionale Arrays denkbar, sondern beliebig tief verschachtelte. Bei der dritten Dimension würde man einen Würfel erhalten, bei noch weiteren Dimensionen wird es mit der Vorstellung zwar schwieriger, mathematisch (und somit auch in Bezug auf die Umsetzung im Programm) gibt es hier jedoch keine Grenzen.

Lektion IX: Dictionaries (Konstruktionspläne)

Eine Liste speichert Werte sequentiell und es kann über die Position auf den Inhalt zugegriffen werden. Möchte man ganz unterschiedliche Daten zu einem Namen finden, ohne dass die Position von entscheidender Bedeutung ist, nutzt man besser Objekte. In Python werden solche Sammlungen von Daten Dictionaries genannt: Wörterbücher, die jedem Wert einen eindeutigen Schlüssel zuweisen.

benutzer = { "vorname": "Nixus", "nachname": "Minimax", "alter": 36 } print(benutzer["vorname"]) # → gibt "Nixus" aus
Die geschweifte Klammer kennzeichnet das Dictionary. Jedes Paar aus Schlüssel und Wert wird durch einen Doppelpunkt verbunden, die Paare durch Kommata getrennt.

Werte lassen sich gezielt abrufen, ändern und ergänzen:

benutzer["familienstand"] = "ledig" # → neues Feld anlegen benutzer["alter"] = 37 # → Wert überschreiben print(benutzer.get("schuhgroesse", "unbekannt")) # → sicherer Abruf
Der .get()-Aufruf in der letzten Zeile ist besonders empfehlenswert: Er gibt einen Standardwert zurück, wenn der Schlüssel nicht existiert, anstatt das Programm mit einem Fehler abstürzen zu lassen.

Exkurs Datensicherheit: Dictionaries werden in der Praxis häufig dazu genutzt, Benutzerdaten zu speichern und weiterzuverarbeiten. Dabei ist Vorsicht geboten: Enthält ein Dictionary Daten aus einer externen Quelle, etwa eine Formulareingabe, sollte man niemals blind auf Schlüssel zugreifen, die möglicherweise gar nicht vorhanden sind. Der direkte Zugriff mit benutzer["rolle"] würde einen fatalen Fehler (KeyError) werfen, wenn "rolle" fehlt. Ein Angreifer könnte gezielt Felder weglassen, um das System zu sabotieren. Der .get()-Operator mit Standardwert ist daher nicht nur komfortabel, sondern ein kleiner, aber wichtiger Sicherheitsbaustein.

Lektion X: Schleifen – Endlose Zahnräder

Ein Zahnrad dreht sich, greift ins nächste, das dreht sich weiter – immer wieder, so lange die Maschine läuft. Programme kennen ein ähnliches Prinzip: die Schleife. Sie wiederholt einen Codeblock so oft, wie es die Bedingung verlangt, und erspart so das ermüdende Abtippen immer gleicher Befehlsabfolgen.

Die for-Schleife läuft durch eine Liste oder einen Bereich und führt zu jedem Eintrag den eingerückten Block aus:

for zahl in range(1, 6): print(f"Schritt {zahl}")
range(1, 6) erzeugt die Zahlenfolge 1 bis 5 (die obere Grenze ist, wie bei Listenindizes, ausgeschlossen). Wir können auch direkt über eine Liste iterieren:

staedte = ["Berlin", "Wien", "Zürich"] for stadt in staedte: print(f"Nächster Halt: {stadt}")

Exkurs: In Lektion VIII haben wir uns mit Arrays beschäftigt, auch mehrdimensionalen. Hier bietet es sich an, sich die Arbeit mit Schleifen enorm zu vereinfachen:
Nehmen wir den Stundenplan aus der obigen Lektion und versuchen wir, mit einer verschachtelten for-Schleife alle Einträge der Reihe nach zu durchlaufen:

for tag in stundenplan: for fach in tag: print(fach, end=" ") print() # Zeilenumbruch nach jeder Tageszeile

Die while-Schleife hingegen läuft so lange, wie eine Bedingung wahr ist. Das ist nützlich, wenn wir nicht im Voraus wissen, wie viele Durchläufe nötig sind:

versuche = 0 while versuche < 3: print(f"Versuch {versuche + 1}") versuche = versuche + 1

Exkurs Datensicherheit: Die while-Schleife birgt eine tückische Gefahr: die Endlosschleife. Vergisst man, die Bedingung irgendwann unwahr werden zu lassen, dreht sich das Zahnrad für immer. Das Programm hängt fest, der Server antwortet nicht mehr oder bricht irgendwann mit einem Speicherüberlauf ab. Angreifer nutzen dieses Prinzip bewusst aus: Ein sogenannter Denial-of-Service-Angriff (DoS) kann darin bestehen, ein System so mit manipulierten Anfragen zu überfluten, dass es in einer erzwungenen Endlosschleife feststeckt und für echte Nutzer nicht mehr erreichbar ist. Schleifen sollten daher immer eine garantiert erreichbare Abbruchbedingung besitzen.

Lektion XI: Funktionen – Kompakte Maschinenbauteile

Stellen Sie sich vor, Sie bauen eine Maschine und benötigen an zwölf verschiedenen Stellen das gleiche Bauteil. Stellen Sie sich weiter vor, Sie bräuchten es nicht zwölf Mal einzeln zu fertigen, sondern könnten es einmal konstruieren, benennen und überall dort einsetzen, wo es gebraucht wird. Genau dieses Prinzip steckt hinter Funktionen.

Eine Funktion wird einmal definiert und kann dann beliebig oft aufgerufen werden. Das vermeidet Wiederholungen, macht den Code lesbarer und erleichtert spätere Korrekturen erheblich. Eine Änderung an einer Stelle wirkt sich so gleichzeitig auf alle Aufrufe aus.

def begruessung(name): print(f"Willkommen, {name}!")
begruessung("Ada") begruessung("Charles")
Das Schlüsselwort def leitet die Definition ein. In den Klammern stehen die Parameter – Platzhalter für Werte, die beim Aufruf übergeben werden. Funktionen können auch einen Wert zurückgeben:

def quadrat(zahl): return zahl * zahl
ergebnis = quadrat(7) print(ergebnis) # → gibt 49 aus
Mit return gibt die Funktion ein Ergebnis zurück, das wir im Hauptprogramm weiterverarbeiten können. Probieren Sie es aus und schreiben Sie in der Sandbox eine eigene Funktion, die den Bruttopreis aus Lektion V berechnet!

Exkurs Datensicherheit: Funktionen sind nicht nur ein Werkzeug der Ordnung, sondern auch der Sicherheit. Das Prinzip dahinter heißt Kapselung (Encapsulation): Eine Funktion erledigt genau eine isolierte Aufgabe und hat standardmäßig keinen Zugriff auf Variablen außerhalb ihres Bereichs, die sie nichts angehen. Je klarer die Grenzen einer Funktion gezogen sind, desto schwieriger ist es für Angreifer, unbeabsichtigtes Verhalten im Gesamtsystem auszulösen. Funktionen, die zu viele Aufgaben gleichzeitig übernehmen oder unkontrolliert globale Variablen verändern, bilden oft gefährliche Schwachstellen.

Lektion XII: Das Sicherheitskonzept – Schutz vor Sabotage

Wir sind am Ende unseres Tutorials angelangt. Werfen wir nun noch einmal einen allgemeinen Blick auf das Thema Sicherheit, das uns in allen Lektionen begleitet hat. Sicherheit ist kein nachträgliches Konzept, kein Schloss, das man im letzten Moment ans Tor hängt. Sie ist das Fundament, auf dem jede gute Maschine errichtet wird.

Werfen wir einen Blick zurück und sehen wir, was wir bereits gelernt haben:

Variablen und Typen (Lektion IV): Unkontrollierte Eingaben sind das Einfallstor Nummer eins. Wer nicht prüft, was in eine Variable geschrieben wird, riskiert, dass ein Angreifer Daten einschleust, die das Programm in unvorhergesehene Bahnen lenken.

Konstanten (Lektion V): Sicherheitskritische Grenzwerte gehören in Konstanten – niemand sollte sie im laufenden Betrieb stillschweigend überschreiben können.

Bedingungen (Lektion VII): Jede Zugangskontrolle, jede Berechtigungsprüfung ist im Kern eine If-Anweisung. Ein logischer Fehler hier öffnet Türen, die geschlossen bleiben sollten.

Listen und Off-by-one (Lektion VIII): Ein Index zu weit, und das Programm liest oder schreibt Daten, die ihm nicht gehören.

Schleifen und Endlosigkeit (Lektion X): Eine Schleife ohne sicheres Ende kann ein System komplett lahmlegen – ob durch einen Programmierfehler oder einen gezielten Angriff.

Funktionen und Kapselung (Lektion XI): Klare Grenzen schützen das Getriebe. Was eine Funktion nicht sehen kann, kann sie auch nicht manipulieren.

Darüber hinaus gibt es einige universelle Grundsätze, die in jedem Programm gelten sollten:

Prinzip 1 – Minimale Rechte (Least Privilege): Jede Komponente eines Systems sollte nur so viele Rechte besitzen, wie sie für ihre unmittelbare Aufgabe benötigt. Eine Funktion, die eine Datei nur liest, braucht kein Schreibrecht. Ein Benutzer, der Beiträge liest, braucht keinen Administratorzugang.

Prinzip 2 – Niemals Nutzereingaben vertrauen: Alles, was von außen in ein Programm gelangt (Formulareingaben, URL-Parameter, hochgeladene Dateien...), ist grundsätzlich als potenziell feindselig zu betrachten. Es muss vor der Verarbeitung strikt validiert und begrenzt werden.

Prinzip 3 – Fehler sichtbar machen, aber nicht ausplaudern: Ein Programm sollte Fehler intern detailliert protokollieren, nach außen hin aber nur das Nötigste mitteilen. Eine Fehlermeldung auf dem Bildschirm, die den internen Codeaufbau oder Datenbankstrukturen verrät, ist ein wertvolles Geschenk an jeden Angreifer.

Prinzip 4 – Sicherheit ist kein Zustand, sondern ein Prozess: Kein System ist dauerhaft sicher. Neue Angriffsmethoden entstehen, alte Programmbibliotheken werden anfällig, Konfigurationen veralten. Sicherheit erfordert kontinuierliche Wartung und Aufmerksamkeit.

Wir hoffen, dieser Kurs hat Ihnen ein paar neue Einblicke vermitteln können und vor allem auch Freude bereitet. Natürlich freuen wir uns bei Gefallen auch über eine kleine Spende; unsere Bankverbindung finden Sie im Impressum.

Python Sandbox (Pyodide)