Zrozumienie i Łagodzenie Podatności TOCTOU w Aplikacjach C#

Sławomir Zakrzewski

Podatności Time-of-Check to Time-of-Use (TOCTOU – czas sprawdzenia do czasu użycia) stanowią krytyczną klasę błędów bezpieczeństwa związanych z wyścigami (ang. race condition) w programach, które współpracują z zasobami zewnętrznymi. Podatności te pojawiają się gdy program weryfikuje bezpieczeństwo zasobu (np. pliku, katalogu lub obiektu systemowego), ale jego stan zmienia się przed faktycznym użyciem. W rezultacie powstaje luka, którą atakujący mogą wykorzystać do manipulowania zachowaniem programu.

W kontekście programowania w C# na platformie Windows, gdzie operacje na systemie plików oraz dynamiczne ładowanie kodu są powszechne, zrozumienie ryzyka TOCTOU jest kluczowe dla budowania bezpiecznych aplikacji.

Struktura Podatności TOCTOU

Podstawowy Mechanizm

Wzorzec podatności TOCTOU przebiega według przewidywalnej sekwencji:

  1. Faza Sprawdzania: Aplikacja weryfikuje właściwości zasobu, takie jak jego istnienie, uprawnienia lub zawartość.
  2. Okno Wyścigu: Atakujący modyfikuje zasób między momentem sprawdzenia, a jego użyciem.
  3. Faza Użycia: Aplikacja korzysta z zasobu na podstawie wcześniejszej weryfikacji, nieświadoma jego zmiany.

To właśnie luka czasowa między walidacją a wykorzystaniem zasobu umożliwia atakującym manipulację krytycznymi zasobami systemowymi.

W aplikacjach C# podatności TOCTOU najczęściej dotyczą operacji na plikach, ale mogą także obejmować dostęp do rejestru systemowego, zarządzania procesami oraz dynamicznego ładowania kodu.

Powierzchnie Ataku w Systemie Windows

System Windows wprowadza unikalne zagrożenia TOCTOU ze względu na swoją architekturę oraz sposób zarządzania systemem plików. Niektóre funkcje systemowe mogą ułatwiać atakującym manipulowanie zasobami między momentem sprawdzenia, a ich użyciem:

  • Punkty Podłączenia NTFS (ang. NTFS Junctions): Punkty podłączenia (reparse points) pozwalają atakującym przekierować operacje plikowe w inne miejsca systemu plików, umożliwiając eskalację uprawnień lub manipulację danymi. Jeśli aplikacja sprawdza początkową ścieżkę pliku, ale nie monitoruje zmian w jego lokalizacji, atakujący może podmienić docelowy zasób przed jego użyciem.
  • Transactional NTFS (TxF): Choć funkcja ta jest obecnie w dużej mierze wycofana, umożliwiała wykonywanie operacji na plikach w ramach transakcji. Jeśli aplikacja sprawdza stan pliku przed jego użyciem, atakujący mógł dokonać modyfikacji w ramach transakcji, która pozostawała niewidoczna aż do momentu zatwierdzenia.
  • Recykling Uchwytów (ang. Handle Recycling): Windows agresywnie zarządza ponownym użyciem uchwytów procesów i plików. Jeśli aplikacja niepoprawnie śledzi ich ważność, atakujący może wymusić zamknięcie prawidłowego uchwytu i utworzenie nowego obiektu w jego miejscu, co może prowadzić do operowania na nieoczekiwanym zasobie.
  • Pamięć Podręczna Walidacji Podpisów (ang. Signature Validation Cache): Windows przechowuje w pamięci podręcznej wyniki walidacji podpisów plików w celu poprawy wydajności. Jeśli aplikacja sprawdza podpis pliku przed jego wykonaniem, ale nie weryfikuje go ponownie w momencie użycia, atakujący może podmienić plik na niepodpisaną lub zmodyfikowaną wersję.
  • Ładowanie Asemblii (ang. Assembly Loading): Mechanizm .NET umożliwia dynamiczne ładowanie asemblii, co stanowi kolejną powierzchnię ataku TOCTOU. Jeśli aplikacja sprawdza integralność asemblii przed jej załadowaniem, ale nie wymusza zabezpieczeń w momencie wykonania, atakujący może podmienić bibliotekę w oknie wyścigu, prowadząc do wykonania dowolnego kodu.

