Spis treści
Kurs łączy materiał przedmiotów „Bazy danych” i „Projektowanie i administracja relacyjnymi bazami danych”. Zaczyna od modelu relacyjnego i projektowania, przechodzi przez SQL i administrację, a kończy na bezpieczeństwie: uprawnieniach, szyfrowaniu, audycie, SQL Injection i bazach NoSQL. Przykłady są zapisane w składni MariaDB/MySQL (z uwagami o PostgreSQL), a w części o bazach nierelacyjnych w MongoDB.
Kurs jest podzielony na siedem części. Każda lekcja kończy się krótkimi pytaniami „Sprawdź się” ze schowaną odpowiedzią, a każda część zadaniem praktycznym. Ćwiczenia wykonuj na własnych bazach testowych, nigdy na cudzych systemach bez zgody właściciela.
Część A
Projektowanie relacyjnych baz danych
Tabele, klucze, typy danych, więzy, diagramy ERD, normalizacja, separacja danych wrażliwych, transakcje i ACID.
Lekcja 1 · Część A
Podstawy relacyjnych baz danych
Baza danych to uporządkowany zbiór danych, a system zarządzania bazą danych (SZBD) to program, który je przechowuje, udostępnia i chroni (np. MariaDB, MySQL, PostgreSQL, SQL Server, Oracle). W modelu relacyjnym dane są zapisane w tabelach powiązanych relacjami.
| Pojęcie | Znaczenie | Przykład |
|---|---|---|
| Tabela (relacja) | zbiór danych o jednym rodzaju obiektów | uczniowie |
| Wiersz (rekord, krotka) | jeden egzemplarz obiektu | Anna Kowalska, klasa 2A |
| Kolumna (atrybut, pole) | jedna cecha obiektu o określonym typie | nazwisko typu tekstowego |
| Klucz główny (PK) | kolumna (lub zestaw) jednoznacznie identyfikująca wiersz; unikalny i niepusty | id |
| Klucz obcy (FK) | kolumna wskazująca klucz główny innej tabeli; tworzy relację | uczniowie.klasa_id → klasy.id |
| Indeks | struktura przyspieszająca wyszukiwanie | indeks na nazwisko |
| Widok | zapisane zapytanie widziane jak tabela | v_uczniowie |
| Transakcja | grupa operacji wykonywanych w całości albo wcale | przelew: zdjęcie i dopisanie środków |
Rodzaje poleceń SQL
| Grupa | Zadanie | Polecenia |
|---|---|---|
| DDL | definiowanie struktur | CREATE, ALTER, DROP, TRUNCATE |
| DML | zmiana danych | INSERT, UPDATE, DELETE |
| DQL | odczyt danych | SELECT |
| DCL | uprawnienia | GRANT, REVOKE |
| TCL | transakcje | START TRANSACTION, COMMIT, ROLLBACK |
SQL jest standardem, ale każdy SZBD ma własne rozszerzenia. W kursie przykłady są zapisane w składni MariaDB/MySQL, a tam, gdzie różni się PostgreSQL, jest to zaznaczone.
Czym różni się klucz główny od klucza obcego?
Pokaż przykładową odpowiedź
Klucz główny jednoznacznie identyfikuje wiersz w swojej tabeli (unikalny, niepusty). Klucz obcy to kolumna, która wskazuje klucz główny innej tabeli i tworzy powiązanie między tabelami.
Do której grupy poleceń należy GRANT?
Pokaż przykładową odpowiedź
Do DCL (język kontroli dostępu), bo nadaje uprawnienia użytkownikom i rolom.
Lekcja 2 · Część A
Typy danych i więzy integralności
Dobór typu danych wpływa na poprawność, miejsce i bezpieczeństwo. Za mały typ powoduje błędy przepełnienia, za duży marnuje miejsce i obniża wydajność.
| Dane | Dobry typ | Uwagi |
|---|---|---|
| liczby całkowite | TINYINT, SMALLINT, INT, BIGINT | INT signed: do 2 147 483 647; UNSIGNED podwaja zakres dodatni |
| kwoty pieniężne | DECIMAL(10,2) | nigdy FLOAT (błędy zaokrągleń) |
| tekst | VARCHAR(n), CHAR(n), TEXT | ogranicz długość n do potrzeb |
| data i czas | DATE, DATETIME, TIMESTAMP | nie przechowuj dat jako tekstu |
| wartość logiczna | BOOLEAN (TINYINT(1)) | |
| wartość z listy | ENUM lub tabela słownikowa | tabela słownikowa łatwiej się rozwija |
W trybie nieścisłym baza potrafi po cichu obciąć zbyt długi tekst lub zaokrąglić liczbę poza zakresem. Włącz tryb ścisły (sql_mode z STRICT_TRANS_TABLES), aby błędne dane powodowały błąd, a nie były zmieniane.
Więzy integralności
| Więzy | Działanie |
|---|---|
PRIMARY KEY | unikalna, niepusta identyfikacja wiersza |
FOREIGN KEY | wartość musi istnieć w tabeli nadrzędnej |
UNIQUE | wartości w kolumnie nie mogą się powtarzać |
NOT NULL | kolumna nie może być pusta |
CHECK | wartość musi spełniać warunek |
DEFAULT | wartość domyślna |
CREATE TABLE klasy (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
nazwa VARCHAR(10) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uq_klasy_nazwa (nazwa)
) ENGINE=InnoDB;
CREATE TABLE uczniowie (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
imie VARCHAR(50) NOT NULL,
nazwisko VARCHAR(80) NOT NULL,
email VARCHAR(120) NOT NULL,
klasa_id INT UNSIGNED NOT NULL,
utworzono DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_uczniowie_email (email),
CONSTRAINT fk_uczniowie_klasa FOREIGN KEY (klasa_id) REFERENCES klasy (id)
) ENGINE=InnoDB;
Dlaczego kwoty pieniężne przechowuje się jako DECIMAL, a nie FLOAT?
Pokaż przykładową odpowiedź
FLOAT zapisuje liczby przybliżone, więc pojawiają się błędy zaokrągleń. DECIMAL przechowuje dokładne wartości dziesiętne, co jest konieczne w rozliczeniach.
Co robi ograniczenie CHECK?
Pokaż przykładową odpowiedź
Wymusza, aby wartość w kolumnie spełniała podany warunek (np. ocena od 1 do 6). Wstawienie lub zmiana na wartość spoza warunku kończy się błędem.
Lekcja 3 · Część A
Modelowanie: diagramy związków encji i rodzaje relacji
Projekt bazy zaczyna się od diagramu związków encji (ERD): ustalamy encje (obiekty, np. uczeń, klasa, przedmiot), ich atrybuty oraz relacje między nimi. Dopiero potem tworzymy tabele.
| Relacja | Znaczenie | Przykład | Jak ją zapisać w tabelach |
|---|---|---|---|
| 1:1 | jednemu wierszowi odpowiada jeden wiersz | uczeń i jego dane wrażliwe | FK z ograniczeniem UNIQUE lub wspólny klucz |
| 1:N | jeden wiersz ma wiele powiązanych | jedna klasa, wielu uczniów | FK po stronie „wiele” (uczniowie.klasa_id) |
| N:M | wiele do wielu | uczniowie i przedmioty | tabela łącząca z dwoma kluczami obcymi |
KLASY (1) ----< UCZNIOWIE (N)
UCZNIOWIE (N) >----< PRZEDMIOTY (M)
realizowane przez tabelę łączącą OCENY:
OCENY(id, uczen_id -> UCZNIOWIE, przedmiot_id -> PRZEDMIOTY, ocena, data)
- w tabeli łączącej klucz główny bywa złożony (
uczen_id+przedmiot_id+data) lub sztuczny (id), - nazwy tabel i kolumn powinny być spójne i opisowe,
- każdy diagram opisuje kardynalność (1, N) i obowiązkowość (czy relacja musi istnieć),
- diagram jest dokumentacją: aktualizuj go razem ze schematem.
Jak zapiszesz relację wiele do wielu między uczniami a kołami zainteresowań?
Pokaż przykładową odpowiedź
Przez tabelę łączącą (np. czlonkostwa) z kluczami obcymi do uczniowie i kola. Para (uczeń, koło) jest wierszem tabeli łączącej.
Gdzie umieszcza się klucz obcy w relacji 1:N?
Pokaż przykładową odpowiedź
Po stronie „wiele”, czyli w tabeli, której wiersze należą do jednego wiersza tabeli nadrzędnej (np. uczniowie.klasa_id).
Lekcja 4 · Część A
Normalizacja: eliminacja redundancji
Normalizacja to porządkowanie tabel tak, aby ograniczyć redundancję (powtarzanie tych samych danych) oraz anomalie: wstawiania, aktualizacji i usuwania. Kolejne postacie normalne dodają coraz ostrzejsze wymagania.
| Postać | Wymaganie | Naruszenie (przykład) |
|---|---|---|
| 1NF | wartości atomowe, brak powtarzających się grup kolumn | kolumna telefony z listą „111, 222, 333” |
| 2NF | 1NF i brak zależności od części klucza złożonego | w tabeli (zamówienie, produkt) nazwa produktu zależy tylko od produktu |
| 3NF | 2NF i brak zależności przechodnich (kolumna niekluczowa zależy tylko od klucza) | adres klienta w tabeli zamówień zależy od klienta, a nie od zamówienia |
-- Przed: powtarzają się dane klienta i produktu
zamowienia(id_zam, klient, adres_klienta, produkt, cena, ilosc)
-- Po (3NF):
klienci(id_klienta, nazwa, adres)
produkty(id_produktu, nazwa, cena)
zamowienia(id_zam, id_klienta -> klienci, data)
pozycje_zamowienia(id_zam -> zamowienia, id_produktu -> produkty, ilosc)
Korzyść: adres klienta zapisany raz, więc jego zmiana to jedna aktualizacja, a usunięcie zamówienia nie niszczy danych o kliencie. Denormalizację (świadome powtórzenia) stosuje się czasem dla wydajności raportów, ale kosztem spójności i złożoności.
Jaki problem rozwiązuje przeniesienie adresu klienta z tabeli zamówień do tabeli klientów?
Pokaż przykładową odpowiedź
Eliminuje redundancję: adres jest zapisany raz. Unika się anomalii aktualizacji (zmiana adresu w jednym miejscu) i usuwania (usunięcie zamówienia nie usuwa danych klienta).
Co oznacza, że tabela jest w pierwszej postaci normalnej (1NF)?
Pokaż przykładową odpowiedź
Wszystkie wartości są atomowe (niepodzielne), a w tabeli nie ma powtarzających się grup kolumn ani list w jednym polu.
Lekcja 5 · Część A
Separacja danych wrażliwych i minimalizacja danych
Dane wrażliwe (PESEL, adres, dane zdrowotne, hasła) wymagają osobnej ochrony. Projekt schematu powinien oddzielić je od danych zwykłych i ograniczyć ich dostępność.
| Rodzaj separacji | Opis | Przykład |
|---|---|---|
| Logiczna | osobne tabele lub schematy, osobne uprawnienia | tabela dane_osobowe w osobnej bazie lub schemacie |
| Fizyczna | osobny serwer, dysk lub przestrzeń tabel | szyfrowany wolumen tylko dla danych wrażliwych |
| Widoki | udostępnianie tylko wybranych kolumn | v_uczniowie bez adresu i PESEL |
| Szyfrowanie / tokenizacja | dane zapisane w postaci zaszyfrowanej lub zastąpione tokenem | szyfrowanie kolumny po stronie aplikacji |
CREATE DATABASE szkola_wrazliwe;
CREATE TABLE szkola_wrazliwe.dane_osobowe (
uczen_id INT UNSIGNED NOT NULL PRIMARY KEY,
pesel CHAR(11) NOT NULL,
adres VARCHAR(200) NOT NULL
) ENGINE=InnoDB;
CREATE VIEW szkola.v_uczniowie AS
SELECT id, imie, nazwisko, klasa_id FROM szkola.uczniowie;
Minimalizacja danych: przechowuj tylko to, co potrzebne, i tak długo, jak trzeba (zgodność z RODO). Hasła zapisuj jako skróty Argon2/bcrypt z solą, nigdy jawnie ani szyfrowane odwracalnie. Dostęp do danych wrażliwych nadawaj nielicznym rolom.
Po co tworzy się widok pomijający kolumny z danymi wrażliwymi?
Pokaż przykładową odpowiedź
Aby użytkownicy i aplikacje, które nie potrzebują tych danych, miały dostęp tylko do bezpiecznego podzbioru kolumn. Zmniejsza to ryzyko wycieku i ułatwia nadawanie uprawnień.
Dlaczego hasła użytkowników zapisuje się jako skróty, a nie szyfruje?
Pokaż przykładową odpowiedź
Szyfrowanie jest odwracalne: kto zdobędzie klucz, odczyta wszystkie hasła. Skrót z solą (Argon2, bcrypt) jest jednokierunkowy, więc wyciek bazy nie ujawnia haseł wprost.
Lekcja 6 · Część A
Transakcje, ACID, indeksowanie i audytowalność
Transakcja to zestaw operacji, które wykonują się jako jedna całość. Poprawne transakcje spełniają zasady ACID:
| Litera | Zasada | Znaczenie |
|---|---|---|
| A | Atomowość (Atomicity) | wykonują się wszystkie operacje albo żadna |
| C | Spójność (Consistency) | po transakcji dane spełniają wszystkie więzy |
| I | Izolacja (Isolation) | równoległe transakcje nie zakłócają się wzajemnie |
| D | Trwałość (Durability) | zatwierdzone zmiany przetrwają awarię |
Klasyczny przykład: przelew. Zdjęcie kwoty z jednego konta i dopisanie na drugie musi być jedną transakcją, bo awaria po pierwszej operacji zniszczyłaby spójność danych.
Indeksowanie i partycjonowanie (pojęcia)
- indeks przyspiesza wyszukiwanie i sortowanie, ale spowalnia zapis i zajmuje miejsce,
- partycjonowanie dzieli dużą tabelę na części (np. po miesiącach), co ułatwia zarządzanie i archiwizację,
- audytowalność: możliwość ustalenia, kto, co i kiedy zmienił (kolumny audytowe, tabele historii, logi).
Co oznacza atomowość transakcji?
Pokaż przykładową odpowiedź
Że wszystkie jej operacje wykonują się razem albo żadna. W razie błędu wszystkie zmiany są wycofywane, więc dane nie zostają w stanie pośrednim.
Dlaczego nie warto zakładać indeksu na każdej kolumnie?
Pokaż przykładową odpowiedź
Każdy indeks zajmuje miejsce i musi być aktualizowany przy zapisie, więc spowalnia INSERT, UPDATE i DELETE. Indeksy zakłada się na kolumnach często używanych do wyszukiwania, łączenia i sortowania.
Zadanie praktyczne
Projekt bazy danych dziennika szkolnego
Zadanie
Zaprojektuj bazę danych uproszczonego dziennika elektronicznego z uwzględnieniem bezpieczeństwa danych.
Założenia
- Encje: uczniowie, klasy, nauczyciele, przedmioty, oceny oraz dane osobowe uczniów (PESEL, adres).
- Narysuj diagram ERD z relacjami 1:N i N:M (tabela łącząca) i sprawdź, czy schemat jest w 3NF.
- Dobierz typy danych i więzy: klucze główne i obce,
UNIQUE,NOT NULL,CHECK(ocena od 1 do 6). - Oddziel dane wrażliwe do osobnej tabeli lub schematu i przygotuj widok bez tych danych.
- Napisz skrypt
CREATE TABLEi krótko opisz (3–4 zdania), jak projekt chroni dane i wspiera audyt zmian.
Ocenie podlegać będzie
- poprawność ERD i relacji,
- normalizacja do 3NF,
- dobór typów i więzów,
- separacja danych wrażliwych,
- działający skrypt SQL.
Wskazówki
- Tabela łącząca dla uczniów i przedmiotów może być tabelą
oceny. - Pamiętaj o kolejności tworzenia tabel (najpierw nadrzędne).
- Wróć do lekcji 2, 4 i 5.
Część B
SQL: tworzenie struktur i zarządzanie danymi
DDL, indeksy i widoki, klucze obce i kaskady, bezpieczne wstawianie, zmiana i usuwanie danych, transakcje, maskowanie i migracje schematu.
Lekcja 7 · Część B
Tworzenie i modyfikowanie struktur: DDL
CREATE DATABASE szkola CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE szkola;
ALTER TABLE uczniowie ADD COLUMN telefon VARCHAR(20) NULL AFTER email;
ALTER TABLE uczniowie MODIFY COLUMN telefon VARCHAR(25) NULL;
ALTER TABLE uczniowie DROP COLUMN telefon;
TRUNCATE TABLE tymczasowe; -- usuwa wszystkie wiersze, nie da się wycofać w wielu SZBD
DROP TABLE tymczasowe; -- usuwa tabelę wraz z danymi
DROP DATABASE IF EXISTS testowa;
Izolowane schematy i przestrzenie tabel. Osobne schematy (w MariaDB/MySQL baza danych = schemat, w PostgreSQL schematy są wewnątrz bazy) pozwalają oddzielić dane i nadać różne uprawnienia. Przestrzenie tabel (tablespaces) pozwalają umieścić dane na wybranych dyskach, np. szybkim lub szyfrowanym.
Zmiany struktury bez zakłócania działania
- wykonuj zmiany w oknie serwisowym, najpierw na kopii lub środowisku testowym,
- przed zmianą wykonaj kopię zapasową,
- dla dużych tabel używaj zmian online:
ALTER TABLE ... ALGORITHM=INPLACE, LOCK=NONE(MariaDB/MySQL), narzędzi do zmiany schematu bez blokad lub kolumn dodawanych jako opcjonalne, - pamiętaj, że w MariaDB/MySQL polecenia DDL zatwierdzają transakcję domyślnie, a w PostgreSQL DDL można wycofać w transakcji,
- zmiany wprowadzaj skryptami i dokumentuj (lekcja 13).
ALTER TABLE oceny
ADD COLUMN komentarz VARCHAR(200) NULL,
ALGORITHM=INPLACE, LOCK=NONE;
DROP i TRUNCATE usuwają dane bez możliwości wycofania (w większości SZBD). Przed wykonaniem upewnij się, na jakim serwerze i w jakiej bazie jesteś, i miej kopię.
Czym różni się DELETE od TRUNCATE i DROP?
Pokaż przykładową odpowiedź
DELETE usuwa wskazane wiersze i można go wycofać w transakcji. TRUNCATE usuwa wszystkie wiersze tabeli (struktura zostaje), a DROP usuwa całą tabelę. TRUNCATE i DROP w wielu SZBD są nieodwracalne.
Jak ograniczyć skutki zmiany struktury dużej tabeli na działającym systemie?
Pokaż przykładową odpowiedź
Zrobić kopię, przetestować zmianę na kopii, użyć mechanizmów zmiany online (np. ALGORITHM=INPLACE, LOCK=NONE) i wykonać zmianę w oknie serwisowym.
Lekcja 8 · Część B
Indeksy i widoki
Indeksy
CREATE INDEX idx_oceny_uczen ON oceny (uczen_id);
CREATE INDEX idx_logowania_ip_czas ON logowania (adres_ip, czas); -- indeks złożony
CREATE UNIQUE INDEX uq_uczniowie_email ON uczniowie (email);
EXPLAIN SELECT * FROM oceny WHERE uczen_id = 42;
SHOW INDEX FROM oceny;
W wyniku EXPLAIN kolumna type z wartością ALL oznacza pełne przeglądanie tabeli, a ref lub const użycie indeksu. W indeksie złożonym liczy się kolejność kolumn: indeks (adres_ip, czas) pomaga przy warunku na adres_ip, ale nie na samym czas.
- indeks pomaga przy
WHERE,JOIN,ORDER BYiGROUP BY, - nie pomaga przy kolumnach o małym zróżnicowaniu (np. płeć) ani przy funkcjach na kolumnie (
WHERE YEAR(data) = 2026), - zbędne indeksy spowalniają zapis i zajmują miejsce.
Widoki
CREATE VIEW v_oceny_uczniow AS
SELECT u.imie, u.nazwisko, p.nazwa AS przedmiot, o.ocena
FROM oceny o
JOIN uczniowie u ON u.id = o.uczen_id
JOIN przedmioty p ON p.id = o.przedmiot_id;
SELECT * FROM v_oceny_uczniow WHERE nazwisko = 'Kowalska';
Widok upraszcza złożone zapytania i pozwala udostępnić wybrane kolumny i wiersze bez dawania dostępu do tabel źródłowych. Zwykły widok nie przyspiesza zapytań, bo jest wykonywany przy każdym użyciu.
Co oznacza wartość ALL w kolumnie type wyniku EXPLAIN?
Pokaż przykładową odpowiedź
Pełne przeglądanie tabeli (bez użycia indeksu). Przy dużych tabelach jest to wolne i zwykle sygnał, że warto dodać indeks.
Do czego poza upraszczaniem zapytań służą widoki?
Pokaż przykładową odpowiedź
Do ograniczania dostępu do danych: widok ujawnia tylko wybrane kolumny i wiersze, więc użytkownik może dostać uprawnienie do widoku bez uprawnień do tabel.
Lekcja 9 · Część B
Klucze obce, kaskady i spójność referencyjna
Spójność referencyjna oznacza, że każdy klucz obcy wskazuje istniejący wiersz tabeli nadrzędnej. Baza nie pozwoli dodać ucznia z nieistniejącą klasą ani usunąć klasy, do której należą uczniowie (zależnie od ustawionej akcji).
| Akcja przy usunięciu (ON DELETE) | Działanie |
|---|---|
RESTRICT / NO ACTION | blokuje usunięcie, gdy istnieją wiersze zależne (bezpieczne ustawienie domyślne) |
CASCADE | usuwa także wiersze zależne |
SET NULL | ustawia klucz obcy na NULL (kolumna musi dopuszczać NULL) |
SET DEFAULT | ustawia wartość domyślną (nie w InnoDB) |
ALTER TABLE oceny
ADD CONSTRAINT fk_oceny_uczen
FOREIGN KEY (uczen_id) REFERENCES uczniowie (id)
ON DELETE CASCADE
ON UPDATE RESTRICT;
ON DELETE CASCADE na kluczu prowadzącym do ważnych danych może jednym poleceniem usunąć setki powiązanych wierszy (klasa → uczniowie → oceny). Dla danych istotnych stosuj RESTRICT i usuwanie logiczne (lekcja 10).
Co się stanie przy próbie usunięcia wiersza nadrzędnego, do którego odwołuje się klucz obcy z ON DELETE RESTRICT?
Pokaż przykładową odpowiedź
Baza odrzuci operację z błędem, dopóki istnieją wiersze zależne. Najpierw trzeba usunąć lub przenieść wiersze podrzędne.
Dlaczego CASCADE bywa niebezpieczne?
Pokaż przykładową odpowiedź
Jedna operacja usunięcia może automatycznie usunąć wiele powiązanych danych, także tych, których użytkownik się nie spodziewał, co ułatwia przypadkową lub złośliwą utratę danych.
Lekcja 10 · Część B
Wstawianie, zmiana i usuwanie danych z rozliczalnością
INSERT INTO uczniowie (imie, nazwisko, email, klasa_id)
VALUES ('Anna', 'Kowalska', 'anna.k@szkola.pl', 3),
('Jan', 'Nowak', 'jan.n@szkola.pl', 3);
UPDATE uczniowie SET klasa_id = 4 WHERE id = 17;
DELETE FROM uczniowie WHERE id = 17;
Przed UPDATE i DELETE uruchom SELECT z tym samym warunkiem WHERE i sprawdź, które wiersze zostaną objęte. UPDATE lub DELETE bez WHERE zmienia lub usuwa wszystkie wiersze.
Rozliczalność i bezpieczne usuwanie
- kolumny audytowe:
utworzono,zmodyfikowano,zmodyfikowal(kto), - tabele historii lub wyzwalacze zapisujące poprzednie wartości (lekcja 27),
- usuwanie logiczne: kolumna
usunieto_atzamiast kasowania wiersza, a fizyczne usunięcie według procedury (po okresie retencji), - usuwanie zgodnie z procedurą: najpierw kopia/archiwum, potem usunięcie, potem weryfikacja, a także usunięcie z kopii zapasowych zgodnie z polityką,
- nigdy nie wpisuj haseł w jawnej postaci do tabel i logów.
ALTER TABLE uczniowie
ADD COLUMN zmodyfikowano DATETIME NULL ON UPDATE CURRENT_TIMESTAMP,
ADD COLUMN usunieto_at DATETIME NULL;
UPDATE uczniowie SET usunieto_at = NOW() WHERE id = 17;
SELECT * FROM uczniowie WHERE usunieto_at IS NULL;
Co grozi po wykonaniu UPDATE uczniowie SET klasa_id = 1 bez klauzuli WHERE?
Pokaż przykładową odpowiedź
Wszyscy uczniowie zostaną przeniesieni do klasy 1. Dlatego przed zmianą sprawdza się warunek poleceniem SELECT i wykonuje zmianę w transakcji.
Na czym polega usuwanie logiczne i jaka jest jego zaleta?
Pokaż przykładową odpowiedź
Wiersz nie jest kasowany, tylko oznaczany jako usunięty (np. kolumną z datą). Można go odtworzyć i zachować historię, a fizyczne usunięcie wykonuje się według procedury po okresie retencji.
Lekcja 11 · Część B
Transakcje w SQL: COMMIT, ROLLBACK i punkty przywracania
START TRANSACTION;
UPDATE oceny SET ocena = ocena + 0.5
WHERE przedmiot_id = 3 AND ocena < 5.5;
SELECT COUNT(*) AS zmienione FROM oceny WHERE przedmiot_id = 3 AND ocena > 5.5; -- kontrola
COMMIT; -- zatwierdź, jeśli wynik zgodny z oczekiwaniem
-- ROLLBACK; -- albo wycofaj wszystkie zmiany
START TRANSACTION;
INSERT INTO uczniowie (imie, nazwisko, email, klasa_id) VALUES ('A', 'B', 'a@b.pl', 3);
SAVEPOINT po_wstawieniu;
UPDATE uczniowie SET klasa_id = 99 WHERE email = 'a@b.pl'; -- błąd: nie ma klasy 99
ROLLBACK TO po_wstawieniu; -- wycofuje tylko część zmian
COMMIT;
- domyślnie wiele SZBD działa w trybie
autocommit: każde polecenie jest zatwierdzane od razu; transakcję zaczynasz poleceniemSTART TRANSACTION, COMMITzatwierdza,ROLLBACKwycofuje,SAVEPOINTpozwala wycofać część zmian.
Poziomy izolacji
| Poziom | Chroni przed | Uwagi |
|---|---|---|
| READ UNCOMMITTED | niczym | możliwy brudny odczyt |
| READ COMMITTED | brudnym odczytem | domyślny w PostgreSQL |
| REPEATABLE READ | brudnym i niepowtarzalnym odczytem | domyślny w InnoDB (MariaDB/MySQL) |
| SERIALIZABLE | wszystkimi anomaliami (także fantomami) | najwolniejszy |
Przy dłuższych transakcjach rośnie ryzyko blokad i zakleszczeń (deadlock), więc transakcje powinny być krótkie.
Co zrobisz, gdy po masowej aktualizacji w transakcji kontrola pokaże nieoczekiwany wynik?
Pokaż przykładową odpowiedź
Wykonam ROLLBACK, który wycofa wszystkie zmiany z transakcji, a następnie poprawię warunek i powtórzę operację.
Do czego służy SAVEPOINT?
Pokaż przykładową odpowiedź
Pozwala wycofać tylko część zmian wykonanych w transakcji (do wskazanego punktu) bez wycofywania całej transakcji.
Lekcja 12 · Część B
Maskowanie danych wrażliwych i walidacja
Maskowanie ukrywa część danych wrażliwych przy ich wyświetlaniu, np. osobom z działu pomocy. Dane w tabeli pozostają kompletne, a użytkownik widzi wersję zasłoniętą.
SELECT id,
CONCAT(LEFT(email, 2), '***@', SUBSTRING_INDEX(email, '@', -1)) AS email_maska,
CONCAT('*******', RIGHT(pesel, 4)) AS pesel_maska
FROM dane;
CREATE VIEW v_wsparcie AS
SELECT id, imie, CONCAT(LEFT(email, 2), '***@', SUBSTRING_INDEX(email, '@', -1)) AS email
FROM uczniowie;
-- GRANT SELECT ON szkola.v_wsparcie TO rola_wsparcie;
Niektóre SZBD i bramy mają dynamiczne maskowanie (np. SQL Server, filtr maskowania w MariaDB MaxScale). Do środowisk testowych używa się anonimizacji: zamiany prawdziwych danych na fikcyjne, żeby nie kopiować danych osobowych poza produkcję.
Walidacja danych względem typów
- włącz tryb ścisły (
STRICT_TRANS_TABLES), aby błędne wartości kończyły się błędem, - używaj
CHECK(np.ocena BETWEEN 1 AND 6),ENUMlub tabel słownikowych, - waliduj dane także w aplikacji: baza jest ostatnią linią obrony, nie jedyną (obrona warstwowa).
Czym różni się maskowanie danych od szyfrowania?
Pokaż przykładową odpowiedź
Maskowanie zmienia sposób wyświetlania danych (ukrywa ich część), a dane w bazie pozostają jawne dla uprawnionych. Szyfrowanie zmienia zapisane dane tak, że bez klucza są nieczytelne.
Dlaczego do testów nie powinno się używać kopii produkcyjnej danych osobowych?
Pokaż przykładową odpowiedź
Środowiska testowe są zwykle słabiej chronione, więc wzrasta ryzyko wycieku. Należy używać danych zanonimizowanych lub fikcyjnych.
Lekcja 13 · Część B
Dokumentowanie zmian i wersjonowanie schematu
Schemat bazy zmienia się razem z aplikacją. Zmiany wprowadzane ręcznie na serwerze są trudne do odtworzenia i śledzenia. Dlatego stosuje się migracje: kolejne, numerowane skrypty SQL przechowywane w repozytorium (np. Git).
migracje/
V001__utworz_tabele_podstawowe.sql
V002__dodaj_kolumne_telefon.sql
V003__indeks_oceny_uczen.sql
V004__widok_wsparcie.sql
- narzędzia takie jak Flyway i Liquibase zapamiętują w bazie, które migracje zostały wykonane (tabela historii),
- każda migracja ma skrypt „w przód” i, jeśli to możliwe, skrypt cofający,
- migracje przechodzą przegląd kodu i testy na kopii przed wdrożeniem,
- obok skryptów utrzymuj dokumentację: aktualny diagram ERD i słownik danych (opis tabel i kolumn, typy, więzy, wrażliwość danych),
- zmiany na produkcji wykonuj wyłącznie przez migracje, nigdy ręcznie.
Dlaczego zmiany schematu bazy powinny być zapisywane jako migracje w repozytorium?
Pokaż przykładową odpowiedź
Pozwala to odtworzyć dowolny stan bazy, śledzić kto i kiedy zmienił schemat, przeglądać zmiany przed wdrożeniem i identycznie wdrażać je na wszystkich środowiskach.
Co powinien zawierać słownik danych?
Pokaż przykładową odpowiedź
Opis tabel i kolumn: znaczenie, typ, więzy, wartości dozwolone, informację o wrażliwości danych oraz relacje między tabelami.
Zadanie praktyczne
Implementacja schematu, danych i bezpiecznych operacji
Zadanie
Zaimplementuj schemat dziennika z zadania A i wykonaj bezpieczne operacje na danych.
Założenia
- Utwórz bazę i tabele (z kluczami, więzami i indeksami), oddzielając dane wrażliwe. Każdą zmianę zapisz jako osobną migrację (plik
V001__...sql). - Dodaj widok ukrywający dane wrażliwe i widok maskujący adres e-mail dla roli wsparcia.
- Wstaw przykładowe dane. Wykonaj masową aktualizację ocen w transakcji z kontrolą wyniku, a potem wycofaj ją poleceniem
ROLLBACKi zweryfikuj powrót danych. - Zademonstruj działanie klucza obcego (próba wstawienia ucznia z nieistniejącą klasą) i wpływ
ON DELETE RESTRICTna usuwanie klasy. - Dodaj kolumny audytowe i usuwanie logiczne. Opisz w 3–4 zdaniach, jak procedura usuwania chroni dane.
Ocenie podlegać będzie
- poprawna struktura i migracje,
- poprawne widoki i maskowanie,
- poprawne użycie transakcji,
- zrozumienie więzów i akcji referencyjnych,
- czytelna dokumentacja.
Wskazówki
- Każdą migrację testuj na pustej bazie testowej.
- Przed UPDATE sprawdź warunek poleceniem SELECT.
- Skorzystaj z przykładów w lekcjach 7–13.
Część C
Zapytania SQL i analiza danych
SELECT, JOIN, agregacje, podzapytania, wykrywanie anomalii i ataków w logach oraz weryfikacja zapytań tworzonych z pomocą AI.
Lekcja 14 · Część C
SELECT: wybieranie, filtrowanie i sortowanie danych
W tej części korzystamy z przykładowych tabel: uzytkownicy(id, login, rola) i logowania(id, uzytkownik_id, adres_ip, wynik, czas), gdzie wynik to „OK” lub „BLAD”.
SELECT login, rola FROM uzytkownicy WHERE rola IN ('admin', 'kadry') ORDER BY login;
SELECT DISTINCT adres_ip FROM logowania WHERE wynik = 'BLAD';
SELECT id, czas FROM logowania
WHERE czas BETWEEN '2026-10-05 00:00:00' AND '2026-10-05 23:59:59'
ORDER BY czas DESC
LIMIT 20;
SELECT login FROM uzytkownicy WHERE login LIKE 'a%'; -- zaczyna się od „a”
SELECT login FROM uzytkownicy WHERE telefon IS NULL; -- uwaga: nie „= NULL”
| Klauzula | Zadanie |
|---|---|
SELECT | które kolumny lub wyrażenia zwrócić (unikaj SELECT * w kodzie aplikacji) |
FROM | z jakiej tabeli lub widoku |
WHERE | filtruje wiersze przed grupowaniem |
GROUP BY, HAVING | grupowanie i filtrowanie grup |
ORDER BY | sortowanie wyniku |
LIMIT | ograniczenie liczby wierszy |
NULL oznacza „brak wartości”. Porównanie kolumna = NULL nigdy nie daje prawdy. Używaj IS NULL i IS NOT NULL. Wyrażenia z NULL zwykle dają NULL (np. 5 + NULL).
Czym różni się WHERE od HAVING?
Pokaż przykładową odpowiedź
WHERE filtruje pojedyncze wiersze przed grupowaniem, a HAVING filtruje grupy po zastosowaniu funkcji agregujących (np. COUNT).
Dlaczego zapytanie SELECT * FROM t WHERE telefon = NULL nie zwróci wierszy z pustym telefonem?
Pokaż przykładową odpowiedź
Porównanie z NULL nigdy nie daje prawdy. Poprawny zapis to WHERE telefon IS NULL.
Lekcja 15 · Część C
JOIN: łączenie tabel i korelowanie logów
Dane z kilku tabel łączy się klauzulą JOIN na podstawie kluczy. To podstawowe narzędzie analizy zdarzeń: np. powiązanie logów logowania z kontami użytkowników.
| JOIN | Wynik |
|---|---|
INNER JOIN (JOIN) | tylko wiersze mające pary w obu tabelach |
LEFT JOIN | wszystkie wiersze lewej tabeli; brakujące dane z prawej jako NULL |
RIGHT JOIN | wszystkie wiersze prawej tabeli |
FULL OUTER JOIN | wiersze z obu tabel (PostgreSQL; w MySQL/MariaDB przez UNION) |
CROSS JOIN | iloczyn kartezjański: każdy z każdym |
SELECT u.login, l.adres_ip, l.czas
FROM logowania l
JOIN uzytkownicy u ON u.id = l.uzytkownik_id
WHERE l.wynik = 'BLAD'
AND l.czas >= NOW() - INTERVAL 1 HOUR
ORDER BY l.czas DESC;
SELECT u.login
FROM uzytkownicy u
LEFT JOIN logowania l ON l.uzytkownik_id = u.id
WHERE l.id IS NULL;
Łącząc tabele logów (logowania, zdarzenia systemowe, zmiany uprawnień) po wspólnych kluczach lub czasie, odtwarzasz przebieg incydentu: kto, skąd, kiedy i co potem zrobił.
Czym różni się INNER JOIN od LEFT JOIN?
Pokaż przykładową odpowiedź
INNER JOIN zwraca tylko wiersze, które mają odpowiednik w obu tabelach. LEFT JOIN zwraca wszystkie wiersze lewej tabeli, a brakujące dane z prawej uzupełnia wartościami NULL.
Jak znajdziesz konta bez żadnego logowania?
Pokaż przykładową odpowiedź
LEFT JOIN tabeli kont z logowaniami i warunek WHERE l.id IS NULL, który zostawia konta bez odpowiadających wierszy w tabeli logowań.
Lekcja 16 · Część C
Funkcje agregujące, GROUP BY i wykrywanie ataków brute-force
Funkcje agregujące zwracają jedną wartość dla grupy wierszy: COUNT, SUM, AVG, MIN, MAX. Z GROUP BY liczą wartości osobno dla każdej grupy.
SELECT adres_ip,
COUNT(*) AS proby,
MIN(czas) AS pierwsza,
MAX(czas) AS ostatnia
FROM logowania
WHERE wynik = 'BLAD'
AND czas >= NOW() - INTERVAL 15 MINUTE
GROUP BY adres_ip
HAVING COUNT(*) >= 10
ORDER BY proby DESC;
Wiele błędnych prób z jednego adresu w krótkim czasie to typowy sygnał ataku brute-force. Podobnie wykrywa się anomalie statystyczne: konta z liczbą logowań wyraźnie powyżej średniej lub aktywność w nietypowych godzinach.
SELECT uzytkownik_id, COUNT(*) AS liczba
FROM logowania
WHERE czas >= NOW() - INTERVAL 1 DAY
GROUP BY uzytkownik_id
HAVING COUNT(*) > (SELECT AVG(c) + 3 * STDDEV(c)
FROM (SELECT COUNT(*) AS c FROM logowania
WHERE czas >= NOW() - INTERVAL 1 DAY
GROUP BY uzytkownik_id) t);
Każda kolumna w SELECT, która nie jest funkcją agregującą, musi występować w GROUP BY. W przeciwnym razie wynik jest niejednoznaczny (i część SZBD zgłosi błąd).
Co robi klauzula HAVING COUNT(*) >= 10 w zapytaniu z GROUP BY adres_ip?
Pokaż przykładową odpowiedź
Zostawia tylko te adresy IP, dla których w grupie jest co najmniej 10 wierszy (np. 10 nieudanych logowań).
Jak za pomocą SQL wykryjesz prawdopodobny atak brute-force na logowanie?
Pokaż przykładową odpowiedź
Zliczam nieudane logowania pogrupowane po adresie IP (lub koncie) w krótkim oknie czasowym i wybieram grupy z liczbą prób powyżej progu (GROUP BY + HAVING).
Lekcja 17 · Część C
Podzapytania i wykrywanie nieautoryzowanych powiązań
Podzapytanie to zapytanie zagnieżdżone w innym. Może zwracać jedną wartość, listę wartości lub tabelę pomocniczą. Skorelowane podzapytanie odwołuje się do wiersza zapytania zewnętrznego.
-- skalarne: konta z największą liczbą logowań
SELECT login FROM uzytkownicy
WHERE id = (SELECT uzytkownik_id FROM logowania GROUP BY uzytkownik_id ORDER BY COUNT(*) DESC LIMIT 1);
-- IN: użytkownicy z nieudanymi logowaniami
SELECT login FROM uzytkownicy
WHERE id IN (SELECT uzytkownik_id FROM logowania WHERE wynik = 'BLAD');
-- tabela pochodna w FROM
SELECT AVG(liczba) FROM (SELECT COUNT(*) AS liczba FROM logowania GROUP BY adres_ip) t;
Podzapytania świetnie nadają się do znajdowania nieautoryzowanych powiązań, np. użytkowników, którzy mają dostęp do zasobu, choć nie mają wymaganej roli:
SELECT d.uzytkownik_id
FROM dostepy d
WHERE d.zasob = 'place'
AND NOT EXISTS (SELECT 1
FROM role_uzytkownikow r
WHERE r.uzytkownik_id = d.uzytkownik_id
AND r.rola = 'kadry');
Wyrażenia WITH nazwa AS (...) (CTE) czynią złożone zapytania czytelniejszymi. Są dostępne w MariaDB 10.2+, MySQL 8 i PostgreSQL.
Do czego służy EXISTS w podzapytaniu?
Pokaż przykładową odpowiedź
Sprawdza, czy podzapytanie zwraca jakikolwiek wiersz. Z NOT EXISTS znajduje wiersze, dla których nie istnieje powiązany wiersz.
Jak znajdziesz użytkowników mających uprawnienie do zasobu, ale bez wymaganej roli?
Pokaż przykładową odpowiedź
Zapytaniem z NOT EXISTS (lub NOT IN), które dla każdego wpisu dostępu sprawdza brak odpowiedniej roli w tabeli ról.
Lekcja 18 · Część C
Generatywna AI i zapytania SQL: tworzenie, refaktoryzacja i weryfikacja
Narzędzia generatywnej AI potrafią napisać zapytanie SQL na podstawie opisu w języku naturalnym, zaproponować czytelniejszą wersję i wyjaśnić działanie istniejącego kodu. Wynik zawsze wymaga weryfikacji: zapytanie może wyglądać poprawnie, a zwracać błędne dane.
Dobry opis zadania dla AI
- podaj schemat (nazwy tabel, kolumn, typy, klucze), bez prawdziwych danych,
- określ dialekt SQL (MariaDB, PostgreSQL),
- opisz oczekiwany wynik i warunki (zakres czasu, progi),
- poproś o wyjaśnienie kroków, aby łatwiej sprawdzić poprawność.
Weryfikacja ręczna
| Kontrola | Jak |
|---|---|
| Czy kolumny i tabele istnieją | uruchom na testowej bazie; AI bywa „halucynować” nazwy |
| Czy JOIN nie mnoży wierszy | porównaj liczbę wierszy z oczekiwaną; sprawdź klucze łączenia |
| Czy wynik jest poprawny | przetestuj na małym zbiorze z znanym wynikiem |
| Wydajność | EXPLAIN, zwróć uwagę na pełne przeglądanie tabel |
| Bezpieczeństwo | szukaj DELETE/UPDATE bez WHERE, DROP, zmian uprawnień |
-- „Usuń nieaktywnych użytkowników”
DELETE FROM uzytkownicy; -- brak WHERE: usunie wszystkich!
-- Poprawnie (po kontroli SELECT i w transakcji):
START TRANSACTION;
SELECT COUNT(*) FROM uzytkownicy WHERE ostatnie_logowanie < NOW() - INTERVAL 2 YEAR;
UPDATE uzytkownicy SET usunieto_at = NOW() WHERE ostatnie_logowanie < NOW() - INTERVAL 2 YEAR;
COMMIT;
Nie wklejaj do narzędzi AI danych osobowych, haseł ani zrzutów prawdziwych baz. Kod SQL tworzony w aplikacji z użyciem konkatenacji tekstów (zamiast zapytań parametryzowanych) jest podatny na SQL Injection także wtedy, gdy wygenerowała go AI.
Jakich informacji nie wolno przekazywać narzędziu AI przy pisaniu zapytań?
Pokaż przykładową odpowiedź
Prawdziwych danych osobowych, haseł, kluczy i zawartości produkcyjnych baz. Wystarczy sam schemat (nazwy i typy) oraz opis zadania.
Jak sprawdzisz zapytanie SQL wygenerowane przez AI przed użyciem w systemie?
Pokaż przykładową odpowiedź
Uruchomię je na testowej kopii z małym zbiorem o znanym wyniku, sprawdzę liczbę i treść wierszy, plan wykonania (EXPLAIN) oraz brak niebezpiecznych poleceń (DELETE/UPDATE bez WHERE, DROP).
Zadanie praktyczne
Analiza logów logowania w bazie danych
Zadanie
Dostajesz tabele uzytkownicy(id, login, rola), logowania(id, uzytkownik_id, adres_ip, wynik, czas) i dostepy(uzytkownik_id, zasob). Utwórz je w bazie testowej, wstaw kilkadziesiąt przykładowych wierszy (w tym symulowany atak) i napisz zapytania.
Założenia
- Wypisz nieudane logowania z ostatnich 24 godzin wraz z loginem użytkownika (
JOIN). - Znajdź adresy IP z co najmniej 10 nieudanymi próbami w 15 minut (
GROUP BY+HAVING). - Znajdź konta, które zalogowały się poprawnie zaraz po serii błędnych prób z tego samego adresu IP.
- Znajdź użytkowników z dostępem do zasobu „place” bez roli „kadry” (
NOT EXISTS). - Poproś narzędzie AI o jedno z zapytań, zweryfikuj je (test na znanych danych,
EXPLAIN) i opisz znalezione błędy lub potwierdź poprawność.
Ocenie podlegać będzie
- poprawność zapytań i wyników,
- umiejętność wykrycia wzorca ataku,
- poprawne użycie JOIN, agregacji i podzapytań,
- krytyczna weryfikacja kodu z AI,
- czytelność i opis wniosków.
Wskazówki
- Dla zapytania 3 użyj samozłączenia lub podzapytania po adresie IP i czasie.
- Dodaj indeks na (
adres_ip,czas) i sprawdźEXPLAIN. - Skorzystaj z przykładów w lekcjach 15–17.
Część D
Administracja SZBD, kopie zapasowe i odtwarzanie
Instalacja i konfiguracja serwera, dostęp lokalny i zdalny, wydajność i miejsce, kopie zapasowe, odtwarzanie, harmonogram i logi.
Lekcja 19 · Część D
Instalacja i konfiguracja serwera bazy danych
Po instalacji SZBD (np. apt install mariadb-server w Linux, instalator MSI lub pakiet WAMP w Windows) wykonuje się wstępne zabezpieczenie i konfigurację.
sudo mariadb-secure-installation
# ustaw hasło/uwierzytelnianie root, usuń anonimowych użytkowników,
# zablokuj zdalne logowanie roota, usuń bazę „test”, przeładuj uprawnienia
[mysqld]
bind-address = 127.0.0.1 # nasłuch tylko lokalnie
port = 3306
sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
local_infile = 0
| Dostęp | Konfiguracja | Uwagi |
|---|---|---|
| Lokalny | gniazdo Unix lub 127.0.0.1 | najbezpieczniejszy; konto root może używać uwierzytelniania gniazdem (unix_socket) |
| Zdalny | bind-address na wybrany interfejs, konto 'u'@'adres', otwarty port w zaporze | ogranicz do sieci aplikacji, wymuś TLS, nie wystawiaj portu 3306 do internetu |
CREATE USER 'app'@'10.0.20.%' IDENTIFIED BY 'LosoweDlugieHaslo!';
GRANT SELECT, INSERT, UPDATE ON szkola.* TO 'app'@'10.0.20.%';
W PostgreSQL nasłuch ustawia listen_addresses w postgresql.conf, a dostęp zdalny określa pg_hba.conf (kto, do jakiej bazy, z jakiego adresu i jaką metodą uwierzytelnienia).
Dlaczego po instalacji uruchamia się mariadb-secure-installation?
Pokaż przykładową odpowiedź
Usuwa niebezpieczne ustawienia domyślne: anonimowych użytkowników, zdalne logowanie roota i bazę testową oraz pozwala ustawić silne uwierzytelnianie konta administratora.
Co oznacza bind-address = 127.0.0.1?
Pokaż przykładową odpowiedź
Serwer nasłuchuje tylko na pętli zwrotnej, więc połączenia są możliwe wyłącznie z tego samego komputera. Zdalny dostęp wymaga zmiany ustawienia i zabezpieczenia (zapora, TLS, ograniczenie kont).
Lekcja 20 · Część D
Przestrzeń dyskowa, wydajność i monitorowanie serwera
SELECT table_schema AS baza,
ROUND(SUM(data_length + index_length) / 1024 / 1024, 1) AS rozmiar_mb
FROM information_schema.tables
GROUP BY table_schema
ORDER BY rozmiar_mb DESC;
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW FULL PROCESSLIST; -- aktualnie wykonywane zapytania
SHOW GLOBAL VARIABLES LIKE 'slow_query_log%';
-- w konfiguracji: logowanie wolnych zapytań
slow_query_log = 1
long_query_time = 1
log_queries_not_using_indexes = 1
| Zagadnienie | Co robić |
|---|---|
| Miejsce na dysku | monitoruj zajętość (df -h), rotuj logi binarne i błędów, osobna partycja na dane |
| Wydajność zapytań | EXPLAIN, indeksy, log wolnych zapytań, optymalizacja kodu |
| Pamięć | ustaw rozmiar bufora (np. innodb_buffer_pool_size) odpowiednio do zasobów |
| Połączenia | limit max_connections, pula połączeń w aplikacji |
| Statystyki i tabele | ANALYZE TABLE (statystyki), OPTIMIZE TABLE (defragmentacja) |
Zapełnienie dysku (np. przez rosnące logi binarne) powoduje zatrzymanie bazy. To także wektor ataku typu DoS, więc monitoruj miejsce i ustaw automatyczne wygasanie logów (binlog_expire_logs_seconds).
Jak sprawdzisz, jakie zapytania serwer wykonuje w tej chwili?
Pokaż przykładową odpowiedź
Poleceniem SHOW FULL PROCESSLIST (lub widokiem information_schema.PROCESSLIST).
Do czego służy log wolnych zapytań?
Pokaż przykładową odpowiedź
Zapisuje zapytania wykonujące się dłużej niż ustalony próg, co pozwala znaleźć miejsca wymagające indeksu lub optymalizacji.
Lekcja 21 · Część D
Kopie zapasowe baz danych
| Rodzaj | Opis | Narzędzia | Zalety i wady |
|---|---|---|---|
| Logiczna | zapis poleceń SQL odtwarzających dane | mariadb-dump (mysqldump), pg_dump | przenośna, łatwa do analizy; wolna dla dużych baz |
| Fizyczna | kopia plików danych | mariabackup, pg_basebackup | szybka dla dużych baz; związana z wersją i systemem |
| Przyrostowa | tylko zmiany od poprzedniej kopii | mariabackup --incremental-basedir, archiwizacja WAL/binlog | oszczędza miejsce; odtwarzanie wymaga łańcucha |
mariadb-dump --single-transaction --routines --triggers --events \
--databases szkola > szkola_$(date +%F).sql
pg_dump -Fc szkola > szkola_$(date +%F).dump # PostgreSQL
mariabackup --backup --target-dir=/kopie/pelna --user=kopie --password='...'
mariabackup --backup --target-dir=/kopie/inc1 --incremental-basedir=/kopie/pelna --user=kopie --password='...'
mariabackup --prepare --target-dir=/kopie/pelna
--single-transactiondaje spójną kopię tabel InnoDB bez blokowania zapisu,- dziennik binarny (binlog) i WAL (PostgreSQL) pozwalają odtworzyć stan z konkretnej chwili (PITR),
- kopie szyfruj i chroń uprawnieniami (
chmod 600), a hasło do kopii trzymaj w magazynie sekretów, - kopie przechowuj zgodnie z zasadą 3-2-1 i testuj odtwarzanie.
Czym różni się kopia logiczna od fizycznej?
Pokaż przykładową odpowiedź
Kopia logiczna zapisuje polecenia SQL (dane i struktury) i jest przenośna, ale wolniejsza. Fizyczna kopiuje pliki danych serwera, jest szybka dla dużych baz, ale zależy od wersji i konfiguracji serwera.
Po co stosuje się opcję --single-transaction w mysqldump?
Pokaż przykładową odpowiedź
Aby uzyskać spójną kopię tabel transakcyjnych (InnoDB) w jednej chwili bez blokowania zapisu przez całą kopię.
Lekcja 22 · Część D
Odtwarzanie po awarii, harmonogram i monitorowanie logów
Odtwarzanie
# odtworzenie bazy z kopii
mariadb < szkola_2026-10-05.sql
# odtworzenie do chwili sprzed błędnego polecenia (PITR): kopia pełna + dziennik binarny
mariadb-binlog --start-datetime="2026-10-05 10:00:00" --stop-datetime="2026-10-05 10:29:59" \
/var/lib/mysql/mariadb-bin.000012 | mariadb
Procedura: zatrzymaj aplikację (lub przełącz w tryb tylko do odczytu), odtwórz dane na osobnej instancji, sprawdź spójność (liczby rekordów, wybrane zapytania, CHECK TABLE), a dopiero potem przywróć ruch. Odtwarzanie ćwicz regularnie, bo nieprzetestowana kopia nie jest gwarancją.
Harmonogram zadań
# crontab -e (konto kopii)
30 1 * * * /usr/local/bin/kopia_baz.sh
0 4 * * 0 find /kopie -name "*.sql.gz" -mtime +30 -delete
Logi i wykrywanie błędów oraz prób nadużyć
- dziennik błędów (np.
/var/log/mysql/error.log) pokazuje awarie, błędy uruchamiania, zapełnienie dysku, - komunikat
Access denied for userpowtarzany z jednego adresu to sygnał próby zgadywania hasła, - regularnie rotuj i czyść logi, ale archiwizuj te potrzebne do audytu,
- wysyłaj logi do centralnego systemu (SIEM) i ustaw alerty na krytyczne błędy.
Zasada 3-2-1: trzy kopie danych, na dwóch różnych nośnikach, jedna poza lokalizacją podstawową (lub niezmienialna kopia odłączona od sieci, ochrona przed ransomware).
Co umożliwia dziennik binarny w odtwarzaniu bazy?
Pokaż przykładową odpowiedź
Odtworzenie stanu z konkretnej chwili (PITR): po przywróceniu kopii pełnej odtwarza się zapisane w dzienniku zmiany do wybranego momentu, np. tuż przed błędnym usunięciem danych.
Jak rozpoznasz w logach próbę zgadywania hasła do bazy?
Pokaż przykładową odpowiedź
Wiele komunikatów Access denied for user w krótkim czasie dla tego samego konta lub z jednego adresu IP. Warto ustawić na to alert.
Zadanie praktyczne
Plan administracyjny serwera bazy danych
Zadanie
Zainstaluj serwer bazy danych na maszynie testowej i przygotuj plan jego utrzymania.
Założenia
- Zainstaluj SZBD, uruchom skrypt zabezpieczający, ustaw nasłuch lokalny i tryb ścisły. Opisz, jak umożliwisz zdalny dostęp aplikacji z jednej podsieci (konto, zapora, TLS).
- Napisz zapytanie pokazujące rozmiary baz i włącz log wolnych zapytań. Opisz, kiedy dodasz indeks.
- Przygotuj skrypt kopii nocnej (kopia logiczna), zaplanuj go w cron i opisz retencję oraz miejsca przechowywania zgodnie z 3-2-1.
- Wykonaj symulowaną awarię (usuń dane z tabeli testowej) i odtwórz je z kopii. Zapisz czas odtworzenia i porównaj dane.
- Zaproponuj trzy alerty na podstawie logów i miejsc na dysku (np. brak miejsca, błędy logowania, awaria kopii).
Ocenie podlegać będzie
- poprawna instalacja i zabezpieczenie,
- poprawny skrypt i harmonogram kopii,
- udane i udokumentowane odtworzenie,
- sensowny plan monitorowania,
- zgodność z zasadą 3-2-1.
Wskazówki
- Polecenia znajdziesz w lekcjach 19–22.
- Odtwarzanie ćwicz na osobnej instancji lub testowej bazie.
- Hasła do kopii trzymaj poza skryptem.
Część E
Bezpieczeństwo baz danych: dostęp, szyfrowanie, audyt
Konta i role, hasła i sekrety, integracja z katalogiem, szyfrowanie, audyt i wyzwalacze, monitorowanie oraz utwardzanie serwera.
Lekcja 23 · Część E
Użytkownicy, role i zasada najmniejszych uprawnień
Dostęp do bazy nadaje się przez konta i role. Zasada najmniejszych uprawnień oznacza, że każde konto ma tylko takie prawa, jakich wymaga jego zadanie, i nic więcej.
CREATE USER 'raport'@'10.0.20.%' IDENTIFIED BY '...';
GRANT SELECT ON szkola.v_oceny_uczniow TO 'raport'@'10.0.20.%'; -- tylko widok, tylko odczyt
CREATE ROLE nauczyciel;
GRANT SELECT, INSERT, UPDATE ON szkola.oceny TO nauczyciel;
GRANT SELECT ON szkola.uczniowie TO nauczyciel;
GRANT nauczyciel TO 'anna'@'%';
SET DEFAULT ROLE nauczyciel FOR 'anna'@'%';
SHOW GRANTS FOR 'anna'@'%';
REVOKE INSERT ON szkola.oceny FROM nauczyciel;
DROP USER 'anna'@'%'; -- po odejściu pracownika
| Typ konta | Uprawnienia | Uwagi |
|---|---|---|
| Aplikacyjne | SELECT, INSERT, UPDATE (czasem DELETE) do swoich tabel | bez DROP, ALTER, FILE |
| Raportowe | tylko SELECT na widoki | osobne konto, najlepiej na replice |
| Migracyjne | CREATE, ALTER w swoim schemacie | używane tylko podczas wdrożeń |
| Administracyjne | szerokie | imienne konta administratorów, MFA do dostępu do serwera, ograniczony zakres adresów |
GRANT ALL PRIVILEGES ON *.*, uprawnień GRANT OPTION, FILE, SUPER dla kont aplikacji oraz wspólnych kont używanych przez wiele osób (brak rozliczalności). Okresowo przeglądaj nadane prawa i usuwaj zbędne.
Jakich uprawnień potrzebuje konto aplikacji WWW, która tylko odczytuje i dopisuje dane w swoich tabelach?
Pokaż przykładową odpowiedź
SELECT i INSERT (ewentualnie UPDATE) na tych tabelach. Nie powinno mieć DROP, ALTER, FILE ani praw administratora.
Jaką korzyść daje użycie ról zamiast nadawania uprawnień każdemu użytkownikowi osobno?
Pokaż przykładową odpowiedź
Uprawnienia definiuje się raz dla roli, a użytkownika dodaje się do roli. Ułatwia to zarządzanie, spójność i audyt oraz szybkie odebranie dostępu.
Lekcja 24 · Część E
Hasła, MFA i magazyny sekretów
Polityka haseł i rotacja
- silne, długie i losowe hasła dla kont serwisowych, zapisane w magazynie sekretów (nie w kodzie),
- polityka haseł: plugin sprawdzający jakość hasła (np.
simple_password_checkw MariaDB,validate_passwordw MySQL), - rotacja haseł kont serwisowych według harmonogramu, najlepiej automatyczna,
- wygasanie haseł kont ludzkich tylko tam, gdzie wymaga tego polityka (zgodnie z aktualnymi zaleceniami nie wymusza się zbyt częstych zmian).
ALTER USER 'serwis'@'10.0.20.5' PASSWORD EXPIRE INTERVAL 90 DAY;
ALTER USER 'anna'@'%' ACCOUNT LOCK; -- czasowa blokada konta
Sekrety dla aplikacji
Aplikacja potrzebuje hasła do bazy, ale nie powinno ono leżeć w kodzie ani w repozytorium. Dobre sposoby dostarczania sekretów:
| Sposób | Opis |
|---|---|
| Magazyn sekretów | zewnętrzna usługa (np. HashiCorp Vault, menedżer sekretów dostawcy chmury); aplikacja pobiera hasło po uwierzytelnieniu, a sekret można rotować |
| Zmienne środowiskowe | wstrzykiwane przy uruchomieniu; nie trafiają do repozytorium |
| Plik konfiguracyjny poza repozytorium | z prawem odczytu tylko dla konta aplikacji (chmod 600) |
Uwierzytelnianie wieloskładnikowe dla administratorów
Większość SZBD nie ma wbudowanego MFA dla zwykłych połączeń, dlatego stosuje się je na poziomie dostępu do serwera: administrator łączy się przez VPN lub bastion z MFA (TOTP, klucz FIDO2/U2F), a konta administracyjne można uwierzytelniać przez PAM lub SSO. Dostęp bezpośredni z internetu do portu bazy jest wykluczony.
Dlaczego hasło do bazy nie powinno być zapisane w pliku kodu aplikacji?
Pokaż przykładową odpowiedź
Kod trafia do repozytoriów i kopii, a wyciek pliku ujawnia hasło. Sekrety trzyma się w magazynie sekretów lub w konfiguracji poza repozytorium z ograniczonymi uprawnieniami.
Jak zapewnić MFA administratorom bazy, jeśli SZBD go nie obsługuje?
Pokaż przykładową odpowiedź
Wymagać połączenia przez VPN lub bastion z uwierzytelnianiem wieloskładnikowym i ograniczyć dostęp do portu bazy do tej sieci, a konta administracyjne uwierzytelniać przez PAM lub SSO.
Lekcja 25 · Część E
Integracja z LDAP/Active Directory, limity i okna czasowe
Centralne zarządzanie tożsamością (LDAP, Active Directory) pozwala zarządzać kontami w jednym miejscu: wyłączenie konta w AD blokuje dostęp do bazy, a zmiany grup przekładają się na uprawnienia.
| SZBD | Sposób integracji |
|---|---|
| MariaDB / MySQL | wtyczka PAM (IDENTIFIED VIA pam) przekazująca uwierzytelnianie do SSSD/LDAP/AD |
| PostgreSQL | metoda ldap lub gss (Kerberos) w pg_hba.conf |
| SQL Server | uwierzytelnianie zintegrowane z Windows (Active Directory) |
# TYPE DATABASE USER ADDRESS METHOD
host szkola all 10.0.20.0/24 ldap ldapserver=ldap.szkola.local ldapprefix="uid=" ldapsuffix=",ou=ludzie,dc=szkola,dc=local" ldaptls=1
Limity sesji i konta techniczne
CREATE USER 'raport'@'10.0.20.%' IDENTIFIED BY '...'
WITH MAX_USER_CONNECTIONS 5 MAX_QUERIES_PER_HOUR 5000;
SET GLOBAL wait_timeout = 600; -- zamykanie bezczynnych sesji po 10 minutach
- limity (liczba połączeń, zapytań na godzinę, czas bezczynności) ograniczają skutki przejęcia konta i nadużyć,
- okna czasowe (dostęp tylko w godzinach pracy dla kont technicznych) realizuje się zwykle poza SZBD: modułem PAM (
pam_time), regułami zapory lub zdarzeniami, które blokują i odblokowują konto, - konta techniczne (kopie, raporty) powinny być osobne, z minimalnymi prawami i ograniczonym adresem.
Jaką korzyść daje integracja bazy z Active Directory?
Pokaż przykładową odpowiedź
Centralne zarządzanie kontami: wyłączenie konta w AD odcina dostęp do bazy, a grupy w AD mogą wyznaczać role. Zmniejsza się liczba osobnych haseł i błędów administracyjnych.
Po co ustawia się limity połączeń i zapytań dla kont raportowych?
Pokaż przykładową odpowiedź
Aby ograniczyć skutki nadużycia lub przejęcia konta (np. masowy eksport danych, przeciążenie serwera) oraz zapobiec zajmowaniu wszystkich połączeń przez jedno konto.
Lekcja 26 · Część E
Szyfrowanie: w spoczynku i w ruchu
| Rodzaj | Chroni przed | Sposoby |
|---|---|---|
| W spoczynku (at rest) | odczytem danych z dysku lub kopii po kradzieży nośnika | szyfrowanie tabel i dzienników przez SZBD (TDE), szyfrowanie dysku (LUKS, BitLocker), szyfrowanie kopii |
| W ruchu (in transit) | podsłuchem i zmianą danych w sieci | TLS 1.2/1.3 między klientem a serwerem, wymuszenie szyfrowanych połączeń |
| Na poziomie aplikacji / kolumn | dostępem administratorów bazy do konkretnych danych | szyfrowanie wartości w aplikacji (AES-GCM), klucze w magazynie kluczy (KMS) |
# konfiguracja
[mysqld]
plugin_load_add = file_key_management
file_key_management_filename = /etc/mysql/encryption/keyfile.enc
innodb_encrypt_tables = ON
innodb_encrypt_log = ON
ssl_ca = /etc/mysql/tls/ca.pem
ssl_cert = /etc/mysql/tls/server-cert.pem
ssl_key = /etc/mysql/tls/server-key.pem
tls_version = TLSv1.2,TLSv1.3
ALTER USER 'app'@'10.0.20.%' REQUIRE SSL;
SHOW STATUS LIKE 'Ssl_cipher'; -- w sesji klienta: czy połączenie jest szyfrowane
Szyfrowanie jest tak mocne, jak ochrona kluczy. Klucze trzymaj osobno od danych (KMS, moduł HSM), rotuj je i kontroluj dostęp. Szyfrowanie dysku nie chroni przed użytkownikiem, który ma dostęp do działającej bazy.
Przed czym chroni szyfrowanie danych w spoczynku?
Pokaż przykładową odpowiedź
Przed odczytem danych po zdobyciu nośnika lub kopii zapasowej (kradzież dysku, nieuprawniony dostęp do plików). Nie chroni przed legalnym użytkownikiem bazy z uprawnieniami do odczytu.
Jak wymusić, aby konto aplikacji łączyło się wyłącznie przez szyfrowane połączenie?
Pokaż przykładową odpowiedź
Przez opcję REQUIRE SSL (lub REQUIRE X509) na koncie oraz włączenie TLS na serwerze, najlepiej z minimalną wersją TLS 1.2.
Lekcja 27 · Część E
Audyt, wyzwalacze i monitorowanie zdarzeń
| Źródło | Zawartość |
|---|---|
| Dziennik błędów | awarie, uruchomienia, błędy zasobów |
| Log wolnych zapytań | kosztowne zapytania |
| Log ogólny (general log) | wszystkie zapytania (duży narzut, tylko na czas diagnostyki) |
| Wtyczka audytu | kto, skąd i co wykonał: MariaDB Audit Plugin, pgaudit w PostgreSQL |
[mysqld]
plugin_load_add = server_audit
server_audit_logging = ON
server_audit_events = CONNECT,QUERY_DDL,QUERY_DCL
server_audit_file_path = /var/log/mysql/audit.log
Wyzwalacz audytowy
CREATE TABLE oceny_audyt (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
ocena_id INT NOT NULL,
stara DECIMAL(2,1),
nowa DECIMAL(2,1),
zmienil VARCHAR(100) NOT NULL,
kiedy DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
DELIMITER //
CREATE TRIGGER trg_oceny_audyt AFTER UPDATE ON oceny
FOR EACH ROW
BEGIN
IF OLD.ocena <> NEW.ocena THEN
INSERT INTO oceny_audyt (ocena_id, stara, nowa, zmienil)
VALUES (OLD.id, OLD.ocena, NEW.ocena, CURRENT_USER());
END IF;
END//
DELIMITER ;
Nietypowe wzorce wskazujące na naruszenia
| Wzorzec | Możliwe znaczenie |
|---|---|
bardzo duże SELECT bez WHERE na tabelach z danymi osobowymi | eksfiltracja danych |
SELECT ... INTO OUTFILE, wywołania mariadb-dump przez nietypowe konto | kopiowanie danych poza bazę |
| zapytania w nocy lub z nowego adresu IP | przejęte konto |
wiele Access denied | zgadywanie haseł |
nowe konta, GRANT, zmiany DDL | eskalacja uprawnień, zmiana schematu |
Dla zdarzeń krytycznych (awaria serwera, brak miejsca, nietypowy wzrost nieudanych logowań) skonfiguruj automatyczne alarmy: wysyłanie logów do systemu SIEM i powiadomienia e-mail lub komunikator.
Do czego służy wyzwalacz audytowy?
Pokaż przykładową odpowiedź
Automatycznie zapisuje szczegóły zmian w ważnych tabelach (kto, kiedy, jaka stara i nowa wartość), co zapewnia rozliczalność i ułatwia wykrycie nieuprawnionych modyfikacji.
Który wzorzec w logach zapytań może wskazywać na próbę wyprowadzenia danych z bazy?
Pokaż przykładową odpowiedź
Bardzo duże zapytania odczytujące całe tabele z danymi osobowymi (zwłaszcza w nietypowych godzinach lub z nowego konta) oraz użycie eksportu do pliku.
Lekcja 28 · Część E
Utwardzanie serwera bazy danych
Utwardzanie bazy to systematyczne ograniczanie jej powierzchni ataku i usuwanie niebezpiecznych ustawień domyślnych.
| Obszar | Działanie |
|---|---|
| Konta | usuń anonimowych użytkowników i bazę test; sprawdź konta bez hasła i konta domyślne; ogranicz root do dostępu lokalnego |
| Sieć | bind-address na potrzebny interfejs, zapora: port bazy tylko z serwerów aplikacji; zablokuj nieużywane porty i funkcje sieciowe |
| Funkcje | wyłącz local_infile, ogranicz FILE i ustaw secure_file_priv, skip_symbolic_links, usuń nieużywane wtyczki i komponenty |
| Szyfrowanie | TLS co najmniej 1.2, szyfrowanie danych i kopii |
| Aktualizacje | stosuj poprawki bezpieczeństwa, używaj wspieranych wersji |
| Zgodność | porównaj konfigurację z benchmarkiem (np. CIS Benchmark dla MySQL/PostgreSQL) |
SELECT user, host, plugin, password_expired FROM mysql.user;
SELECT user, host FROM mysql.user WHERE user = '' OR host = '%'; -- anonimowi i konta z dowolnego adresu
SHOW FULL PROCESSLIST; -- aktywne sesje
SHOW GRANTS FOR 'app'@'10.0.20.%';
[mysqld]
bind-address = 10.0.20.10
local_infile = 0
skip_symbolic_links = 1
secure_file_priv = /var/lib/mysql-files
tls_version = TLSv1.2,TLSv1.3
sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
Co jakiś czas sprawdzaj: konta domyślne i nieużywane, aktywne sesje, zgodność z benchmarkiem, brakujące poprawki, wersje TLS i stan kopii. Zmiany wprowadzaj kontrolowanie i dokumentuj.
Dlaczego usuwa się anonimowych użytkowników i bazę testową?
Pokaż przykładową odpowiedź
Anonimowe konta i baza test dają dostęp bez prawdziwego uwierzytelnienia, co ułatwia atakującemu uzyskanie pierwszego kontaktu z bazą.
Do czego służy ustawienie local_infile = 0?
Pokaż przykładową odpowiedź
Wyłącza możliwość wczytywania plików z klienta poleceniem LOAD DATA LOCAL, co ogranicza ryzyko ataków polegających na odczycie plików z maszyny klienta lub serwera.
Zadanie praktyczne
Utwardzenie serwera bazy i audyt uprawnień
Zadanie
Na maszynie testowej z serwerem MariaDB lub PostgreSQL przeprowadź przegląd bezpieczeństwa i wprowadź poprawki.
Założenia
- Zapisz stan wyjściowy: lista kont i uprawnień, nasłuch sieciowy, ustawienia TLS, włączone funkcje (
local_infile). - Utwórz role
aplikacja,raportiadministrator_danychz zasadą najmniejszych uprawnień oraz konta przypisane do tych ról. - Wymuś TLS dla konta aplikacji, ogranicz adresy kont i ustaw limity połączeń.
- Dodaj wyzwalacz audytowy dla wybranej tabeli i włącz wtyczkę audytu. Zademonstruj wpis po zmianie danych.
- Usuń konta domyślne i zbędne funkcje, ustaw nasłuch i zaporę; powtórz przegląd i opisz różnice (raport 1–2 strony).
Ocenie podlegać będzie
- poprawny model ról i uprawnień,
- wymuszone szyfrowanie połączeń,
- działający audyt (wyzwalacz i dziennik),
- poprawne utwardzenie konfiguracji,
- czytelny raport przed i po.
Wskazówki
- Korzystaj z poleceń w lekcjach 23–28.
- Zmiany w konfiguracji wykonuj po kopii pliku i testuj ponowne uruchomienie serwera.
- Nie używaj kont i haseł z innych systemów.
Część F
SQL Injection
Mechanizm i rodzaje ataku, identyfikacja podatnego kodu, zapytania przygotowane, walidacja, minimalne uprawnienia i testy bezpieczeństwa.
Lekcja 29 · Część F
SQL Injection: mechanizm i skutki
SQL Injection (wstrzyknięcie SQL) to atak, w którym dane wprowadzone przez użytkownika są interpretowane jako część polecenia SQL. Powstaje, gdy aplikacja buduje zapytanie przez sklejanie tekstów z danymi z formularza, adresu URL, nagłówka lub ciasteczka. Znajduje się w kategorii Injection listy OWASP Top 10.
login = request.form["login"]
haslo = request.form["haslo"]
zapytanie = "SELECT id FROM uzytkownicy WHERE login = '" + login + "' AND haslo = '" + haslo + "'"
cursor.execute(zapytanie)
# Dane wpisane w formularzu: login: admin' -- hasło: dowolne
# Zapytanie po sklejeniu:
SELECT id FROM uzytkownicy WHERE login = 'admin' -- ' AND haslo = 'dowolne'
# „--” zaczyna komentarz: sprawdzenie hasła zostało pominięte
| Rodzaj | Opis |
|---|---|
| In-band (error-based, UNION) | wynik lub błędy zwracane w odpowiedzi aplikacji; UNION dołącza dane z innych tabel |
| Blind (boolean, time-based) | aplikacja nie pokazuje wyniku; atakujący wnioskuje z różnic w odpowiedzi lub w czasie |
| Out-of-band | dane przekazywane innym kanałem (np. zapytanie sieciowe z serwera bazy) |
| Drugiego rzędu (second-order) | złośliwy tekst zostaje zapisany w bazie i zadziała dopiero przy późniejszym użyciu |
Skutki
- obejście logowania i uzyskanie dostępu do cudzych kont,
- odczyt całej bazy (dane osobowe, hasła, dane finansowe),
- zmiana lub usunięcie danych,
- przy nadmiernych uprawnieniach konta bazy: odczyt plików, wykonanie poleceń na serwerze, przejęcie hosta.
Przykłady służą do rozpoznania i naprawy luk. Testowanie cudzych aplikacji bez zgody jest przestępstwem. Ćwiczenia wykonuj wyłącznie na własnych lub celowo podatnych aplikacjach w izolowanym laboratorium.
Na czym polega SQL Injection?
Pokaż przykładową odpowiedź
Dane wejściowe użytkownika są wklejane do tekstu zapytania SQL i interpretowane jako jego część, więc atakujący może zmienić logikę zapytania (np. pominąć sprawdzenie hasła lub odczytać inne dane).
Jaki jest skutek wprowadzenia loginu admin' -- w podatnym zapytaniu z kluczem login/hasło?
Pokaż przykładową odpowiedź
Znaki „--” rozpoczynają komentarz SQL, więc warunek na hasło zostaje pominięty i atakujący może zalogować się jako admin bez hasła.
Lekcja 30 · Część F
Identyfikacja miejsc podatnych na wstrzykiwanie SQL
Podatności szuka się w kodzie źródłowym (przegląd kodu, analiza statyczna) i w działającej aplikacji. Wzorzec jest zawsze ten sam: dane od użytkownika trafiają do zapytania bez parametryzacji.
| Wzorzec w kodzie | Dlaczego ryzykowny |
|---|---|
sklejanie tekstu: "... WHERE id = " + id, f"...{login}..." | dane stają się częścią polecenia |
interpolacja zmiennych w PHP: "SELECT ... '$login'" | jak wyżej |
dynamiczny ORDER BY lub nazwa kolumny z parametru | parametry zapytania nie obejmują identyfikatorów |
listy IN (...) budowane z tekstu | łatwo pominąć wiązanie wartości |
surowe zapytania w ORM (raw, nativeQuery) | ORM nie chroni przed konkatenacją |
| dane zapisane wcześniej, użyte później w zapytaniu | wstrzyknięcie drugiego rzędu |
grep -rnE "(query|execute|exec)\(.*(\+|\$|\{)" --include=*.py --include=*.php --include=*.java .
grep -rnE "SELECT .*\$_(GET|POST|REQUEST)" --include=*.php .
W aplikacji działającej źródłem wejścia są wszystkie dane od klienta: pola formularzy, parametry URL, nagłówki HTTP (np. User-Agent), ciasteczka, treść JSON. Każde z nich trzeba traktować jako niezaufane.
Dlaczego nazwy kolumn w ORDER BY nie można przekazać jako zwykły parametr zapytania?
Pokaż przykładową odpowiedź
Parametry zapytania zastępują wartości, a nie identyfikatory (nazwy kolumn i tabel). Dlatego dynamiczny ORDER BY trzeba zbudować z listy dozwolonych wartości (whitelist).
Wymień trzy źródła niezaufanych danych w aplikacji WWW inne niż pola formularza.
Pokaż przykładową odpowiedź
Parametry adresu URL, nagłówki HTTP (np. User-Agent) i ciasteczka; także dane JSON oraz dane wcześniej zapisane w bazie.
Lekcja 31 · Część F
Parametryzacja zapytań (zapytania przygotowane)
Najskuteczniejszą obroną jest parametryzacja: struktura zapytania i dane są przekazywane oddzielnie. Serwer najpierw otrzymuje wzór zapytania z miejscami na parametry, a wartości traktuje wyłącznie jako dane, nigdy jako kod SQL.
$st = $pdo->prepare('SELECT id, imie FROM uzytkownicy WHERE login = :login AND aktywny = 1');
$st->execute([':login' => $login]);
$uzytkownik = $st->fetch();
cursor.execute("SELECT id FROM uzytkownicy WHERE login = %s AND aktywny = %s", (login, 1))
PreparedStatement ps = conn.prepareStatement("SELECT id FROM uzytkownicy WHERE login = ?");
ps.setString(1, login);
ResultSet rs = ps.executeQuery();
DOZWOLONE = {"nazwisko": "nazwisko", "klasa": "klasa_id", "data": "utworzono"}
kolumna = DOZWOLONE.get(request.args.get("sort"), "nazwisko")
cursor.execute(f"SELECT imie, nazwisko FROM uczniowie ORDER BY {kolumna}") # kolumna pochodzi z naszej listy, nie od użytkownika
Nawet jeśli użytkownik wpisze ' OR '1'='1, serwer porówna kolumnę z tym tekstem, a nie wykona go jako polecenie. W PDO ustaw ATTR_EMULATE_PREPARES na false, aby korzystać z prawdziwych zapytań przygotowanych serwera.
Co zmienia się w sposobie przesyłania zapytania, gdy użyjemy zapytań przygotowanych?
Pokaż przykładową odpowiedź
Szkielet zapytania i wartości parametrów są przekazywane oddzielnie, więc wartości są traktowane wyłącznie jako dane i nie mogą zmienić struktury zapytania.
Jak bezpiecznie obsłużyć sortowanie według kolumny wybranej przez użytkownika?
Pokaż przykładową odpowiedź
Przez porównanie wartości z listą dozwolonych kolumn (whitelist) i użycie nazwy kolumny z tej listy, a nie tekstu przesłanego przez użytkownika.
Lekcja 32 · Część F
Walidacja, minimalne uprawnienia i ograniczanie skutków
Parametryzacja jest podstawą, ale bezpieczna aplikacja stosuje obronę warstwową:
| Warstwa | Działanie |
|---|---|
| Parametryzacja | podstawowa ochrona przed SQL Injection |
| Walidacja wejścia | sprawdzanie typu, długości, formatu i zakresu po stronie serwera (np. identyfikator to liczba całkowita, e-mail ma poprawny format); whitelist zamiast listy zakazanych znaków |
| Kodowanie i sanityzacja | oczyszczanie danych tam, gdzie parametryzacja nie jest możliwa; escaping jest ostatecznością, nie zamiennikiem parametrów |
| Minimalne uprawnienia konta aplikacji | brak DROP, FILE, praw administratora; osobne konto tylko do odczytu |
| Procedury składowane | pomagają, o ile same nie budują zapytań z tekstu |
| Obsługa błędów | użytkownik widzi ogólny komunikat, szczegóły błędu SQL trafiają do dziennika |
| WAF i monitorowanie | dodatkowa warstwa wykrywania, nie zastępuje poprawek w kodzie |
CREATE USER 'app_szkola'@'10.0.20.5' IDENTIFIED BY '...';
GRANT SELECT, INSERT, UPDATE ON szkola.oceny TO 'app_szkola'@'10.0.20.5';
GRANT SELECT ON szkola.uczniowie TO 'app_szkola'@'10.0.20.5';
Komunikaty SQL pokazywane użytkownikowi (nazwy tabel, zapytania, wersja serwera) ułatwiają atak. W środowisku produkcyjnym wyłącz szczegółowe komunikaty i zapisuj je tylko w dzienniku.
Dlaczego nie wystarczy filtrowanie apostrofów w danych wejściowych?
Pokaż przykładową odpowiedź
Listy zakazanych znaków są łatwe do obejścia (kodowania, inne konstrukcje), a nie wszystkie ataki używają apostrofów. Pewną ochroną jest parametryzacja, a walidacja whitelist jest uzupełnieniem.
Jak ograniczenie uprawnień konta aplikacji zmniejsza skutki udanego SQL Injection?
Pokaż przykładową odpowiedź
Atakujący może wykonać tylko operacje dostępne dla konta: bez DROP, FILE czy praw administratora nie usunie tabel ani nie odczyta plików serwera, a szkody ograniczą się do wskazanych tabel.
Lekcja 33 · Część F
Testowanie pod kątem SQL Injection i weryfikacja poprawek
Testy bezpieczeństwa sprawdzają, czy aplikacja jest odporna na wstrzykiwanie SQL. Wykonuje się je wyłącznie za zgodą właściciela i w środowisku testowym, np. na celowo podatnej aplikacji w izolowanym laboratorium.
| Rodzaj testu | Opis |
|---|---|
| Przegląd kodu (SAST) | wyszukiwanie sklejania tekstów w zapytaniach ręcznie i narzędziami analizy statycznej |
| Testy ręczne | wprowadzenie w pola apostrofu ' lub cudzysłowu i obserwacja błędów lub zmian w odpowiedzi, proste warunki logiczne (' OR '1'='1) w środowisku testowym |
| Testy automatyczne (DAST) | skanery aplikacji webowych i narzędzia specjalistyczne używane w laboratorium, np. sqlmap, wyłącznie na zgodnych celach |
| Testy jednostkowe i regresji | dodawanie przypadków ze „złośliwymi” danymi do zestawu testów, aby poprawka nie została cofnięta |
def test_logowanie_odporne_na_sqli(baza_testowa):
wynik = zaloguj("admin' OR '1'='1", "dowolne")
assert wynik is None # logowanie nie może się udać
def test_logowanie_poprawne(baza_testowa):
assert zaloguj("anna", "PoprawneHaslo!1") is not None
Naprawa i weryfikacja
- napraw przyczynę (parametryzacja), a nie objawy (filtr znaków),
- ponów ten sam test, który wykrył lukę, i sprawdź, że już nie działa,
- dodaj test regresji i opisz zmianę w dokumentacji,
- zbadaj podobne miejsca w kodzie (ta sama luka często powtarza się w wielu zapytaniach).
Jak sprawdzisz, że poprawka usunęła podatność na SQL Injection?
Pokaż przykładową odpowiedź
Powtarzam test, który wykazał podatność, i potwierdzam, że atak już nie działa, a normalne funkcje aplikacji nadal działają. Dodaję test regresji do zestawu testów.
Dlaczego testy penetracyjne aplikacji wykonuje się za zgodą właściciela i w środowisku testowym?
Pokaż przykładową odpowiedź
Testy bez zgody mogą naruszać prawo (np. art. 267 k.k.), a na systemie produkcyjnym mogą uszkodzić dane lub zakłócić działanie usług.
Zadanie praktyczne
Naprawa podatnej aplikacji
Zadanie
Otrzymujesz prostą aplikację WWW (PHP lub Python) z formularzem logowania i wyszukiwarką uczniów, w której zapytania SQL są budowane przez sklejanie tekstów. Pracujesz w izolowanym laboratorium.
Założenia
- Znajdź w kodzie wszystkie miejsca podatne na SQL Injection i opisz każde (źródło danych, zapytanie, ryzyko).
- Zademonstruj lukę w środowisku testowym (np. obejście logowania prostym wzorcem) i opisz skutek.
- Napraw kod: zapytania przygotowane, lista dozwolonych wartości dla sortowania, walidacja typów, ogólne komunikaty błędów.
- Utwórz osobne konto bazy dla aplikacji z minimalnymi uprawnieniami i zastosuj je.
- Napisz testy (przynajmniej 3 przypadki z „złośliwymi” danymi) potwierdzające, że luka została usunięta, i krótki raport.
Ocenie podlegać będzie
- pełna lista podatnych miejsc,
- poprawna parametryzacja i walidacja,
- konto z minimalnymi uprawnieniami,
- skuteczne testy regresji,
- jakość raportu.
Wskazówki
- Wzorce poszukiwań w lekcji 30, przykłady poprawek w lekcji 31.
- Pamiętaj o sortowaniu: nazwy kolumn nie przekazuje się jako parametry.
- Środowisko ćwiczeniowe musi być odizolowane od sieci szkolnej.
Część G
Bazy nierelacyjne (NoSQL)
Rodzaje i cechy NoSQL, MongoDB i CRUD, projekt dokumentów, bezpieczeństwo, NoSQL injection oraz migracja i dobór technologii.
Lekcja 34 · Część G
Bazy nierelacyjne: rodzaje, cechy i zastosowania
Bazy nierelacyjne (NoSQL) to systemy, które nie opierają się na tabelach z rygorystycznym schematem. Powstały, by obsługiwać duże ilości danych o zmiennej strukturze i skalować się poziomo (przez dodawanie serwerów).
| Rodzaj | Model danych | Przykłady | Zastosowania |
|---|---|---|---|
| Dokumentowe | dokumenty podobne do JSON | MongoDB, CouchDB | katalogi produktów, profile użytkowników, treści |
| Klucz-wartość | para klucz i wartość | Redis | pamięć podręczna, sesje, liczniki |
| Kolumnowe (rodzin kolumn) | szerokie wiersze, kolumny w rodzinach | Cassandra | dane czasowe, logi na dużą skalę |
| Grafowe | węzły i krawędzie | Neo4j | sieci powiązań, rekomendacje, wykrywanie oszustw |
| Cecha | Relacyjne (SQL) | Nierelacyjne (NoSQL) |
|---|---|---|
| Schemat | sztywny, zdefiniowany z góry | elastyczny, często dowolny |
| Relacje i złączenia | mocne (klucze obce, JOIN) | ograniczone; dane często osadzane w dokumentach |
| Spójność | pełne transakcje ACID | różnie; często spójność ostateczna (podejście BASE), nowsze systemy wspierają transakcje |
| Skalowanie | głównie pionowe | naturalnie poziome |
| Zapytania | SQL, złożone analizy | język specyficzny dla systemu |
Relacyjna baza: dane silnie powiązane, transakcje, raporty i złożone zapytania (finanse, ewidencje). NoSQL: elastyczna lub zmienna struktura, bardzo duża skala odczytów i zapisów, szybkie prototypowanie. Wiele systemów łączy oba podejścia (poliglotyczna persystencja).
Podaj przykład zastosowania bazy klucz-wartość.
Pokaż przykładową odpowiedź
Pamięć podręczna (cache) i przechowywanie sesji użytkowników, np. w systemie Redis, ze względu na bardzo szybkie odczyty po kluczu.
Czym różni się podejście do schematu w bazie dokumentowej i relacyjnej?
Pokaż przykładową odpowiedź
W relacyjnej schemat jest ściśle zdefiniowany z góry (kolumny i typy). W dokumentowej poszczególne dokumenty w kolekcji mogą mieć różną strukturę, co daje elastyczność, ale wymaga dyscypliny i walidacji.
Lekcja 35 · Część G
MongoDB: kolekcje, dokumenty i operacje CRUD
W MongoDB dane są zapisane w dokumentach (format BSON, odpowiednik JSON z dodatkowymi typami), pogrupowanych w kolekcje, a kolekcje w bazach. Każdy dokument ma unikalne pole _id. Pracę wykonuje się np. w konsoli mongosh.
| Pojęcie relacyjne | Odpowiednik w MongoDB |
|---|---|
| baza danych | baza danych |
| tabela | kolekcja |
| wiersz | dokument |
| kolumna | pole dokumentu |
| klucz główny | pole _id |
use szkola
// Create
db.uczniowie.insertOne({ imie: "Anna", nazwisko: "Kowalska", klasa: "2A",
oceny: [ { przedmiot: "Sieci", ocena: 5 }, { przedmiot: "Bazy", ocena: 4 } ] })
db.uczniowie.insertMany([ { imie: "Jan", nazwisko: "Nowak", klasa: "2A" } ])
// Read
db.uczniowie.find({ klasa: "2A" }, { imie: 1, nazwisko: 1, _id: 0 })
db.uczniowie.find({ "oceny.ocena": { $gte: 5 } })
db.uczniowie.countDocuments({ klasa: "2A" })
// Update
db.uczniowie.updateOne({ imie: "Anna" }, { $set: { klasa: "3A" } })
// Delete
db.uczniowie.deleteOne({ imie: "Jan" })
| Operator | Znaczenie |
|---|---|
$eq, $ne | równy, różny |
$gt, $gte, $lt, $lte | większy, większy lub równy, mniejszy, mniejszy lub równy |
$in, $nin | w zbiorze, poza zbiorem |
$and, $or | koniunkcja i alternatywa warunków |
$set, $inc, $push | ustawienie pola, zwiększenie liczby, dodanie do tablicy |
Jak w MongoDB odpowiada się pojęciu tabeli i wiersza?
Pokaż przykładową odpowiedź
Tabeli odpowiada kolekcja, a wierszowi dokument (w formacie BSON/JSON).
Który operator wybierze uczniów z oceną co najmniej 5 w tablicy ocen?
Pokaż przykładową odpowiedź
db.uczniowie.find({ "oceny.ocena": { $gte: 5 } }).
Lekcja 36 · Część G
Projektowanie dokumentów, JSON/BSON i walidacja schematu
W bazach dokumentowych projekt zależy od sposobu odczytu danych. Główna decyzja to osadzanie (embedding) czy odwołania (references).
| Podejście | Kiedy stosować | Przykład |
|---|---|---|
| Osadzanie | dane zawsze czytane razem, relacja 1:kilka, dane niezbyt rosnące | oceny ucznia wewnątrz dokumentu ucznia |
| Odwołania | dane duże lub rosnące bez ograniczeń, współdzielone przez wiele dokumentów | klasa_id wskazujący dokument klasy; logi w osobnej kolekcji |
- rozmiar jednego dokumentu jest ograniczony (w MongoDB do 16 MB), więc nie osadzaj list rosnących bez końca,
- BSON rozszerza JSON o typy: data,
ObjectId,Decimal128(dokładne liczby dziesiętne), dane binarne, - pola, po których szukasz, wymagają indeksów:
db.uczniowie.createIndex({ nazwisko: 1 }).
db.createCollection("uczniowie", {
validator: { $jsonSchema: {
bsonType: "object",
required: ["imie", "nazwisko", "klasa"],
properties: {
imie: { bsonType: "string", maxLength: 50 },
nazwisko: { bsonType: "string", maxLength: 80 },
klasa: { bsonType: "string", pattern: "^[1-5][A-Z]$" }
}
} },
validationAction: "error"
})
Brak sztywnego schematu nie oznacza braku dyscypliny. Walidacja schematu i indeksy pomagają zachować poprawność i wydajność danych w kolekcji.
Kiedy lepiej osadzić dane w dokumencie, a kiedy użyć odwołania?
Pokaż przykładową odpowiedź
Osadzam, gdy dane są zawsze czytane razem i nie rosną w nieskończoność. Używam odwołań dla danych dużych, rosnących lub współdzielonych przez wiele dokumentów.
Do czego służy walidacja $jsonSchema?
Pokaż przykładową odpowiedź
Wymusza strukturę i typy pól w dokumentach kolekcji (pola wymagane, typy, długości, wzorce), co chroni przed zapisem niepoprawnych danych.
Lekcja 37 · Część G
Bezpieczeństwo baz nierelacyjnych
Wiele wycieków danych dotyczyło baz NoSQL wystawionych do internetu bez uwierzytelniania. Starsze instalacje i niektóre domyślne konfiguracje nie wymuszały haseł, więc każdy, kto znalazł otwarty port, miał pełny dostęp.
net:
port: 27017
bindIp: 127.0.0.1
tls:
mode: requireTLS
certificateKeyFile: /etc/mongodb/tls/server.pem
security:
authorization: enabled
use szkola
db.createUser({
user: "app_szkola",
pwd: passwordPrompt(),
roles: [ { role: "readWrite", db: "szkola" } ]
})
| Obszar | Zalecenie |
|---|---|
| Uwierzytelnianie | włącz authorization, silne hasła, konta imienne dla administratorów |
| Role (RBAC) | najmniejsze uprawnienia: read lub readWrite na jedną bazę zamiast root |
| Sieć | bindIp na potrzebny interfejs, zapora, brak dostępu z internetu |
| Szyfrowanie | TLS w ruchu, szyfrowanie danych w spoczynku, szyfrowane kopie |
| Audyt i logi | włącz audyt (w edycjach wspierających) i monitoruj logowania oraz zmiany uprawnień |
| Aktualizacje | stosuj poprawki, używaj wspieranych wersji |
Wstrzykiwanie w bazach NoSQL
NoSQL injection polega na przekazaniu w danych wejściowych operatorów zapytania, które zmieniają jego sens. Gdy aplikacja wstawia do zapytania obiekt z żądania bez sprawdzenia typu, atakujący może przesłać zamiast hasła obiekt z operatorem.
// aplikacja: db.uzytkownicy.findOne({ login: req.body.login, haslo: req.body.haslo })
// dane w żądaniu: { "login": "admin", "haslo": { "$ne": null } }
// powstaje warunek: haslo różne od null, czyli prawdziwy dla każdego konta
- waliduj typy: login i hasło muszą być tekstem, a nie obiektem,
- nie przechowuj haseł w jawnej postaci (skrót z solą), więc porównuje się je w aplikacji, a nie w zapytaniu,
- unikaj operatorów wykonujących kod (
$where, JavaScript po stronie serwera), - używaj bibliotek mapujących i walidujących dane wejściowe.
Jaka była częsta przyczyna wycieków z baz MongoDB w internecie?
Pokaż przykładową odpowiedź
Serwer bazy był dostępny z internetu bez włączonego uwierzytelniania (brak haseł), więc każdy mógł odczytać i zmienić dane.
Na czym polega NoSQL injection z operatorem $ne?
Pokaż przykładową odpowiedź
Atakujący przesyła w polu zamiast tekstu obiekt z operatorem (np. { "$ne": null }), który sprawia, że warunek jest prawdziwy dla każdego konta. Obrona: walidacja typu danych wejściowych.
Lekcja 38 · Część G
Migracja i integracja SQL z NoSQL oraz dobór technologii
Dane często trzeba przenieść z bazy relacyjnej do dokumentowej (lub połączyć obie). Narzędzia AI mogą zaproponować mapowanie schematu, skrypt migracji i przykładowe zapytania, ale wynik wymaga dokładnej kontroli.
| Krok migracji | Co sprawdzić |
|---|---|
| Mapowanie schematu | tabele → kolekcje; klucze obce → osadzanie lub odwołania; tabele łączące → tablice |
| Typy danych | DECIMAL → Decimal128 (nie zwykła liczba zmiennoprzecinkowa); daty i strefy czasowe |
| Spójność | liczba rekordów przed i po migracji, losowe porównania, sumy kontrolne |
| Powiązania | czy odwołania wskazują istniejące dokumenty |
| Dane wrażliwe | nie wysyłaj prawdziwych danych do zewnętrznych narzędzi AI; użyj danych zanonimizowanych lub samego schematu |
| Bezpieczeństwo | uprawnienia kont migracji, szyfrowanie transportu, usunięcie plików pośrednich po migracji |
-- 1. eksport z SQL do JSON (np. przez zapytanie JSON_OBJECT lub skrypt w Pythonie)
-- 2. walidacja: liczba rekordów w tabeli = liczba dokumentów w kolekcji
-- 3. import do MongoDB: mongoimport --db szkola --collection uczniowie --file uczniowie.json --jsonArray
-- 4. porównanie wyników zapytań w obu systemach na próbce
Dobór technologii do wymagań
| Pytanie | Skłania ku relacyjnej | Skłania ku NoSQL |
|---|---|---|
| Struktura danych | stała, dobrze znana | zmienna, półstrukturalna |
| Transakcje wielu rekordów | kluczowe (finanse, rejestry) | rzadko potrzebne |
| Powiązania i raporty | złożone zapytania, JOIN-y | odczyt całych obiektów naraz |
| Skala | umiarkowana | bardzo duża, rozproszona |
| Kompetencje zespołu | dobra znajomość SQL | doświadczenie z danym systemem NoSQL |
Co należy zweryfikować po migracji danych z bazy relacyjnej do dokumentowej?
Pokaż przykładową odpowiedź
Zgodność liczby rekordów, poprawność typów (np. dokładne liczby dziesiętne, daty), spójność powiązań, wyniki zapytań na próbkach oraz brak utraty lub zniekształcenia danych.
Kiedy projekt powinien pozostać przy bazie relacyjnej?
Pokaż przykładową odpowiedź
Gdy dane są silnie powiązane, struktura jest stała, a kluczowe są transakcje ACID, spójność i złożone raporty (np. systemy finansowe, ewidencje).
Zadanie praktyczne
Projekt hybrydowy: baza relacyjna i dokumentowa
Zadanie
Dla aplikacji szkolnej z lekcji poprzednich zaprojektuj rozwiązanie wykorzystujące bazę relacyjną dla ewidencji (uczniowie, oceny) i dokumentową dla materiałów i aktywności (np. wiadomości, wpisy aktywności ucznia).
Założenia
- Uzasadnij podział: które dane trafią do SQL, a które do MongoDB (3–5 zdań).
- Zainstaluj MongoDB w środowisku testowym, włącz uwierzytelnianie i utwórz konto z rolą
readWritena jedną bazę. - Zaprojektuj dokumenty (osadzanie lub odwołania), dodaj walidację
$jsonSchemai indeks. - Wykonaj operacje CRUD i jedną migrację fragmentu danych z SQL do MongoDB; zweryfikuj liczbę rekordów.
- Opisz zagrożenia (NoSQL injection, otwarty dostęp) i zabezpieczenia oraz napisz test, który odrzuci hasło przesłane jako obiekt z operatorem.
Ocenie podlegać będzie
- sensowne uzasadnienie podziału danych,
- bezpieczna konfiguracja i konto,
- poprawny projekt dokumentów i walidacja,
- udana i zweryfikowana migracja,
- trafny opis zagrożeń i zabezpieczeń.
Wskazówki
- Skorzystaj z przykładów w lekcjach 35–38.
- Pamiętaj o wiązaniu serwera tylko z adresem lokalnym i TLS poza laboratorium.
- Do migracji użyj danych testowych.