Initial commit: IHK Ausbildung materials
This commit is contained in:
42
1-Ausbildungsjahr/LF6-Webanwendungen/LF6-00-Übersicht.md
Normal file
42
1-Ausbildungsjahr/LF6-Webanwendungen/LF6-00-Übersicht.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Lernfeld 6: Webanwendungen entwickeln
|
||||
|
||||
## Übersicht
|
||||
|
||||
Dieses Lernfeld behandelt die Entwicklung von Webanwendungen.
|
||||
|
||||
## Themen
|
||||
|
||||
| Nr. | Thema | Beschreibung |
|
||||
| --- | --- | --- |
|
||||
| 6.1 | [[LF6-01-Web-Grundlagen]] | HTTP, HTML, CSS |
|
||||
| 6.2 | [[LF6-02-Frontend]] | JavaScript, Frameworks |
|
||||
| 6.3 | [[LF6-03-Backend]] | Server, APIs, Datenbanken |
|
||||
| 6.4 | [[LF6-04-Sicherheit-Web]] | XSS, CSRF, SQL Injection |
|
||||
|
||||
## Lernziele
|
||||
|
||||
- Webanwendungen entwickeln
|
||||
- Frontend und Backend verbinden
|
||||
- Sicherheitslücken erkennen und vermeiden
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- LF3: Datenbanken
|
||||
- LF4: IT-Sicherheit
|
||||
- LF5: Analyse und Design
|
||||
|
||||
## Prüfungsrelevanz
|
||||
|
||||
- Teil 2 Abschlussprüfung (praktisch)
|
||||
|
||||
---
|
||||
|
||||
## Querverweise
|
||||
|
||||
- [[LF5-04-Testverfahren|Zurück: Testverfahren]]
|
||||
- [[LF9-Netzwerke-Dienste|Nächstes Lernfeld: Netzwerke]]
|
||||
- [[Wissen/Wirtschafts-Sozialkunde/WISO-Zusammenfassung|WISO: E-Commerce]]
|
||||
|
||||
---
|
||||
|
||||
*Stand: 2024*
|
||||
225
1-Ausbildungsjahr/LF6-Webanwendungen/LF6-01-Web-Grundlagen.md
Normal file
225
1-Ausbildungsjahr/LF6-Webanwendungen/LF6-01-Web-Grundlagen.md
Normal file
@@ -0,0 +1,225 @@
|
||||
# 6.1 Web-Grundlagen
|
||||
|
||||
## Internet und WWW
|
||||
|
||||
### Grundbegriffe
|
||||
|
||||
```
|
||||
Internet - Netzwerk der Netzwerke
|
||||
WWW (World Wide Web) - Dienst im Internet
|
||||
```
|
||||
|
||||
### Funktionsweise
|
||||
|
||||
```
|
||||
Client-Server-Modell
|
||||
┌─────────┐ ┌─────────┐
|
||||
│ Browser │ ───────►│ Server │
|
||||
│ (Client)│ ◄───────│ │
|
||||
└─────────┘ └─────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HTTP - Hypertext Transfer Protocol
|
||||
|
||||
### HTTP-Ablauf
|
||||
|
||||
```
|
||||
HTTP - Kommunikation
|
||||
1. Client sendet Request
|
||||
2. Server verarbeitet
|
||||
3. Server sendet Response
|
||||
```
|
||||
|
||||
### HTTP-Methoden
|
||||
|
||||
| Methode | Beschreibung | idempotent |
|
||||
|---------|-------------|------------|
|
||||
| **GET** | Daten abrufen | Ja |
|
||||
| **POST** | Daten senden | Nein |
|
||||
| **PUT** | Daten ersetzen | Ja |
|
||||
| **PATCH** | Daten teilweise ändern | Nein |
|
||||
| **DELETE** | Daten löschen | Ja |
|
||||
|
||||
### HTTP-Statuscodes
|
||||
|
||||
| Code | Bedeutung | Beispiel |
|
||||
|------|-----------|----------|
|
||||
| **200** | OK | Erfolgreich |
|
||||
| **201** | Created | Erstellt |
|
||||
| **301** | Moved Permanently | Umleitung |
|
||||
| **400** | Bad Request | Fehlerhafte Anfrage |
|
||||
| **401** | Unauthorized | Nicht angemeldet |
|
||||
| **403** | Forbidden | Keine Berechtigung |
|
||||
| **404** | Not Found | Nicht gefunden |
|
||||
| **500** | Internal Server Error | Serverfehler |
|
||||
|
||||
### HTTPS
|
||||
|
||||
```
|
||||
HTTPS = HTTP + TLS-Verschlüsselung
|
||||
|
||||
Vorteile:
|
||||
├── Vertraulichkeit
|
||||
├── Integrität
|
||||
└── Authentifizierung
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HTML - HyperText Markup Language
|
||||
|
||||
### Grundstruktur
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Titel</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Inhalt -->
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### HTML-Elemente
|
||||
|
||||
| Element | Bedeutung |
|
||||
|---------|----------|
|
||||
| `<h1>` bis `<h6>` | Überschriften |
|
||||
| `<p>` | Absatz |
|
||||
| `<a>` | Link |
|
||||
| `<img>` | Bild |
|
||||
| `<ul>`, `<ol>` | Liste |
|
||||
| `<table>` | Tabelle |
|
||||
| `<form>` | Formular |
|
||||
| `<div>`, `<span>` | Container |
|
||||
|
||||
### Semantisches HTML
|
||||
|
||||
```html
|
||||
<header>Kopfbereich</header>
|
||||
<nav>Navigation</nav>
|
||||
<main>
|
||||
<article>
|
||||
<section>Inhalt</section>
|
||||
</article>
|
||||
<aside>Seitenleiste</aside>
|
||||
</main>
|
||||
<footer>Fußbereich</footer>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CSS - Cascading Style Sheets
|
||||
|
||||
### Einbindung
|
||||
|
||||
```html
|
||||
<!-- Extern -->
|
||||
<link rel="stylesheet" href="style.css">
|
||||
|
||||
<!-- Intern -->
|
||||
<style>
|
||||
body { background: white; }
|
||||
</style>
|
||||
|
||||
<!-- Inline -->
|
||||
<p style="color: red;">Text</p>
|
||||
```
|
||||
|
||||
### CSS-Selektoren
|
||||
|
||||
```css
|
||||
/* Element */
|
||||
p { color: blue; }
|
||||
|
||||
/* Klasse */
|
||||
.klasse { font-size: 16px; }
|
||||
|
||||
/* ID */
|
||||
#id { background: gray; }
|
||||
|
||||
/* Attribut */
|
||||
[type="text"] { border: 1px solid; }
|
||||
|
||||
/* Pseudoklasse */
|
||||
:hover { cursor: pointer; }
|
||||
|
||||
/* Nachfahre */
|
||||
div p { margin: 10px; }
|
||||
```
|
||||
|
||||
### Flexbox
|
||||
|
||||
```css
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
```
|
||||
|
||||
### Grid
|
||||
|
||||
```css
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Responsive Design
|
||||
|
||||
### Viewport
|
||||
|
||||
```html
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0">
|
||||
```
|
||||
|
||||
### Media Queries
|
||||
|
||||
```css
|
||||
/* Mobile zuerst */
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
width: 750px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
width: 970px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Breakpoints
|
||||
|
||||
| Gerät | Breite |
|
||||
|-------|--------|
|
||||
| Mobile | < 768px |
|
||||
| Tablet | 768px - 1023px |
|
||||
| Desktop | >= 1024px |
|
||||
|
||||
---
|
||||
|
||||
## Querverweise
|
||||
|
||||
- [[LF6-02-Frontend|Nächstes Thema: Frontend-Entwicklung]]
|
||||
- [[LF4-02-Schutzmassnahmen|IT-Sicherheit: HTTPS]]
|
||||
|
||||
---
|
||||
|
||||
*Stand: 2024*
|
||||
268
1-Ausbildungsjahr/LF6-Webanwendungen/LF6-02-Frontend.md
Normal file
268
1-Ausbildungsjahr/LF6-Webanwendungen/LF6-02-Frontend.md
Normal file
@@ -0,0 +1,268 @@
|
||||
# 6.2 Frontend-Entwicklung
|
||||
|
||||
## JavaScript
|
||||
|
||||
### Grundlagen
|
||||
|
||||
```javascript
|
||||
// Variablen
|
||||
let name = "Max"; // veränderbar
|
||||
const alter = 25; // konstant
|
||||
|
||||
// Datentypen
|
||||
let text = "Hallo"; // String
|
||||
letzahl = 42; // Number
|
||||
let wahr = true; // Boolean
|
||||
let array = [1, 2, 3]; // Array
|
||||
let objekt = { // Object
|
||||
name: "Max",
|
||||
alter: 25
|
||||
};
|
||||
```
|
||||
|
||||
### Kontrollstrukturen
|
||||
|
||||
```javascript
|
||||
// Bedingung
|
||||
if (alter >= 18) {
|
||||
console.log("Volljährig");
|
||||
} else {
|
||||
console.log("Minderjährig");
|
||||
}
|
||||
|
||||
// Switch
|
||||
switch (tag) {
|
||||
case "Mo":
|
||||
case "Di":
|
||||
console.log("Arbeitstag");
|
||||
break;
|
||||
default:
|
||||
console.log("Wochenende");
|
||||
}
|
||||
|
||||
// Schleifen
|
||||
for (let i = 0; i < 5; i++) {
|
||||
console.log(i);
|
||||
}
|
||||
|
||||
array.forEach(item => {
|
||||
console.log(item);
|
||||
});
|
||||
```
|
||||
|
||||
### Funktionen
|
||||
|
||||
```javascript
|
||||
// Funktionsdeklaration
|
||||
function gruss(name) {
|
||||
return "Hallo " + name;
|
||||
}
|
||||
|
||||
// Arrow Function
|
||||
const gruss = (name) => "Hallo " + name;
|
||||
|
||||
// Mit Standardwert
|
||||
function gruss(name = "Gast") {
|
||||
return "Hallo " + name;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DOM - Document Object Model
|
||||
|
||||
### Element auswählen
|
||||
|
||||
```javascript
|
||||
// Nach ID
|
||||
const element = document.getElementById("meine-id");
|
||||
|
||||
// Nach Klasse
|
||||
const elemente = document.getElementsByClassName("klasse");
|
||||
|
||||
// Nach Selektor
|
||||
const element = document.querySelector(".klasse");
|
||||
const elemente = document.querySelectorAll("p");
|
||||
```
|
||||
|
||||
### Inhalt ändern
|
||||
|
||||
```javascript
|
||||
// Text ändern
|
||||
element.textContent = "Neuer Text";
|
||||
|
||||
// HTML ändern
|
||||
element.innerHTML = "<strong>Fett</strong>";
|
||||
|
||||
// Attribute
|
||||
element.setAttribute("class", "neu");
|
||||
element.getAttribute("href");
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
```javascript
|
||||
// Event Listener
|
||||
element.addEventListener("click", function() {
|
||||
console.log("Geklickt!");
|
||||
});
|
||||
|
||||
// Arrow Function
|
||||
element.addEventListener("click", () => {
|
||||
alert("Geklickt!");
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Formulare
|
||||
|
||||
### HTML-Formular
|
||||
|
||||
```html
|
||||
<form id="login-form">
|
||||
<label for="email">E-Mail:</label>
|
||||
<input type="email" id="email" name="email" required>
|
||||
|
||||
<label for="password">Passwort:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
|
||||
<button type="submit">Anmelden</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
### Formulardaten auslesen
|
||||
|
||||
```javascript
|
||||
const formular = document.getElementById("login-form");
|
||||
|
||||
formular.addEventListener("submit", function(e) {
|
||||
e.preventDefault(); // Verhindert Seitenreload
|
||||
|
||||
const formData = new FormData(formular);
|
||||
const daten = Object.fromEntries(formData);
|
||||
|
||||
console.log(daten);
|
||||
// { email: "...", password: "..." }
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frameworks
|
||||
|
||||
### React
|
||||
|
||||
```jsx
|
||||
import React, { useState } from 'react';
|
||||
|
||||
function Counter() {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Zähler: {count}</p>
|
||||
<button onClick={() => setCount(count + 1)}>
|
||||
Erhöhen
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Vue
|
||||
|
||||
```html
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const count = ref(0);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p>Zähler: {{ count }}</p>
|
||||
<button @click="count++">Erhöhen</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Vergleich
|
||||
|
||||
| Framework | Typ |特点 |
|
||||
|-----------|-----|-----|
|
||||
| React | Library | Flexibel, große Community |
|
||||
| Vue | Framework | Einfach zu lernen |
|
||||
| Angular | Framework | Enterprise, TypeScript |
|
||||
|
||||
---
|
||||
|
||||
## Asynchrone Programmierung
|
||||
|
||||
### Promises
|
||||
|
||||
```javascript
|
||||
// Promise erstellen
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
// Async Operation
|
||||
if (erfolg) {
|
||||
resolve("Erfolg!");
|
||||
} else {
|
||||
reject("Fehler!");
|
||||
}
|
||||
});
|
||||
|
||||
// Nutzen
|
||||
promise
|
||||
.then(ergebnis => console.log(ergebnis))
|
||||
.catch(fehler => console.error(fehler));
|
||||
```
|
||||
|
||||
### Async/Await
|
||||
|
||||
```javascript
|
||||
async function datenLaden() {
|
||||
try {
|
||||
const response = await fetch('/api/daten');
|
||||
const daten = await response.json();
|
||||
console.log(daten);
|
||||
} catch (error) {
|
||||
console.error("Fehler:", error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fetch API
|
||||
|
||||
```javascript
|
||||
// GET
|
||||
fetch('/api/benutzer')
|
||||
.then(response => response.json())
|
||||
.then(data => console.log(data));
|
||||
|
||||
// POST
|
||||
fetch('/api/benutzer', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: "Max",
|
||||
email: "max@example.com"
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => console.log(data));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Querverweise
|
||||
|
||||
- [[LF6-01-Web-Grundlagen|Zurück: Web-Grundlagen]]
|
||||
- [[LF6-03-Backend|Nächstes Thema: Backend-Entwicklung]]
|
||||
- [[LF6-04-Sicherheit-Web|Web-Sicherheit]]
|
||||
|
||||
---
|
||||
|
||||
*Stand: 2024*
|
||||
272
1-Ausbildungsjahr/LF6-Webanwendungen/LF6-03-Backend.md
Normal file
272
1-Ausbildungsjahr/LF6-Webanwendungen/LF6-03-Backend.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# 6.3 Backend-Entwicklung
|
||||
|
||||
## Server-Grundlagen
|
||||
|
||||
### Client-Server-Architektur
|
||||
|
||||
```
|
||||
Webanwendung - Architektur
|
||||
┌─────────────┐ HTTP ┌─────────────┐
|
||||
│ Browser │ ◄──────────────►│ Server │
|
||||
│ (Frontend) │ │ (Backend) │
|
||||
└─────────────┘ └──────┬──────┘
|
||||
│
|
||||
┌─────┴─────┐
|
||||
│ Datenbank │
|
||||
└───────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Node.js
|
||||
|
||||
### Grundlagen
|
||||
|
||||
```javascript
|
||||
// Einfacher Server
|
||||
const http = require('http');
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
res.end('<h1>Hallo Welt!</h1>');
|
||||
});
|
||||
|
||||
server.listen(3000, () => {
|
||||
console.log('Server läuft auf Port 3000');
|
||||
});
|
||||
```
|
||||
|
||||
### Express.js
|
||||
|
||||
```javascript
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
// Middleware
|
||||
app.use(express.json());
|
||||
|
||||
// GET-Route
|
||||
app.get('/api/benutzer', (req, res) => {
|
||||
res.json([
|
||||
{ id: 1, name: 'Max' },
|
||||
{ id: 2, name: 'Anna' }
|
||||
]);
|
||||
});
|
||||
|
||||
// POST-Route
|
||||
app.post('/api/benutzer', (req, res) => {
|
||||
const neuerBenutzer = req.body;
|
||||
// Speichern...
|
||||
res.status(201).json(neuerBenutzer);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## REST-API
|
||||
|
||||
### REST-Prinzipien
|
||||
|
||||
```
|
||||
REST - Grundsätze
|
||||
├── Ressourcen-orientiert (Nomen)
|
||||
├── Stateless (keine Session)
|
||||
├── Einheitliche Schnittstelle
|
||||
├── Client-Server-Trennung
|
||||
└── Cache-fähig
|
||||
```
|
||||
|
||||
### HTTP-Methoden
|
||||
|
||||
| Methode | CRUD | Beschreibung |
|
||||
|---------|-----|-------------|
|
||||
| GET | Read | Daten abrufen |
|
||||
| POST | Create | Daten erstellen |
|
||||
| PUT | Update | Daten vollständig ersetzen |
|
||||
| PATCH | Update | Daten teilweise ändern |
|
||||
| DELETE | Delete | Daten löschen |
|
||||
|
||||
### API-Endpunkte
|
||||
|
||||
```
|
||||
Beispiel: Benutzer-Ressource
|
||||
|
||||
GET /api/benutzer → Alle Benutzer
|
||||
GET /api/benutzer/:id → Ein Benutzer
|
||||
POST /api/benutzer → Benutzer erstellen
|
||||
PUT /api/benutzer/:id → Benutzer ersetzen
|
||||
PATCH /api/benutzer/:id → Benutzer ändern
|
||||
DELETE /api/benutzer/:id → Benutzer löschen
|
||||
```
|
||||
|
||||
### Response-Format
|
||||
|
||||
```json
|
||||
// Erfolgreich (200 OK)
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"name": "Max"
|
||||
}
|
||||
}
|
||||
|
||||
// Erfolg (201 Created)
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Benutzer erstellt",
|
||||
"data": {
|
||||
"id": 2
|
||||
}
|
||||
}
|
||||
|
||||
// Fehler (400 Bad Request)
|
||||
{
|
||||
"status": "error",
|
||||
"message": "Ungültige E-Mail-Adresse"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Datenbank-Zugriff
|
||||
|
||||
### MySQL mit Node.js
|
||||
|
||||
```javascript
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
async function main() {
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'localhost',
|
||||
user: 'root',
|
||||
password: 'passwort',
|
||||
database: 'webshop'
|
||||
});
|
||||
|
||||
// Daten abfragen
|
||||
const [rows] = await connection.execute(
|
||||
'SELECT * FROM benutzer WHERE id = ?',
|
||||
[1]
|
||||
);
|
||||
console.log(rows);
|
||||
|
||||
await connection.end();
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
### MongoDB mit Node.js
|
||||
|
||||
```javascript
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
// Verbindung
|
||||
mongoose.connect('mongodb://localhost:27017/webshop');
|
||||
|
||||
// Schema
|
||||
const benutzerSchema = new mongoose.Schema({
|
||||
name: String,
|
||||
email: { type: String, unique: true },
|
||||
alter: Number
|
||||
});
|
||||
|
||||
const Benutzer = mongoose.model('Benutzer', benutzerSchema);
|
||||
|
||||
// Daten speichern
|
||||
const neuerBenutzer = new Benutzer({
|
||||
name: 'Max',
|
||||
email: 'max@example.com'
|
||||
});
|
||||
await neuerBenutzer.save();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session und Authentifizierung
|
||||
|
||||
### JWT (JSON Web Token)
|
||||
|
||||
```javascript
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
// Token erstellen
|
||||
function createToken(user) {
|
||||
return jwt.sign(
|
||||
{ id: user.id, email: user.email },
|
||||
'geheimer-schluessel',
|
||||
{ expiresIn: '24h' }
|
||||
);
|
||||
}
|
||||
|
||||
// Token prüfen
|
||||
function verifyToken(token) {
|
||||
try {
|
||||
return jwt.verify(token, 'geheimer-schluessel');
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware
|
||||
function authenticate(req, res, next) {
|
||||
const token = req.headers.authorization?.split(' ')[1];
|
||||
const decoded = verifyToken(token);
|
||||
|
||||
if (!decoded) {
|
||||
return res.status(401).json({ error: 'Nicht autorisiert' });
|
||||
}
|
||||
|
||||
req.user = decoded;
|
||||
next();
|
||||
}
|
||||
```
|
||||
|
||||
### Passwort-Hashing
|
||||
|
||||
```javascript
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
// Passwort hashen
|
||||
async function hashPassword(password) {
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
return await bcrypt.hash(password, salt);
|
||||
}
|
||||
|
||||
// Passwort prüfen
|
||||
async function checkPassword(password, hash) {
|
||||
return await bcrypt.compare(password, hash);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RESTful Best Practices
|
||||
|
||||
### Tipps
|
||||
|
||||
```
|
||||
API-Design - Empfehlungen
|
||||
├── Versionierung: /api/v1/...
|
||||
├── Plural: /benutzer nicht /benutzer
|
||||
├── Filter: /benutzer?alter=25
|
||||
├── Sortierung: /benutzer?sort=name
|
||||
├── Paginierung: /benutzer?page=1&limit=10
|
||||
├── Fehlercodes: HTTP-Statuscodes nutzen
|
||||
└── Dokumentation: OpenAPI/Swagger
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Querverweise
|
||||
|
||||
- [[LF6-02-Frontend|Zurück: Frontend-Entwicklung]]
|
||||
- [[LF6-04-Sicherheit-Web|Nächstes Thema: Web-Sicherheit]]
|
||||
- [[LF3-Datenbanken|Datenbanken]]
|
||||
|
||||
---
|
||||
|
||||
*Stand: 2024*
|
||||
257
1-Ausbildungsjahr/LF6-Webanwendungen/LF6-04-Sicherheit-Web.md
Normal file
257
1-Ausbildungsjahr/LF6-Webanwendungen/LF6-04-Sicherheit-Web.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# 6.4 Web-Sicherheit
|
||||
|
||||
## OWASP Top 10
|
||||
|
||||
### Die wichtigsten Sicherheitsrisiken
|
||||
|
||||
```
|
||||
OWASP Top 10 (2021)
|
||||
├── A01: Broken Access Control
|
||||
├── A02: Cryptographic Failures
|
||||
├── A03: Injection
|
||||
├── A04: Insecure Design
|
||||
├── A05: Security Misconfiguration
|
||||
├── A06: Vulnerable Components
|
||||
├── A07: Authentification Failures
|
||||
├── A08: Software and Data Integrity Failures
|
||||
├── A09: Security Logging Failures
|
||||
└── A10: Server-Side Request Forgery
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Injection
|
||||
|
||||
### SQL Injection
|
||||
|
||||
**Problem:** Benutzereingaben werden direkt in SQL-Abfragen eingebaut.
|
||||
|
||||
```sql
|
||||
-- Gefährlich
|
||||
SELECT * FROM benutzer WHERE name = '" + name + "'
|
||||
|
||||
-- Bei name = "' OR '1'='1"
|
||||
SELECT * FROM benutzer WHERE name = '' OR '1'='1'
|
||||
-- → Alle Benutzer werden zurückgegeben!
|
||||
```
|
||||
|
||||
**Schutz: Prepared Statements**
|
||||
|
||||
```javascript
|
||||
// Gefährlich
|
||||
db.query("SELECT * FROM users WHERE name = '" + name + "'");
|
||||
|
||||
// Sicher - Parameterized Query
|
||||
db.query("SELECT * FROM users WHERE name = ?", [name]);
|
||||
```
|
||||
|
||||
### XSS (Cross-Site Scripting)
|
||||
|
||||
**Problem:** Schadcode wird in Webseiten eingeschleust.
|
||||
|
||||
```html
|
||||
<!-- Gefährlich: Benutzereingabe direkt ausgeben -->
|
||||
<div>{{ benutzereingabe }}</div>
|
||||
<!-- Bei eingabe = <script>alert('XSS')</script> -->
|
||||
```
|
||||
|
||||
**Schutz: Output Encoding**
|
||||
|
||||
```javascript
|
||||
// HTML-Escaping
|
||||
function escapeHtml(text) {
|
||||
const map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return text.replace(/[&<>"']/g, m => map[m]);
|
||||
}
|
||||
|
||||
// React macht das automatisch
|
||||
<div>{benutzereingabe}</div>
|
||||
```
|
||||
|
||||
### Content Security Policy (CSP)
|
||||
|
||||
```http
|
||||
Content-Security-Policy: default-src 'self'; script-src 'self'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CSRF (Cross-Site Request Forgery)
|
||||
|
||||
### Das Problem
|
||||
|
||||
```
|
||||
CSRF-Angriff
|
||||
1. Opfer ist eingeloggt bei bank.com
|
||||
2. Opfer besucht bösartige Seite
|
||||
3. Bösartige Seite sendet Request an bank.com
|
||||
4. Browser sendet automatisch Session-Cookie
|
||||
5. Überweisung wird ausgeführt
|
||||
```
|
||||
|
||||
### Schutz: CSRF-Token
|
||||
|
||||
```javascript
|
||||
// Server: Token generieren
|
||||
app.get('/form', (req, res) => {
|
||||
const csrfToken = crypto.randomBytes(32).toString('hex');
|
||||
req.session.csrfToken = csrfToken;
|
||||
res.render('form', { csrfToken });
|
||||
});
|
||||
|
||||
// Server: Token prüfen
|
||||
app.post('/transfer', (req, res) => {
|
||||
if (req.body.csrfToken !== req.session.csrfToken) {
|
||||
return res.status(403).send('CSRF-Angriff erkannt');
|
||||
}
|
||||
// Weiter mit Überweisung...
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentifizierung
|
||||
|
||||
### Unsichere Praktiken
|
||||
|
||||
```
|
||||
Vermeiden
|
||||
├── Passwörter im Klartext speichern
|
||||
├── Schwache Passwörter erlauben
|
||||
├── Keine Zwei-Faktor-Authentifizierung
|
||||
├── Session-IDs in URL
|
||||
└── Unbegrenzte Login-Versuche
|
||||
```
|
||||
|
||||
### Sichere Authentifizierung
|
||||
|
||||
```javascript
|
||||
// 1. Passwörter hashen
|
||||
const hash = await bcrypt.hash(passwort, 12);
|
||||
|
||||
// 2. Rate Limiting
|
||||
const rateLimit = require('express-rate-limit');
|
||||
app.use('/login', rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 5 // 5 Versuche
|
||||
}));
|
||||
|
||||
// 3. Sichere Session
|
||||
app.use(session({
|
||||
secret: 'geheimer-schluessel',
|
||||
httpOnly: true,
|
||||
secure: true, // HTTPS
|
||||
sameSite: 'strict'
|
||||
}));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sicherheits-Header
|
||||
|
||||
### Wichtige Header
|
||||
|
||||
```http
|
||||
# HSTS - HTTPS erzwingen
|
||||
Strict-Transport-Security: max-age=31536000; includeSubDomains
|
||||
|
||||
# X-Content-Type-Options
|
||||
X-Content-Type-Options: nosniff
|
||||
|
||||
# X-Frame-Options - Clickjacking
|
||||
X-Frame-Options: DENY
|
||||
|
||||
# Content Security Policy
|
||||
Content-Security-Policy: default-src 'self'
|
||||
|
||||
# Referrer Policy
|
||||
Referrer-Policy: strict-origin-when-cross-origin
|
||||
```
|
||||
|
||||
### Implementierung in Express
|
||||
|
||||
```javascript
|
||||
const helmet = require('helmet');
|
||||
app.use(helmet());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Eingabevalidierung
|
||||
|
||||
### Grundprinzip
|
||||
|
||||
```
|
||||
Validierung - Regeln
|
||||
├── Nie Benutzereingaben vertrauen
|
||||
├── Client-seitige Validierung reicht nicht
|
||||
├── Whitelist statt Blacklist
|
||||
├── Länge und Format prüfen
|
||||
└── Alle Eingaben validieren
|
||||
```
|
||||
|
||||
### Validierungsbeispiel
|
||||
|
||||
```javascript
|
||||
const Joi = require('joi');
|
||||
|
||||
const benutzerSchema = Joi.object({
|
||||
name: Joi.string()
|
||||
.alphanum()
|
||||
.min(3)
|
||||
.max(30)
|
||||
.required(),
|
||||
|
||||
email: Joi.string()
|
||||
.email()
|
||||
.required(),
|
||||
|
||||
alter: Joi.number()
|
||||
.integer()
|
||||
.min(18)
|
||||
.max(150)
|
||||
});
|
||||
|
||||
// Validierung
|
||||
const { error, value } = benutzerSchema.validate(req.body);
|
||||
if (error) {
|
||||
return res.status(400).json({ error: error.details });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checkliste Web-Sicherheit
|
||||
|
||||
```
|
||||
Sicherheits-Checkliste
|
||||
□ HTTPS erzwingen
|
||||
□ Sicherheits-Header setzen
|
||||
□ SQL Injection verhindern (Prepared Statements)
|
||||
□ XSS verhindern (Escaping)
|
||||
□ CSRF-Token verwenden
|
||||
□ Passwörter hashen (bcrypt)
|
||||
□ Rate Limiting
|
||||
□ Eingaben validieren
|
||||
□ Fehlermeldungen nicht zu detailliert
|
||||
□ Regelmäßige Updates
|
||||
□ Sicherheitstests durchführen
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Querverweise
|
||||
|
||||
- [[LF6-03-Backend|Zurück: Backend-Entwicklung]]
|
||||
- [[LF4-IT-Sicherheit|IT-Sicherheit allgemein]]
|
||||
- [[LF3-05-Datenbankmanagement|Datenbank: SQL Injection]]
|
||||
|
||||
---
|
||||
|
||||
*Stand: 2024*
|
||||
Reference in New Issue
Block a user