Te przykłady ilustrują, w jaki sposób specyficzne cechy systemu Windows mogą zwiększać ryzyko TOCTOU, co wymaga od programistów stosowania bezpiecznych praktyk zarządzania plikami i zasobami.

Strategie łagodzące powinny koncentrować się na operacjach atomowych, restrykcyjnych politykach dostępu i walidacji w czasie rzeczywistym zamiast polegać wyłącznie na wstępnych sprawdzeniach.

Powszechne Przypadki TOCTOU w Kodzie C#

TOCTOU dotyczy głównie operacji na plikach, jednak podobne podatności występują przy animistycznym ładowaniu kodu uraz przy pracy z procesami. Poniżej znajdują się trzy przykłady różnych powierzchni ataku.

Obsługa Plików: Wyścig check-then-use

Jednym z najczęstszych błędów TOCTOU w C# jest wykonywanie sprawdzenia pliku oddzielnie od jego użycia:

if (File.Exists(filePath))
{
    // Okno wyścigu istnieje tutaj
    var content = File.ReadAllText(filePath);
}Code language: JavaScript (javascript)

W tym przypadku atakujący może usunąć, zmodyfikować lub podmienić plik między sprawdzeniem (File.Exists()) a jego odczytem (File.ReadAllText()). Może to prowadzić do nieoczekiwanego zachowania, eskalacji uprawnień lub wykonania dowolnego kodu.

Problem ten jest szczególnie groźny, gdy plik zawiera konfigurację, tokeny uwierzytelniające lub skrypty wykonywalne.

Dynamiczne Ładowanie Kodu: Podmiana Asemblii

Kolejny scenariusz wysokiego ryzyka występuje, gdy aplikacja weryfikuje zewnętrzną bibliotekę DLL przed jej wykonaniem:

Assembly LoadVulnerable(string path)
{
    if (ValidateAssemblyHash(path)) // Check
    {
        // Okno wyścigu: atakujący może podmienić plik DLL przed jego wykonaniem
        return Assembly.LoadFrom(path); // Użycie
    }
    throw new InvalidOperationException("Assembly validation failed.");
}Code language: JavaScript (javascript)

Ponieważ integralność asemblii jest sprawdzana przed jej załadowaniem, atakujący może podmienić plik między sprawdzeniem, a użyciem.

Jest to szczególnie problematyczne w systemach wykorzystujących wtyczki lub aplikacjach ładujących niezaufane biblioteki zewnętrzne, ponieważ może prowadzić do wykonania złośliwego kodu.

Zarządzanie Procesami: Wykonywanie Uprzywilejowanego Procesu

Podatności TOCTOU mogą również występować podczas sprawdzania istnienia procesu lub jego stanu przed podjęciem interakcji z nim:

if (Process.GetProcessesByName("targetProcess").Length > 0)
{
    // Okno wyścigu: proces może zostać zakończony lub zastąpiony
    Process.Start("targetProcess");
}Code language: JavaScript (javascript)

Między sprawdzeniem (GetProcessesByName()) a użyciem (Process.Start()) atakujący może zakończyć oryginalny proces i zastąpić go złośliwym.

Jeśli aplikacja zakłada, że pierwotny proces jest bezpieczny na podstawie początkowego sprawdzenia, może uruchomić niezaufany plik wykonywalny z podwyższonymi uprawnieniami, co prowadzi do naruszenia bezpieczeństwa.

Techniki Defensywne w Programowaniu

Łagodzenie podatności Time-of-Check to Time-of-Use (TOCTOU) wymaga przyjęcia technik programowania defensywnego, aby zapewnić bezpieczny dostęp do plików i ograniczyć wyścigi. Techniki te koncentrują się na bezpiecznym zarządzaniu uchwytami (handle management), stosowaniu zasady najmniejszych uprawnień (least privilege) oraz wykorzystywaniu mechanizmów blokady oportunistycznej (oplock), które razem pomagają zapobiegać wykorzystaniu podatności wynikających z wyścigów operacji na systemie plików.

Bezpieczne Zarządzanie Uchwytami

