Nordlet

← Blog

Tworzenie API księgowego odpornego na audyt

Praktyczny przewodnik po zabezpieczeniach i mechanizmach kontroli audytowej, jakich potrzebuje API finansowe, zanim holenderskie firmy powierzą mu swoje księgi.

Nordlet Team · · 10 min. czytania

API księgowe, które zwraca kod 200 OK na żądanie zapisu płatności, nie mówi Ci właściwie nic. Nie potwierdza ono, że księga główna zarejestrowała wpis, że podmiot posiadał uprawnienia do jego wykonania, że kwota przeszła walidację ani że ktokolwiek o odpowiednich uprawnieniach zatwierdził operację. W przypadku firm w Holandii, gdzie urząd skarbowy wymaga, aby dokumentację dało się zrekonstruować i przechowywać przez siedem lat, ta przepaść między komunikatem „żądanie powiodło się” a stwierdzeniem „księgi są poprawne” jest miejscem, w którym audyty kończą się niepowodzeniem.

Gotowość na audyt (audit-ready) to cel projektowy o charakterze operacyjnym, a nie certyfikat. Oznacza ona, że API zachowuje kompletny łańcuch zdarzeń: uwierzytelniony podmiot, autoryzowana akcja, zweryfikowana zmiana stanu, zatwierdzenie, wynik w księdze głównej oraz dowody odporne na manipulacje. Wszystkie poniższe wskazówki dotyczą celowego budowania tego łańcucha, zamiast polegania na założeniu, że uwierzytelnianie i protokół TLS w pełni go zabezpieczają.

Co sprawia, że API księgowe jest „gotowe na audyt”

Audytor rekonstruujący transakcję musi odpowiedzieć na określoną sekwencję pytań. Kto ją zainicjował? Czy miał do tego uprawnienia? Czy zmiana przeszła walidację księgową? Kto ją zatwierdził? Co zarejestrowała księga główna? Czy ten zapis można zmodyfikować po fakcie bez pozostawienia śladu?

Jeśli Twoje API nie potrafi odpowiedzieć na wszystkie sześć pytań na podstawie własnych logów i modelu danych, nie jest gotowe na audyt, bez względu na to, jak przejrzysty wydaje się kod. Większość systemów API radzi sobie dobrze z pierwszym pytaniem, a z pozostałymi słabo.

Kontrole, które domykają tę lukę, dzielą się na kilka grup: jawny stan księgowy, wielowarstwowa autoryzacja, uwierzytelnianie o ograniczonym zakresie (scoped authentication), operacje zapisu o właściwości idempotencji, walidacja po stronie serwera, podział obowiązków (segregation of duties) oraz rejestrowanie zdarzeń odporne na manipulacje. Elementy te wzajemnie się uzupełniają. Słaba autoryzacja podważa wiarygodność silnego logu audytowego, ponieważ nie możesz już ufać, że zalogowany podmiot miał jakiekolwiek uzasadnienie biznesowe do wykonania danej akcji.

Modeluj stany księgowe przed zamodelowaniem punktów końcowych

Najczęstszym błędem projektowym jest traktowanie każdego rekordu finansowego jako obiektu z pełną swobodą edycji i operacjami create, read, update, delete. Księgowość tak nie działa, a audytor natychmiast zauważy, jeśli zaksięgowany wpis dziennika będzie można bezgłośnie nadpisać.

Każdy zasób o znaczeniu finansowym wymaga jawnej maszyny stanów. Wpis dziennika lub płatność zazwyczaj przechodzą przez następujące etapy:

draft → submitted → approved → posted → settled

Korekty nie cofają tego przebiegu. Błędny, zaksięgowany wpis otrzymuje powiązane przeciwstawne księgowanie (reversal) lub korektę kompensującą. Oryginał pozostaje nienaruszony.

