Building an Accounting API That Survives an Audit
A practical guide to the security and audit controls a financial API needs before Dutch businesses trust it with their books.
Računovodstveni API koji vrši zapis plaćanja i vraća odgovor 200 OK u biti vam nije rekao gotovo ništa. Time nije potvrđeno da je glavna knjiga (general ledger) evidentirala stavku, da je izvršitelj imao ovlast za to, da je iznos prošao validaciju niti da je bilo tko s ovlastima to odobrio. Za tvrtke u Nizozemskoj, gdje porezna uprava očekuje da se knjigovodstvene isprave mogu rekonstruirati i čuvati sedam godina, upravo je taj prostor između "zahtjev je uspio" i "knjige su ispravne" mjesto gdje revizije kreću u krivom smjeru.
Spremnost za reviziju operativni je cilj dizajna, a ne certifikat. To znači da API čuva cijeli lanac: autentificirani subjekt do ovlaštene radnje do validirane promjene stanja do odobrenja do rezultata u glavnoj knjizi do dokaza otpornog na neovlašteno mijenjanje (tamper-evident). Sve u nastavku odnosi se na namjerno izgradnju tog lančanog sustava, umjesto oslanjanja na pretpostavku da autentifikacija i TLS sve pokrivaju.
Što računovodstveni API čini "spreman za reviziju"
Revizor koji rekonstruira transakciju mora odgovoriti na specifičan slijed pitanja. Tko ju je pokrenuo? Jesu li imali dopuštenje? Je li promjena prošla računovodstvenu validaciju? Tko ju je odobrio? Što je zabilježeno u glavnoj knjizi? Može li se taj zapis naknadno promijeniti bez ostavljanja traga?
Ako vaš API ne može odgovoriti na svih šest pitanja iz vlastitih zapisnika (logs) i podatkovnog modela, nije spreman za reviziju, bez obzira na to koliko kod izgledao uredno. Većina API-ja dobro rješava prvo pitanje, a ostala loše.
Kontrole koje zatvaraju taj jaz dijele se u nekoliko skupina: ekskluzivno računovodstveno stanje, slojevita autorizacija, ograničena autentifikacija, idempotenti zapisi, validacija na strani poslužitelja, podjela dužnosti i zapisnici otporni na neovlašteno mijenjanje. One se međusobno podupiru. Slabatska autorizacija narušava snažan revizijski zapis jer više ne možete vjerovati da je evidentirani subjekt uopće smio obaviti tu radnju.
Modelirajte računovodstvena stanja prije nego što modelirate krajnje točke (endpoints)
Najčešća pogreška u dizajnu jest tretiranje svakog financijskog zapisa kao slobodno uredivog objekta s radnjama create, read, update, delete. Računovodstvo ne funkcionira tako i revizor će odmah primijetiti ako se proknjižena temeljnica može prešutno prepisati.
Svaki financijski značajan resurs zahtijeva ekskluzivan automat stanja (state machine). Temeljnica ili plaćanje obično prolaze kroz sljedeće faze:
draft → submitted → approved → posted → settled
Ispravci ne vraćaju taj put unatrag. Proknjižena stavka koja je netočna dobiva povezanu storno stavku ili kompenzacijsko knjiženje. Izvornik ostaje netaknut.
Skup primjenjivih pravila:
- Nacrte (drafts) mogu uređivati ovlašteni korisnici.
- Podnesene zapise mogu uređivati samo dopuštene uloge ili se mogu vratiti u nacrt.
- Odobreni zapisi zahtijevaju kontroliranu izmjenu i novo odobrenje.
- Proknjiženi zapisi ne mogu se prepisati ni izbrisati, već samo stornirati ili korigirati.
- Promjene u zatvorenom obračunskom razdoblju zahtijevaju višu razinu ovlaštenja, naveden razlog i revizijski događaj.
Pokušaj nedopuštenog prijelaza, poput uređivanja proknjižene stavke, treba vratiti stabilnu pogrešku i svejedno zapisati revizijski događaj. Neuspjeli pokušaj također je dokaz.
Upravo tu nepromjenjiva (immutable) dvojna knjigovodstvena evidencija dolazi do izražaja. Nordletova glavna knjiga napravljena je tako da svaka transakcija sjeda kao uravnotežen, trajan unos, pri čemu baze podataka kroz okidače (triggers) osiguravaju ravnotežu u trenutku predaje zapisa, dok se ispravci provode putem storniranja umjesto prepisivanja. To ograničenje tijekom razvoja djeluje strogo, a u trenutku revizije postaje ono u što vaš računovođa ima povjerenja.
Autorizirajte svaki zahtjev, ne samo njegovu autentifikaciju
Autentifikacija dokazuje tko upućuje poziv. Ona ne govori ništa o tome smije li taj pozivatelj dirati određenu fakturu, dnevnik ili pravnu osobu. OWASP API Security Project stavlja neovlašteni pristup objektima na vrh svoje liste rizika upravo zato što API-ji izlažu identifikatore objekata koji olakšavaju neovlašten pristup između različitih zakupaca (tenants) kada nedostaju provjere.
Svaki zahtjev prema financijskom API-ju trebao bi biti autoriziran na temelju kombinacije faktora, a ne samo tokena:
- autentificirani subjekt
- klijentska aplikacija ili servisni račun
- zakupac (tenant)
- pravna osoba
- ID resursa
- tražena radnja
- trenutno stanje resursa
- iznos ili prag transakcije
Dva praktična pravila važnija su od ostalih. Prvo, izvedite kontekst zakupca i korisnika iz autentificiranog identiteta i odbijte bilo kakvu tvrdnju o zakupcu ili ulozi koju pošalje klijent, a koja je u suprotnosti s njim. Pozivatelj nikada ne smije sam tvrditi u čije ime nastupa. Drugo, primijenite princip uskraćivanja prema zadanim postavkama (deny-by-default). Ako nijedno pravilo izričito ne odobrava radnju, ona ne prolazi.
Testirajte u oba smjera. Horizontalni pristup, gdje jedan zakupac dohvaća podatke drugog zakupca, i vertikalni pristup, gdje običan korisnik doseže funkcije odobravanja, knjiženja ili izvoza. Oba su slučaja česta, a u računovodstvenim podacima i katastrofalna.
Nordletov model uloga, s ulogama vlasnika, administratora, računovođe, upravitelja, razvojnog inženjera i promatrača u više tvrtki pod jednim računom, služi upravo tome da to odvajanje bude primjenjivo, a ne samo teoretsko. Uloga razvojnog inženjera koja može kreirati nacrte faktura ne bi smjela moći puštati plaćanja.
Ograničite tokene (scopes) na poslovne mogućnosti, a ne na CRUD glagole
Za delegirani pristup koristite aktualne sigurnosne smjernice za OAuth 2.0 umjesto višekratnih lozinki ili širokih statičnih API ključeva. RFC 9700 predstavlja trenutnu najbolju praksu i postavlja jasne zahtjeve za klijente i poslužitelje autorizacije: provjerene URI-je za preusmjeravanje, primjenu PKCE-a gdje je primjenjiv, provjere izdavatelja i primatelja, kratak vijek trajanja pristupnih tokena te rotaciju osvježavajućih tokena (refresh tokens) uz opoziv.
Odluka o opsegu (scope) najbolje pokazuje razmišljanje specifično za računovodstvo. Generički opsezi poput read i write gotovo su beskorisni za podjelu dužnosti. Opsezi trebaju odgovarati stvarnim mogućnostima:
journal:createijournal:approvezasebni su opsezi.- Stvaranje naloga za plaćanje ne daje dopuštenje za njegovo puštanje.
- Dopuštenje za izvoz podataka razlikuje se od dopuštenja za čitanje jer masovni izvoz predstavlja put propuštanja podataka.
Budite precizni oko toga gdje se određeni proizvod nalazi na toj ljestvici. Nordletovi API ključevi nose opsege po modulima kao što su sales:read i sales:write, koji se provjeravaju pri svakoj radnji. Time se odvajaju moduli i čitanje od pisanja, ali ne i stvaranje od odobravanja; tamo gdje vam danas treba ta finija podjela, provedite je unutar vlastite usluge prije samog poziva.
Za operacije visoke vrijednosti vrijedi razmotriti primjenjuje li se sigurnosni profil FAPI 2.0. On je dizajniran za API-je koji štite osjetljive financijske podatke i nudi jača jamstva od osnovnog OAuth-a. Većini integracija za mala i srednja poduzeća neće trebati cijeli profil, ali tokovi puštanja plaćanja i povezivanja s bankama mjesta su gdje ga valja uzeti u obzir.
Nordletova prijava bez lozinke (passwordless) putem jednokratnih poveznica e-pošte uklanja problem višekratnih lozinki za ljudske korisnike, što znači jednu vjerodajnicu manje za rotaciju, curenje ili krađu identiteta (phishing).
Osigurajte da su financijski upisi idempotentni
Mreže pucaju usred zahtjeva. Klijent šalje nalog za plaćanje, veza se prekida prije nego što stigne odgovor, a klijent ponavlja zahtjev. Bez zaštite, sada imate dva plaćanja.
Ključevi idempotencije rješavaju taj problem. Klijent šalje jedinstveni ključ uz svaki upis; poslužitelj bilježi ključ i njegov rezultat. Ponovljeni pokušaj s istim ključem vraća izvorni rezultat umjesto stvaranja drugog unosa. Nordlet prihvaća zaglavlje Idempotency-Key za svaki poziv koji mijenja stanje, reproducirajući pohranjeni odgovor pri ponovnom pokušaju s istim podatkovnim paketom, dok istovremeno odbija isti ključ s izmijenjenim podatkovnim paketom. Svaki računovodstveni API koji preskoči ovu značajku s vremenom će proizvesti dvostruke stavke u glavnoj knjizi koje netko mora ručno raspetljavati.
Uparite to s validacijom na strani poslužitelja. Nikada nemojte vjerovati klijentu da će provjeriti jesu li zaduženja jednaka odobrenjima, je li stopa PDV-a valjana za tu državu i razdoblje ili postoji li kontni račun. Validacija se odvija na poslužitelju, prije promjene stanja, a odbijeni upis vraća stabilnu pogrešku i stvara revizijski zapis.
Revizijski tragovi otporni na neovlašteno mijenjanje i nizozemska pravila čuvanja
Revizijski zapis sloj je dokaza i ima drugačije zahtjeve od običnih poslovnih podataka. On mora bilježiti odluku o autorizaciji, verziju pravila, subjekt, objekt, radnju, prethodno i novo stanje te šifru razloga, a da pritom nikada ne bilježi tajne podatke ni potpune vjerodajnice za plaćanje.
Ključno je svojstvo otpornost na neovlašteno mijenjanje. Ako administrator može neprimjetno uređivati ili brisati revizijske zapise, zapis ne dokazuje ništa. Spremišta koja podržavaju samo dodavanje zapisa (append-only), kriptografsko uvezivanje ili zadržavanje uz jednokratni upis (write-once) rješavaju taj problem. Poanta je u tome da svaka izmjena mora biti prepoznatljiva.
Zadržavanje podataka zaslužuje vlastitu politiku, odvojenu od brisanja poslovnih podataka. U Nizozemskoj je opći rok čuvanja administrativne dokumentacije sedam godina, dok se za dokumente koji se odnose na nekretnine produžuje na deset godina. Vaši uobičajeni radni procesi brisanja podataka i usklađenosti s GDPR-om ne smiju imati mogućnost uništavanja zapisa ili revizijskih dokaza koje zakonsko pravilo čuvanja još uvijek obuhvaća. Definirajte pravila pravnog zadržavanja (legal hold), arhiviranja i brisanja revizijskih dokaza neovisno jedno o drugome i uskladite ih s jurisdikcijama u kojima posLujete.
Zaključavanje obračunskog razdoblja podržava to na računovodstvenoj razini. Kada se mjesec zatvori, unosi u njemu ne mogu se mijenjati bez povišenih ovlasti i zabilježenog razloga. Nordlet provodi zaključavanje razdoblja tako da zatvoreni mjeseci ostaju zatvoreni, odbijajući svako knjiženje datirano unutar zaključanog razdoblja čak i putem API-ja, što je jedna od prvih stvari koje nizozemski revizor provjerava.
Osigurajte rubne dijelove sustava: webhooks, izvoze i integracije
Granični dio API-ja obično je najbolje branjeni dio sustava, dok rubovi propuštaju.
Webhooks nose rizik lažiranih zahtjeva. Događaj poput sale_invoice.paid mora biti potpisan kako bi primatelj mogao provjeriti je li došao od vas, pri čemu primatelji trebaju tretirati isporuku kao "barem jednom" (at-least-once) i koristiti ID događaja za deduplikaciju. Nordlet potpisuje svaku isporuku webhooka s HMAC potpisom nad izvornim tijelom poruke i ponavlja neuspješne isporuke uz eksponencijalno kašnjenje, pa primatelj mora provjeriti potpis i obaviti deduplikaciju prema ID-ju događaja umjesto da pretpostavlja točno jednokratnu isporuku.
Izvozi podataka predstavljaju put masovnog curenja podataka. Izvozi izvještaja u XLSX, PDF ili JSON formatu povlače velike količine financijskih podataka u jednoj operaciji, stoga im je potrebna vlastita dozvola, ograničenja učestalosti (rate limits) i revizijsko bilježenje. Treće strane s kojima se integrirate trebaju se tretirati kao nepovjerljiv ulaz, uz validaciju istovjetnu onoj koju provodite za izravne klijentske zahtjeve.
Kontrolni popis spremnosti za reviziju nizozemskog računovodstvenog API-ja
Provjerite sljedeće prije nego što tvrdite da je integracija spremna za reviziju:
- Ekskluzivan automat stanja za svaki financijski resurs; proknjižene stavke ne mogu se prepisati
- Autorizacija na razini objekta i pravne osobe za svaki zahtjev, uz uskraćivanje po zadanom
- Kontekst zakupca i uloge izveden iz tokena, a ne iz klijentskih tvrdnji
- OAuth 2.0 prema RFC 9700; opsezi temeljeni na mogućnostima; odvojeno stvaranje i odobravanje
- Podjela dužnosti provedena kroz uloge, a ne samo opisana u dokumentaciji
- Ključevi idempotencije za sve financijske upise
- Računovodstvena validacija i provjera PDV-a na strani poslužitelja prije promjena stanja
- Revizijski zapis otporan na neovlašteno mijenjanje koji podržava samo dodavanje, uz šifre razloga
- Pravila čuvanja i pravnog zadržavanja usklađena s nizozemskim rokovima (sedam godina, deset za nekretnine)
- Zaključavanje razdoblja za zatvorene mjesece
- Potpisani webhooks s deduplikacijom; izvozi s ograničenim opsegom, kontrolom učestalosti i bilježenjem
Što bismo prvo izgradili
Da sutra ugrađujemo računovodstvo u neku nizozemsku platformu, počeli bismo s modelom stanja i autorizacijom na razini objekata, prije pisanja ijedne poslovne krajnje točke. Te dvije odluke uvjetuju sve u nastavku, a naknadno uvođenje takvih promjena u sustav koji je knjiženja tretirao kao uredive retke iznimno je bolno. Idempotencija i revizijski zapis dolaze sljedeći jer ih je jeftino dodati rano, a skupo naknadno kada podaci već teku.
Sve ostalo - tokeni s ograničenim opsegom, potpisani webhooks, zaključavanje razdoblja - lakše dolazi na svoje mjesto jednom kada temelji čvrsto stoje. Sve se to može isprobati na sandbox okruženju tvrtke prije nego što ijedan dio dotakne produkcijske podatke.
FAQ
Znači li API spreman za reviziju da je i poslovanje usklađeno?
Ne, i o tome vrijedi jasno govoriti. Spremnost za reviziju dizajnersko je svojstvo sustava: glavna knjiga, kontrole i lanac dokaza izgrađeni su tako da revizija može uspjeti. Usklađenost s određenim poreznim, računovodstvenim ili sigurnosnim okvirom zasebna je procjena koja te kontrole preslikava na imenovani standard. Dobro izrađen API olakšava usklađenost, ali je ne zamjenjuje.
Koliko dugo se računovodstveni dokumenti moraju čuvati u Nizozemskoj?
Opća obveza iznosi sedam godina za administrativne evidencije, dok se za dokumente vezane uz nekretnine produžuje na deset godina. Izgradite pravila čuvanja i pravnog zadržavanja koja vaši uobičajeni radni procesi brisanja ne mogu nadjačati.
Je li statički API ključ dovoljan za financijsku integraciju?
Za automatizaciju između poslužitelja s uskim opsegom može funkcionirati, ali višekratne statične ključeve teško je rotirati i opasni su ako procure. Za delegirani pristup i sve tokove okrenute korisnicima, OAuth 2.0 s kratkotrajnim tokenima ograničenim na određene mogućnosti predstavlja bolji izbor.
Zašto je idempotencija toliko važna upravo u računovodstvu?
Zato što je dvostruki upis u većini sustava gnjavaža, dok je u računovodstvu to pogrešan broj u knjigama koji netko mora pronaći i stornirati. Ključevi idempotencije čine ponovljene zahtjeve sigurnima umjesto rizičnima.
Daljnje čitanje
- OWASP API Security Project za trenutne kategorije API rizika
- RFC 9700: OAuth 2.0 Security Best Current Practice za zahtjeve delegiranog pristupa
- Real-time EU VAT for developers in the Netherlands za uvid u to kako nizozemska porezna slika izgleda u praksi
- API conventions za opsege, idempotenciju, pogreške i potpisivanje webhooka