Zamiast wielokrotnego zamykania i ponownego otwierania uchwytów plików, bezpieczniejszym podejściem jest utrzymywanie trwałych uchwytów przez cały cykl operacji na pliku. Zapobiega to wyścigom, w których stan pliku może zostać zmieniony między operacjami. Utrzymując uchwyt otwarty od momentu walidacji do zakończenia przetwarzania, programiści mogą w ten sposób mieć pewność, że plik pozostanie niezmieniony, tym samym mogą zapobiec atakom TOCTOU.

Przykład – Użycie trwałego uchwytu pliku w C#:

using var fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
ValidateFileContents(fs);
ProcessFile(fs); // Ponowne użycie tego samego uchwytu plikuCode language: JavaScript (javascript)

Dostęp do Plików z Najniższymi Uprawnieniami

Zasada najniższych uprawnień (PoLP – Principle of Least Privilege) minimalizuje ryzyko wyścigów poprzez ograniczenie uprawnień do współdzielenia plików. Podczas otwierania pliku programiści powinni ograniczyć innym procesom możliwość wykonania krytycznych operacji na tym pliku.

Przykład – Ograniczanie dostępu do plików przy użyciu FileShare:

var fs = new FileStream(
    path, 
    FileMode.Open, 
    FileAccess.Read, 
    FileShare.Read | FileShare.Delete
);Code language: JavaScript (javascript)

Ochrona Plików za Pomocą Blokad Oportunistycznych

Blokady oportunistyczne (Oplocks – Opportunistic Locks) to mechanizm systemu Windows, który pozwala procesowi wykrywać i reagować na zmiany, zanim inny proces zmodyfikuje plik. Dzięki temu plik pozostaje spójny przez cały swój cykl życia.

Poprzez wykorzystanie mechanizmu nakładającego się wejścia/wyjścia (ang. overlapped I/O) i mechanizmów oplock, aplikacje mogą zoptymalizować dostęp do plików poprzez buforowanie operacji i wykrywanie prób modyfikacji pliku przez inny proces.

Przykład – Użycie Oplocks z Overlapped I/O w C#:

DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern SafeFileHandle CreateFile(
    string lpFileName,
    FileAccess dwDesiredAccess,
    FileShare dwShareMode,
    IntPtr lpSecurityAttributes,
    FileMode dwCreationDisposition,
    int dwFlagsAndAttributes,
    IntPtr hTemplateFile
);

const int FILE_FLAG_OVERLAPPED = 0x40000000;

var handle = CreateFile(
    path,
    FileAccess.Read,
    FileShare.Read,
    IntPtr.Zero,
    FileMode.Open,
    FILE_FLAG_OVERLAPPED,
    IntPtr.Zero
);

// Użycie oplock poprzez overlapped I/OCode language: JavaScript (javascript)

Kombinacja ponownego użycia uchwytów, restrykcyjnych uprawnień do plików oraz mechanizmów oplock znacząco zmniejsza ryzyko podatności TOCTOU.

Zapewniając spójność plików przez cały cykl przetwarzania, programiści mogą eliminować wyścigi i zapobiegać atakom wykorzystującym słabości systemu plików.

Strategie dla Programistów C# w Celu Łagodzenia Podatności TOCTOU

Programiści C# mogą minimalizować podatności TOCTOU poprzez zapewnienie nieprzerywalnego dostępu do plików, stosowanie wielowątkowego zarządzania zasobami, wykorzystanie blokad plików oraz wdrażanie operacji atomowych. Te strategie pomagają zachować integralność plików, zapobiegać wyścigom oraz zwiększać bezpieczeństwo przed nieautoryzowanymi modyfikacjami i wykorzystaniem luk.

Nieprzerywalny Dostęp do Plików

Częstym błędem TOCTOU jest oddzielne sprawdzanie istnienia pliku (File.Exists(filePath)) od jego otwarcia, co powoduje wyścig, w którym plik może zostać zmodyfikowany lub podmieniony po sprawdzeniu. Zamiast wykonywać operacje typu „sprawdź, a następnie użyj”, pliki powinny być otwierane w jednym kroku, a błędy dostępu obsługiwane za pomocą wyjątków.

try
{
    using var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
    var content = new StreamReader(fs).ReadToEnd();
}
catch (FileNotFoundException)
{
    // Obsługa braku pliku
}Code language: JavaScript (javascript)