Praktyczny zestaw zasad:

  • Wersje robocze (drafts) mogą być edytowane przez upoważnionych użytkowników.
  • Złożone rekordy mogą być edytowane wyłącznie przez role posiadające odpowiednie uprawnienia lub mogą zostać cofnięte do stanu wersji roboczej.
  • Zatwierdzone rekordy wymagają kontrolowanej modyfikacji oraz ponownego zatwierdzenia.
  • Zaksięgowane rekordy nie mogą być nadpisywane ani usuwane, a jedynie korygowane lub odwracane.
  • Zmiany w zamkniętych okresach wymagają podwyższonych uprawnień, podania przyczyny oraz wygenerowania zdarzenia w dzienniku audytu.

Próba wykonania niedozwolonego przejścia, na przykład edycji zaksięgowanego wpisu, powinna zwrócić stabilny błąd i nadal zapisać zdarzenie w logu audytu. Nieudana próba również stanowi dowód.

W tym miejscu niezmienna księga podwójnego zapisu udowadnia swoją wartość. Księga główna Nordlet została zaprojektowana tak, że każda transakcja trafia jako zbalansowany, trwała wpis, przy czym bilansowanie jest wymuszane przez wyzwalacz bazy danych (database trigger) w momencie zatwierdzenia (commit), a korekty odbywają się poprzez odwrócenie zapisów, a nie ich ponowne przepisywanie. To ograniczenie wydaje się restrykcyjne podczas prac programistycznych, a w trakcie audytu staje się elementem, któremu Twój księgowy ufa.

Autoryzuj każde żądanie, a nie tylko je uwierzytelniaj

Uwierzytelnianie udowadnia, kto wywołuje system. Nie mówi jednak nic o tym, czy ten wywołujący może dotknąć konkretnej faktury, dziennika lub podmiotu prawnego. Projekt OWASP API Security umieszcza uszkodzoną autoryzację na poziomie obiektów (BOLA) na samym początku listy zagrożeń właśnie dlatego, że interfejsy API ujawniają identyfikatory obiektów, co ułatwia dostęp między różnymi najemcami (cross-tenant), jeśli brakuje odpowiednich kontroli.

Każde żądanie wysyłane do API finansowego powinno być autoryzowane w oparciu o kombinację czynników, a nie sam token:

  • uwierzytelniony podmiot,
  • aplikacja klienta lub konto usługowe,
  • najemca (tenant),
  • podmiot prawny,
  • identyfikator zasobu,
  • żądana operacja,
  • bieżący stan zasobu,
  • kwotę lub próg transakcyjny.

Dwie zasady praktyczne mają większe znaczenie niż pozostałe. Po pierwsze, wyprowadzaj kontekst najemcy i użytkownika z uwierzytelnionej tożsamości i odrzucaj wszelkie deklaracje (claims) dotyczące najemcy lub roli dostarczone przez klienta, jeśli są z nią sprzeczne. Wywołujący nigdy nie powinien mieć możliwości samodzielnego deklarowania, w imieniu której spółki działa. Po drugie, stosuj zasadę domyślnej odmowy (deny-by-default). Jeśli żadna polityka wyraźnie nie przyznaje uprawnienia do akcji, operacja kończy się niepowodzeniem.

Testuj oba kierunki. Dostęp horyzontany, w którym jeden najemca uzyskuje dostęp do danych innego najemcy, oraz dostęp wertykalny, w którym zwykły użytkownik dociera do funkcji zatwierdzania, księgowania lub eksportu. Oba scenariusze są powszechne i oba mają katastrofalne skutki w danych księgowych.

Model ról w Nordlet, uwzględniający role właściciela, administratora, księgowego, menedżera, dewelopera i obserwatora w ramach wielu firm przypisanych do jednego konta, istnieje po to, aby ten podział był egzekwowalny, a nie tylko deklaratywny. Rola dewelopera, która pozwala tworzyć projekty faktur, nie powinna umożliwiać realizacji płatności.

Ograniczaj zakres tokenów do zdolności biznesowych, a nie czasowników CRUD

