Wszystkie wpisy

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 #

  1. Weryfikuj wszystko - nie tylko na wejściu, ale przy każdej akcji.
  2. Daj minimum uprawnień - pracownik kuchni ma dostęp tylko do kuchni. Nie do komnaty króla. I tylko w czasie swojej zmiany.
  3. 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:

  1. Silna identyfikacja - nie IP, nie obecność w sieci, tylko tożsamość (MFA dla ludzi, certyfikaty / SPIFFE ID dla usług).
  2. Least privilege - minimum uprawnień, granularnie (na zasób, na operację), nie “dostęp do całego folderu”.
  3. Continuous verification - kontekst requestu (device posture, lokalizacja, czas, anomaly) re-ewaluowany na bieżąco, nie tylko przy logowaniu.
  4. 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.json w 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