Sprawdzanie istnienia pliku oraz jego otwarcie wykonane w ramach jednej operacji, eliminuje wystąpienie okna wyścigu, zapobiegając potencjalnym modyfikacjom plików pomiędzy sprawdzeniem, a użyciem. Zastosowanie FileMode.Open zapewnia, że operacja natychmiastowo zakończy się powodzeniem lub niepowodzeniem, bez możliwości interwencji atakującego. Jeśli plik powinien być tworzony, gdy nie istnieje, można użyć FileMode.OpenOrCreate.

Dlaczego File.Exists() jest niebezpieczne? Atakujący może zastąpić plik między sprawdzeniem istnienia, a jego otwarciem, co prowadzi do nieautoryzowanych modyfikacji lub wykonania złośliwego kodu.

Bezpieczne-Wątkowo Zarządzanie Zasobami

W aplikacjach wielowątkowych wyścigi mogą występować w obrębie tego samego procesu, prowadząc do nieoczekiwanych modyfikacji lub uszkodzenia plików. Gdy wiele wątków próbuje uzyskać dostęp do tego samego zasobu, brak synchronizacji może skutkować niespójnymi stanami lub utratą danych. Stosowanie mechanizmów synchronizacji zapewnia, że w danej chwili tylko jeden wątek może wykonywać operacje na pliku.

private static readonly object _fileLock = new object();

void ThreadSafeWrite(string path, string data)
{
    lock (_fileLock)
    {
        File.WriteAllText(path, data);
    }
}Code language: JavaScript (javascript)

Dzięki temu wszystkie operacje zapisu są wykonywane sekwencyjnie, zapobiegając modyfikacji tego samego pliku przez dwa wątki jednocześnie. Jednak to podejście chroni jedynie przed wyścigami w obrębie jednego procesu – jeśli wiele procesów próbuje w tym samym momencie modyfikować plik, należy użyć zewnętrznych mechanizmów blokowania, takich jak blokady plików.

Bezpieczna Obsługa Plików z Wykorzystaniem Blokad

W aplikacjach, w których wiele procesów uzyskuje dostęp do tego samego pliku, wyścigi można ograniczyć za pomocą blokad plików. Blokady te zapobiegają nieautoryzowanym modyfikacjom w trakcie użycia pliku, zapewniając, że inne procesy nie mogą zmienić jego zawartości.

Korzystając z FileStream.Lock(), proces może zablokować segment pliku (lub cały plik), uniemożliwiając jego modyfikację przez inne procesy:

using var fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
fs.Lock(0, fs.Length); // Zablokowanie całego plikuCode language: JavaScript (javascript)

Zapewnia to, że żaden inny proces nie może modyfikować ani zastępować pliku w czasie jego otwarcia. Po zakończeniu operacji zwolnienie blokady przy użyciu fs.Unlock(0, fs.Length) umożliwia innym procesom dostęp do pliku. Blokady plików są szczególnie przydatne w przypadku współdzielonych konfiguracji, logów czy zasobów wymagających zachowania integralności.

Ograniczenie: FileStream.Lock() nie zapobiega zmianie nazwy ani usunięciu pliku przez atakującego. Wykorzystanie FileOptions.DeleteOnClose może dodatkowo zmniejszyć te zagrożenia, zapewniając, że pliki tymczasowe są bezpiecznie usuwane po ich zamknięciu.

Operacje Atomowe na Plikach

Podatności TOCTOU występują, gdy stan pliku jest sprawdzany przed jego użyciem, co umożliwia atakującemu jego modyfikację lub podmianę w tym czasie. Bezpieczniejszym podejściem jest stosowanie operacji atomowych, które zapewniają, że plik jest zaktualizowany w pełni albo wcale.

Bezpośredni zapis do pliku niesie ryzyko niekompletnych aktualizacji w wyniku niespodziewanych przerw, co może prowadzić do uszkodzenia danych. Aby tego uniknąć, najlepszą praktyką jest zapisanie zmian do pliku tymczasowego, a następnie atomowa zamiana oryginalnego pliku. Metoda File.Replace() gwarantuje, że zawsze będzie istniał albo oryginalny (przed zamianą), albo nowy plik (po zamianie), zapobiegając niespójnym stanom.