W przypadku dostępu delegowanego korzystaj z aktualnych wytycznych dotyczących bezpieczeństwa OAuth 2.0, zamiast polegać na wielokrotnie używanych hasłach lub szerokich, statycznych kluczach API. Dokument RFC 9700 stanowi aktualną najlepszą praktykę (Best Current Practice) i stawia jasne wymagania wobec klientów oraz serwerów autoryzacyjnych: zweryfikowane adresy URI przekierowania, PKCE tam, gdzie ma to zastosowanie, weryfikację wystawcy i odbiorcy, krótkie czasy życia tokenów dostępu oraz rotację tokenów odświeżania wraz z ich unieważnianiem.

Decyzja dotycząca zakresów (scopes) to moment, w którym uwidacznia się myślenie specyficzne dla księgowości. Ogólne zakresy, takie jak read i write, są niemal bezużyteczne z punktu widzenia podziału obowiązków. Zakresy powinny odzwierciedlać konkretne uprawnienia operacyjne:

  • journal:create i journal:approve to osobne zakresy.
  • Utworzenie instrukcji płatności nie daje uprawnienia do jej zwolnienia.
  • Uprawnienie do eksportu jest odrębne od uprawnienia do odczytu, ponieważ masowy eksport stanowi ścieżkę wycieku danych.

Bądź precyzyjny co do tego, w którym miejscu na tej skali znajduje się dany produkt. Klucze API w Nordlet zawierają zakresy przypisane do modułów, takie jak sales:read i sales:write, sprawdzane przy każdej akcji. Rozdziela to moduły oraz odczyt od zapisu, ale nie tworzenie od zatwierdzania – jeśli potrzebujesz w tym miejscu dokładniejszego podziału, egzekwuj go we własnej usłudze przed wywołaniem API.

W przypadku operacji o dużej wartości warto ocenić, czy zastosowanie ma profil bezpieczeństwa FAPI 2.0. Został on zaprojektowany dla interfejsów API chroniących poufne dane finansowe i oferuje silniejsze gwarancje niż bazowy protokół OAuth. Większość integracji księgowych dla małych i średnich przedsiębiorstw nie będzie potrzebować pełnego profilu, jednak procesy zwalniania płatności oraz łączenia z bankami to obszary, w których warto go rozważyć.

Bezhasłowe logowanie w Nordlet za pomocą jednorazowych linków wysyłanych e-mail eliminuję problem wielokrotnie używanych haseł w przypadku użytkowników ludzkich, co oznacza o jedną poświadczenie mniej do rotowania, wycieku czy wyłudzenia (phishing).

Zapewnij idempotencję zapisów finansowych

Sieci padają w trakcie trwania żądania. Klient wysyła instrukcję płatności, połączenie zostaje przerwane przed nadejściem odpowiedzi, a klient ponawia próbę. Bez odpowiedniej ochrony masz teraz dwie płatności.

Klucze idempotencji rozwiązują ten problem. Klient przesyła unikalny klucz wraz z każdą operacją zapisu, a serwer rejestruje klucz oraz wynik operacji. Ponowna próba z tym samym kluczem zwraca oryginalny wynik zamiast tworzyć drugi wpis. Nordlet obsługuje nagłówek Idempotency-Key przy każdym wywołaniu modyfikującym dane, odtwarzając zapisaną odpowiedź w przypadku ponowienia żądania z tym samym ładunkiem (payload) i odrzucając ten sam klucz ze zmienionym ładunkiem. Każde API księgowe, które pomija tę funkcję, prędzej czy później wygeneruje zduplikowane wpisy w księdze, które ktoś będzie musiał ręcznie rozwikłać.

Połącz to z walidacją po stronie serwera. Nigdy nie ufaj klientowi, że sprawdzi, czy strony Winien i Ma się bilansują, czy stawka VAT jest poprawna dla danego kraju i okresu oraz czy konto faktycznie istnieje. Walidacja odbywa się na serwerze, przed przejściem stanu, a odrzucony zapis skutkuje zwróceniem stabilnego błędu oraz zapisaniem zdarzenia w dzienniku audytu.

