Zero Trust - zamek, w którym każdy pokój ma strażnika
W starym modelu broniliśmy fosy. Dziś każde drzwi w zamku mają własną kontrolę.
Stary zamek - most zwodzony i koniec #
Wyobraź sobie średniowieczny zamek. Dookoła fosa. Jedna brama, jeden most zwodzony, jeden strażnik.
Jak przejdziesz przez most i pokażesz mu dowód - wolno Ci chodzić wszędzie: do kuchni, do zbrojowni, do komnaty króla. Strażnik zakłada: „jeśli jesteś w środku, jesteś swój”.
To był stary model bezpieczeństwa IT. Firewall = most zwodzony. Jak przejdziesz → masz dostęp do wszystkiego w firmie.
Problem: Jak złodziej się przebierze za kuriera i przejdzie most - może okraść całe królestwo. W IT: jedna zainfekowana lampa USB potrafiła zabrać dane z całej firmy.
Nowy zamek - Zero Trust #
W zamku Zero Trust jest inaczej. Każdy pokój ma własnego strażnika. Wchodzisz do kuchni? Strażnik pyta o dowód. Wychodzisz, idziesz do zbrojowni? Ten sam strażnik patrzył na Ciebie 3 sekundy temu - ale i tak pyta o dowód jeszcze raz.
To brzmi paranoicznie. I o to chodzi. „Nigdy nie ufaj, zawsze weryfikuj”.
Trzy zasady #
- Weryfikuj wszystko - nie tylko na wejściu, ale przy każdej akcji.
- Daj minimum uprawnień - pracownik kuchni ma dostęp tylko do kuchni. Nie do komnaty króla. I tylko w czasie swojej zmiany.
- Zakładaj, że ktoś jest w środku - projektuj tak, jakby złodziej już przeszedł most. Niech mu się nie opłaca chodzić dalej.
W praktyce #
Zanim otwierasz plik w firmowym folderze, Twój komputer (jego „strażnik”) pyta:
- Czy masz świeży token logowania?
- Czy Twój laptop ma najnowsze łatki?
- Czy łączysz się z firmowej lokalizacji czy z Tajlandii?
- Czy w tej konkretnej chwili masz prawo czytać ten plik?
Jeśli cokolwiek śmierdzi - dostęp zablokowany. Nawet jeśli godzinę temu wszystko było OK.
Dlaczego to jest kluczowe w 2026? #
W erze pracy zdalnej nie ma już „wewnątrz firmy”. Twoje dane są wszędzie: na laptopie w domu, w chmurze, na telefonie. Nie ma jednej bramy, którą wystarczy obronić.
Dlatego każdy pokój - każdy mikroserwis, każda baza danych, każdy plik - ma dziś własnego strażnika.
Zasada dla programisty: Jak projektujesz system, nie zakładaj, że wewnętrzny ruch jest bezpieczny. Szyfruj wszystko. Loguj wszystko. Ufaj niczemu.
Perimetryczny model - i co z nim nie tak #
Klasyczny model bezpieczeństwa zakładał zaufaną strefę (firma, data center) ograniczoną firewallem. Raz zalogowany użytkownik / serwer wewnątrz miał praktycznie pełny dostęp. Założenie: ruch wewnętrzny jest bezpieczny.
Problemy:
- Lateral movement - atakujący po wejściu (phishing, zainfekowany laptop) porusza się po sieci bez dalszej weryfikacji.
- Praca zdalna - nie ma “wewnątrz”. Sieć VPN rozciąga perimetr w nieskończoność.
- Cloud / microservices - usługi nie siedzą za jednym firewallem, komunikują się między chmurami.
- Insider threat - zaufany użytkownik też może być problemem.
Zero Trust - założenia #
“Never trust, always verify”. Każdy request (użytkownika albo usługi) jest weryfikowany niezależnie od miejsca pochodzenia. Filary:
- Silna identyfikacja - nie IP, nie obecność w sieci, tylko tożsamość (MFA dla ludzi, certyfikaty / SPIFFE ID dla usług).
- Least privilege - minimum uprawnień, granularnie (na zasób, na operację), nie “dostęp do całego folderu”.
- Continuous verification - kontekst requestu (device posture, lokalizacja, czas, anomaly) re-ewaluowany na bieżąco, nie tylko przy logowaniu.
- Assume breach - projektuj jakby atakujący już był w środku. Segmentacja, szyfrowanie, logowanie wszędzie.
Najbardziej znana implementacja: Google BeyondCorp.
Autentykacja vs autoryzacja #
- AuthN (authentication) - kim jesteś. Token, hasło, certyfikat.
- AuthZ (authorization) - co możesz. Sprawdzane per-akcja.
Rozdzielenie jest krytyczne. Tokenu się nie “ufa w całości” - po weryfikacji podpisu dalej sprawdzasz claims przeciwko polityce.
JWT + ASP.NET Core minimal setup #
// title: Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opts =>
{
opts.Authority = "https://auth.example.com"; // OIDC discovery
opts.Audience = "orders-api";
opts.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.FromSeconds(30), // domyślne 5 min - za dużo
};
});
builder.Services.AddAuthorization(opts =>
{
opts.AddPolicy("OrdersRead", p => p
.RequireAuthenticatedUser()
.RequireClaim("scope", "orders:read"));
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/orders", () => "list")
.RequireAuthorization("OrdersRead");
app.Run();
Uwaga: Authority pobiera JWKS (klucze publiczne) z .well-known/openid-configuration. Rotacja kluczy jest obsługiwana automatycznie.
Service-to-service: mTLS + SPIFFE #
Dla komunikacji między usługami JWT też się sprawdza (np. client credentials), ale złotym standardem jest mutual TLS:
- Każda usługa ma własny certyfikat.
- Serwer weryfikuje certyfikat klienta, klient weryfikuje certyfikat serwera.
- Tożsamość zakorzeniona w certyfikacie, nie w tokenie przekazywanym w nagłówku.
W Kubernetes realizuje to service mesh (Istio, Linkerd) - sidecar proxy nawiązuje mTLS przezroczyście dla aplikacji. Tożsamości wydawane przez SPIRE zgodne z SPIFFE ID (spiffe://cluster.local/ns/default/sa/orders).
Policy engine - poza RBAC #
Prosty RBAC (role → uprawnienia) nie wystarcza przy Zero Trust - chcesz decyzje kontekstowe:
- Użytkownik ma rolę
admin, ALE request przychodzi z kraju nieoczekiwanego → deny. - Usługa ma prawo czytać sekret, ALE tylko w godzinach 8-18 dla tego workloadu → deny.
Narzędzia: OPA (Open Policy Agent) z językiem Rego, AWS IAM condition keys, Azure ABAC. Policy as code - wersjonowane w gicie, review’owane, testowane.
// Przykład - integracja z OPA przez HTTP sidecar
public class OpaAuthorizationHandler : AuthorizationHandler<OpaRequirement>
{
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context, OpaRequirement requirement)
{
var input = new {
user = context.User.Identity?.Name,
action = requirement.Action,
resource = requirement.Resource,
time = DateTimeOffset.UtcNow,
};
var allowed = await _opa.EvaluateAsync("data.app.authz.allow", input);
if (allowed) context.Succeed(requirement);
}
}
Short-lived credentials #
Długo żyjące hasła / klucze API to anti-pattern. Preferuj:
- Tokeny z krótkim TTL (15-60 min) + refresh token z rotacją.
- Certyfikaty wystawiane on-demand (Vault PKI, AWS ACM Private CA).
- Instance metadata service (IMDS) / workload identity - nigdy nie hardcoduj credentiali w obrazie.
Observability - nie ufaj, loguj #
- Wszystkie próby autoryzacji (sukces + porażka) do SIEM-u.
- Correlation ID przechodzący przez całą ścieżkę request-responsu (distributed tracing - OpenTelemetry).
- Alerty na anomalie: nietypowa lokalizacja, eksplozja 403, nowy klient.
Zasady dla developera #
- Nigdy nie ufaj IP źródłowemu jako wyznacznikowi tożsamości.
- Każde API publiczne wymaga authn + authz, nawet “wewnętrzne” (internal network w chmurze nie jest “prywatną siecią” w starym sensie).
- Deny by default - policy mówi co wolno, reszta zabroniona. Nie odwrotnie.
- Sekrety w Vault / KMS, nigdy w kodzie, nigdy w
appsettings.jsonw repo. - Loguj wrażliwe akcje (zmiany uprawnień, dostęp do PII, operacje na pieniądzach).
Skrótowo #
Zero Trust to przesunięcie od “sieć = granica zaufania” do “każdy request = decyzja oparta na tożsamości + kontekście”. W praktyce: OIDC/OAuth 2.0 dla użytkowników, mTLS + service mesh dla usług, policy engine dla kontekstowych decyzji, short-lived credentials, obserwowalność wszędzie.
Podobne wpisy
Komputer to bardzo szybki, ale całkowicie dosłowny wykonawca
Dlaczego kod musi być pisany z dokładnością, której nigdy nie żądamy od człowieka - i co z tego wynika dla całej branży.
Bazy wektorowe - biblioteka ułożona po nastrojach
Książki o miśkach leżą obok siebie, choć tytuły zaczynają się na różne litery. Tak działa baza wektorowa.
Docker to lunchbox z przegródkami
Makaron nie rozleje się na deser, bo każde danie ma swoją przegródkę. Tyle Docker.