void AtomicWrite(string filePath, string newData)
{
    string tempFile = Path.Combine(Path.GetDirectoryName(filePath), Path.GetRandomFileName());

    // Bezpieczne tworzenie i zapis do pliku tymczasowego
    using (var tempFs = new FileStream(tempFile, FileMode.CreateNew, FileAccess.Write, FileShare.None))
    using (var writer = new StreamWriter(tempFs))
    {
        writer.Write(newData);
        writer.Flush();
        tempFs.Flush();
    }

    // Atomowa zamiana oryginalnego pliku
    File.Replace(tempFile, filePath, null);
}Code language: JavaScript (javascript)

To podejście zapewnia, że plik nigdy nie pozostaje w stanie częściowo zapisanym, co chroni integralność danych. Ogranicza także ryzyko wyścigów, które mogłyby pozwolić na nieautoryzowane modyfikacje przed dokonaniem zamiany.

Jeśli konieczne jest zachowanie starszej wersji pliku, określenie ścieżki kopii zapasowej w File.Replace() pozwala na jej przywrócenie, minimalizując ryzyko przypadkowej utraty danych.

Narzędzia do Wykrywania oraz Przeciwdziałania

Skuteczne mechanizmy wykrywania i zapobiegania są kluczowe w łagodzeniu podatności TOCTOU, szczególnie w operacjach dostępu do plików. Różne narzędzia do analizy statycznej i dynamicznej mogą pomóc w identyfikacji oraz zapobieganiu takim problemom, zanim staną się one podatne na wykorzystanie. Poniżej przedstawiono przegląd najczęściej stosowanych narzędzi i technik.

Analiza Statyczna

Narzędzia do analizy statycznej pomagają wykrywać podatności TOCTOU na poziomie kodu źródłowego jeszcze przed uruchomieniem aplikacji. Analizują one kod w poszukiwaniu wzorców wskazujących na niebezpieczne sekwencyjne sprawdzanie i operacje na plikach.

  • Klocwork SV.TOCTOU.FILE_ACCESS Checker – Wykrywa przypadki, w których aplikacja sprawdza istnienie pliku przed wykonaniem na nim operacji, identyfikując potencjalne wyścigi.
  • Roslyn Analyzers – Niestandardowe reguły analizy kodu dla platformy .NET wykrywające niebezpieczne wzorce związane z użyciem File.Exists i File.Open, co pomaga w stosowaniu dobrych praktyk programowania.
  • CodeQL – Potężne narzędzie do analizy statycznej umożliwiające pisanie niestandardowych zapytań w celu wykrywania sekwencji TOCTOU w kodzie źródłowym. Pozwala na identyfikację wzorców, w których stan pliku jest sprawdzany, a następnie plik jest używany bez odpowiedniej synchronizacji.

Choć analiza statyczna pozwala wykryć wiele podatności na etapie tworzenia kodu, może ona nie wychwycić wyścigów, które manifestują się jedynie w określonych warunkach czasowych lub systemowych.

Aby uzupełnić analizę statyczną, konieczne jest również testowanie dynamiczne.

Ochrona w Czasie Wykonania

Chociaż analiza statyczna pomaga zapobiegać podatnościom przed wdrożeniem, mechanizmy ochrony w czasie wykonania dodają dodatkową warstwę zabezpieczeń, wykrywając i łagodząc podatności TOCTOU podczas działania aplikacji.

  • Śledzenie Uchwytów – Monitorowanie cyklu życia uchwytów plików w celu zapobiegania wyścigom. Dzięki zapewnieniu, że uchwyty plików pozostają ważne i niezmienne przez cały czas użycia, można ograniczyć ryzyko TOCTOU.
  • Minifiltry Systemu Plików – Filtry trybu jądra systemu plików, które wymuszają blokady operacyjne (oplock) w celu zapobiegania zmianom w plikach między momentem sprawdzenia, a użyciem. Jest to szczególnie skuteczne w ochronie krytycznych ścieżek systemowych.
  • Instrumentacja CLR – Dzięki integracji z mechanizmem uruchamiania .NET (Common Language Runtime – CLR) możliwe jest dynamiczne monitorowanie i ograniczanie niebezpiecznych operacji dostępu do plików, co pozwala na egzekwowanie polityk bezpieczeństwa.

Kompleksowe Testy Bezpieczeństwa