Dzienniki audytu odporne na manipulacje a holenderska retencja

Dziennik audytu to warstwa dowodowa, która ma inne wymagania niż zwykłe dane biznesowe. Powinien rejestrować decyzję o autoryzacji, wersję polityki, podmiot, obiekt, operację, poprzedni i nowy stan oraz kod przyczyny, nigdy natomiast nie powinien zapisywać sekretów ani pełnych danych uwierzytelniających płatności.

Kluczową właściwością jest odporność na manipulacje (tamper-evidence). Jeśli administrator może po cichu edytować lub usunąć wpisy w dzienniku audytu, log nie dowodzi niczego. Magazyn typu append-only (tylko do dopisywania), łańcuchy kryptograficzne lub przechowywanie z jednokrotnym zapisem (write-once) rozwiązują ten problem. Istotą jest to, aby każda modyfikacja była wykrywalna.

Retencja zasługuje na osobną politykę, niezależną od usuwania danych biznesowych. W Holandii ogólny okres przechowywania dokumentacji księgowej wynosi siedem lat, a w przypadku dokumentów dotyczących nieruchomości – dziesięć lat. Twoje standardowe procedury usuwania danych i realizacji praw wynikających z RODO nie mogą mieć możliwości zniszczenia rekordów ani dowodów audytowych, które nadal objęte są prawnym obowiązkiem retencji. Zdefiniuj zasady blokady prawnej (legal hold), archiwizacji i usuwania dowodów audytowych niezależnie, dopasowując je do jurysdykcji, w których prowadzisz działalność.

Blokada okresu (period locking) wspiera ten proces na poziomie księgowym. Po zamknięciu miesiąca zawarte w nim wpisy nie mogą ulec zmianie bez podwyższonych uprawnień oraz zarejestrowania przyczyny. Nordlet egzekwuje blokowanie okresów, dzięki czemu zamknięte miesiące pozostają zamknięte – odrzucając każde księgowanie z datą przypadającą wewnątrz zablokowanego okresu, nawet jeśli próba następuje przez API. Jest to jedna z pierwszych rzeczy, które sprawdza holenderski audytor.

Zabezpiecz krawędzie: webhooki, eksporty i integracje

Podstawowe API jest zazwyczaj najlepiej chronioną częścią systemu. Krawędzie systemu wyciekają.

Webhooki wiążą się z ryzykiem fałszowania żądań. Zdarzenie takie jak sale_invoice.paid powinno być podpisane, aby odbiorca mógł zweryfikować, że pochodzi od Ciebie. Odbiorcy powinni traktować dostarczanie komunikatów jako realizowane co najmniej raz (at-least-once) i używać identyfikatora zdarzenia do deduplikacji. Nordlet podpisywać każde doręczenie webhooka za pomocą sygnatury HMAC obliczonej na podstawie surowego ciała (body) żądania i ponawia nieudane doręczenia z użyciem wykładniczego wycofywania (exponential backoff). Odbiorca musi zatem zweryfikować sygnaturę i dokonać deduplikacji po identyfikatorze zdarzenia, zamiast zakładać dostarczenie dokładnie raz (exactly-once).

Eksporty stanowią ścieżkę masowego wycieku danych. Eksporty raportów w formatach XLSX, PDF lub JSON pobierają duże wolumeny danych finansowych w ramach jednej operacji, dlatego wymagają własnych uprawnień, limitów częstotliwości (rate limits) oraz logowania audytowego. Zewnętrzne integracje, z których korzystasz, powinny być traktowane jako dane niezaufane i walidowane w taki sam sposób, jak bezpośrednie żądania klientów.

Lista kontrolna gotowości na audyt dla holenderskiego API księgowego