Oprócz narzędzi automatycznych regularne testowanie bezpieczeństwa jest kluczowe dla identyfikacji podatności TOCTOU, które mogą nie zostać wykryte przez analizę statyczną lub dynamiczną. Organizacje powinny uwzględniać następujące metody testowania w swoich procesach bezpieczeństwa:

  • Przeglądy Kodu – Eksperci ds. bezpieczeństwa powinni przeprowadzać ręczne audyty operacji na plikach, weryfikacji uprawnień i mechanizmów ładowania dynamicznego w celu identyfikacji wyścigów, które mogą zostać pominięte przez narzędzia automatyczne.
  • Fuzzing – Ponieważ podatności TOCTOU często zależą od precyzyjnego czasu wykonania, ich wykrycie poprzez analizę statyczną może być trudne. Techniki fuzzingu, takie jak wymuszone manipulacje systemu plików i testy wielowątkowe, mogą pomóc w detekcji potencjalnych podatności w rzeczywistych warunkach.
  • Testy Penetracyjne – Zespoły zajmujące się ofensywnym bezpieczeństwem powinny przeprowadzać symulowane ataki, próbując wykorzystać wyścigi poprzez podmiany plików, ataki z wykorzystaniem dowiązań symbolicznych i techniki wstrzykiwania procesów.
  • Ciągłe Skanowanie Bezpieczeństwa – Integracja automatycznych testów bezpieczeństwa z pipelanami CI/CD umożliwia wykrywanie podatności TOCTOU na wczesnym etapie cyklu życia aplikacji, zmniejszając koszty naprawy błędów.

Łącząc analizę statyczną, ochronę w czasie wykonania i aktywne testowanie bezpieczeństwa, organizacje mogą znacząco ograniczyć ryzyko podatności TOCTOU i poprawić ogólne bezpieczeństwo swoich aplikacji.

Regularne audyty bezpieczeństwa pozwalają na wykrycie nowych podatności w miarę rozwoju aplikacji, minimalizując ryzyko ataków TOCTOU w środowisku produkcyjnym.

Podsumowanie

Podatności TOCTOU w aplikacjach C# stanowią poważne zagrożenie bezpieczeństwa, szczególnie w środowiskach opartych na operacjach na systemie plików, zarządzaniu procesami i dynamicznym wykonywaniu kodu. Te luki wykorzystują wyścigi, które pojawiają się, gdy aplikacje sprawdzają stan zasobu przed jego użyciem, tworząc możliwość manipulacji zachowaniem systemu pomiędzy walidacją, a wykonaniem operacji.

Minimalizowanie ryzyka TOCTOU wymaga zastosowania wielopoziomowej strategii bezpieczeństwa, łączącej dobre praktyki kodowania, ochronę w czasie wykonania oraz ciągłe audyty bezpieczeństwa. Programiści powinni priorytetowo traktować operacje atomowe zamiast sekwencyjnych sprawdzeń, egzekwować ścisłe zasady kontroli dostępu i stosować kryptograficzną walidację integralności zasobów. Dodatkowo, mechanizmy takie jak śledzenie uchwytów, izolacja procesów i blokowanie plików mogą znacząco ograniczyć powierzchnię ataku.

Chociaż techniczne mechanizmy zabezpieczeń są kluczowe, bezpieczeństwo to proces ciągły. Proaktywne testy bezpieczeństwa, w tym analiza statyczna, fuzzing i testy penetracyjne, pomagają wykrywać wyścigi, zanim staną się one podatne na atak. W miarę ewolucji technik ataków, śledzenie zmian w modelu bezpieczeństwa systemu Windows oraz stosowanie najlepszych praktyk w zakresie bezpiecznego programowania pozostaje kluczowe dla utrzymania odpornych aplikacji.

Przyjęcie strategii obrony w głąb (ang. defense-in-depth) pozwala organizacjom wzmocnić swoje aplikacje przed podatnościami TOCTOU, jednocześnie zapewniając wydajność i niezawodność w nowoczesnym środowisku .NET.

Sławomir Zakrzewski
Offensive Security Engineer

Czy Twoja firma jest bezpieczna w sieci?

Dołącz do grona naszych zadowolonych klientów i zabezpiecz swoją firmę przed cyberzagrożeniami już dziś!

Zostaw nam swoje dane kontaktowe, a nasz zespół skontaktuje się z Tobą, aby omówić szczegóły i dopasować ofertę do Twoich potrzeb. Dbamy o pełną dyskrecję i poufność Twoich danych, dlatego możesz nam zaufać.

Chciałbyś od razu zadać pytanie? Odwiedź naszą stronę kontaktową.