Skorzystaj z tej listy, zanim ogłosisz, że integracja jest gotowa na audyt:

  • Jawna maszyna stanów dla każdego zasobu finansowego; zaksięgowane wpisy nie mogą być nadpisywane.
  • Autoryzacja na poziomie obiektów i podmiotów prawnych dla każdego żądania w oparciu o zasadę domyślnej odmowy.
  • Kontekst najemcy i roli wyprowadzony z tokena, a nie z deklaracji klienta.
  • OAuth 2.0 zgodnie z RFC 9700; zakresy oparte na zdolnościach (capabilities); oddzielone uprawnienia do tworzenia i zatwierdzania.
  • Podział obowiązków egzekwowany w rolach, a nie tylko opisany w dokumentacji.
  • Klucze idempotencji dla wszystkich zapisów finansowych.
  • Walidacja księgowa i podatku VAT po stronie serwera przed przejściami stanów.
  • Dziennik audytu typu append-only, odporny na manipulacje, z kodami przyczyn.
  • Zasady retencji i blokady prawnej dopasowane do holenderskich okresów (siedem lat, dziesięć dla nieruchomości).
  • Blokada okresów dla zamkniętych miesięcy.
  • Podpisane webhooki z mechanizmem deduplikacji; objęte zakresami i limitami eksporty danych z pełnym logowaniem.

Od czego budowę zaczęlibyśmy w pierwszej kolejności

Gdybyśmy jutro wdrażali księgowość w holenderskiej platformie, rozpoczęlibyśmy od modelu stanów oraz autoryzacji na poziomie obiektów, jeszcze przed napisaniem pojedynczego punktu końcowego biznesowego. Te dwie decyzje determinują wszystko, co następuje później, a ich późniejsze wdrażanie w systemie traktującym wpisy księgowe jako edytowalne wiersze jest autentycznie bolesne. Idempotencja oraz dziennik audytu pojawiają się w kolejnym kroku, ponieważ są tanie we wdrożeniu na wczesnym etapie i drogie, gdy dane już płyną.

Reszta – tokeny o ograniczonym zakresie, podpisane webhooki, blokowanie okresów – wynika naturalniej, gdy fundamenty są stabilne. Wszystkie te elementy można przetestować na firmie piaskownicowej (sandbox), zanim jakakolwiek część systemu dotknie danych produkcyjnych.

FAQ

Czy API gotowe na audyt oznacza, że sama firma spełnia wymogi compliance?

Nie, i warto to wyraźnie podkreślić. Gotowość na audyt to cecha projektowa systemu: księga główna, mechanizmy kontrolne i łańcuch dowodowy zostały zbudowane tak, aby audyt mógł zakończyć się sukcesem. Zgodność z określonymi przepisami podatkowymi, rachunkowymi lub ramami bezpieczeństwa to osobna ocena, która mapuje te mechanizmy kontrolne na konkretny standard. Dobrze zaprojektowane API ułatwia pracę nad compliance, ale jej nie zastępuje.

Jak długo dokumentacja księgowa musi być przechowywana w Holandii?

Ogólny obowiązek wynosi siedem lat w przypadku dokumentacji administracyjnej i wydłuża się do dziesięciu lat dla dokumentów powiązanych z nieruchomościami. Zbuduj reguły retencji i blokady prawnej, których standardowe procedury usuwania danych w systemie nie mogą pominąć.

Czy statyczny klucz API występuje jako wystarczające zabezpieczenie integracji finansowej?

W przypadku automatyzacji typu serwer-serwer o wąskim zakresie może to zadziałać, jednak wielokrotnie używane klucze statyczne są trudne do rotowania i niebezpieczne w przypadku wycieku. W przypadku dostępu delegowanego oraz wszelkich procesów skierowanych do użytkowników ludzi, protokół OAuth 2.0 z krótkoterminowymi tokenami o ograniczonym zakresie uprawnień jest lepszym wyborem.

Dlaczego idempotencja ma tak duże znaczenie właśnie w księgowości?

Ponieważ zduplikowany zapis w większości systemów jest co najwyżej niedogodnością, natomiast zduplikowany zapis w księgowości to błędna kwota w księgach, którą ktoś musi odnaleźć i skorygować. Klucze idempotencji sprawiają, że ponowienia są bezpieczne, a nie ryzykowne.

Dalsza lektura