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.
Účetní API, které vrací 200 OK při zápisu platby, vám neříká téměř nic. Nepotvrzuje, že hlavní kniha (ledger) položku zaúčtovala, že subjekt byl k provedení operace oprávněn, že částka prošla validací, ani že ji schválil někdo s patřičnou pravomocí. Pro firmy v Nizozemsku, kde daňový úřad očekává, že záznamy budou rekonstruovatelné a uchovávané po dobu sedmi let, je tato propast mezi „požadavek byl úspěšný“ a „účetnictví je správné“ místem, kde audity selhávají.
Připravenost na audit je operačním designovým cílem, nikoli certifikátem. Znamená to, že API zachovává kompletní řetězec: autentizovaný subjekt, autorizovaná akce, ověřená změna stavu, schválení, výsledek v hlavní knize a záznam zajištěný proti manipulaci (tamper-evident). Následující text se zabývá tím, jak tento řetězec vybudovat záměrně, místo spoléhání na to, že autentizace a TLS postačí.
Čím se účetní API stává „připraveným na audit“
Auditor, který rekonstruuje transakci, potřebuje odpovědět na specifickou posloupnost otázek. Kdo ji inicioval? Byli k tomu oprávněni? Prošla změna účetní validací? Kdo ji schválil? Co zaznamenala hlavní kniha? Lze tento záznam dodatečně pozměnit bez zanechání stopy?
Pokud vaše API nedokáže na všechny tyto otázky odpovědět ze svých vlastních logů a datového modelu, není připraveno na audit, bez ohledu na to, jak čistě kód vypadá. Většina API si poradí dobře s první otázkou, ale se zbytkem špatně.
Kontrolní mechanismy, které tuto mezeru zaplňují, spadají do několika skupin: explicitní účetní stav, vrstvená autorizace, omezená autentizace (scoped authentication), idempotentní zápisy, validace na straně serveru, oddělení povinností (segregation of duties) a logování odolné proti manipulaci. Tyto mechanismy se vzájemně podporují. Slabá autorizace podkopává silný auditní log, protože již nemůžete důvěřovat tomu, že přihlášený subjekt měl oprávnění danou akci provést.
Modelujte účetní stavy dříve, než začnete modelovat koncové body
Nejčastější chybou při návrhu je přístup ke každému finančnímu záznamu jako k libovolně upravitelnému objektu s operacemi create, read, update, delete. Účetnictví takto nefunguje a auditor si toho všimne okamžitě, pokud lze zaúčtovaný zápis do deníku (journal entry) potichu přepsat.
Každý finančně významný zdroj vyžaduje explicitní stavový stroj (state machine). Záznam v deníku nebo platba obvykle prochází těmito stavy:
draft → submitted → approved → posted → settled
Opravy tuto cestu nevracejí zpět. Chybně zaúčtovaná položka získá propojený storno zápis (reversal) nebo kompenzační úpravu. Původní záznam zůstává nedotčen.
Použitelná sada pravidel:
- Koncepty (drafts) mohou upravovat oprávněná uživatelská oprávnění.
- Odeslané záznamy mohou upravovat pouze povolené role nebo je lze vrátit do stavu konceptu.
- Schválené záznamy vyžadují řízenou úpravu a nové schválení.
- Zaúčtované záznamy nelze přepsat ani smazat, pouze stornovat nebo upravit.
- Změny v uzavřeném období vyžadují zvýšené oprávnění, uvedení důvodu a záznam v auditním logu.
Pokus o nelegitimní přechod, například úpravu zaúčtované položky, by měl vrátit stabilní chybu a přesto zapsat auditní záznam. Neúspěšný pokus je totiž také důkazem.
Zde přichází ke slovu neměnná hlavní kniha s podvojným účetnictvím (immutable double-entry ledger). Hlavní kniha Nordlet je navržena tak, že každá transakce končí jako vyrovnaný a trvalý záznam, přičemž rovnováha je vynucena databázovým triggerem v okamžiku commitu a opravy probíhají formou storna namísto přepisování. Toto omezení se během vývoje zdá svazující, ale v průběhu auditu se mění v prvek, kterému váš účetní věří.
Autorizujte každý požadavek, nejen jeho autentizaci
Autentizace prokazuje, kdo volání provádí. Neříká nic o tom, zda daný volající smí sahat na konkrétní fakturu, deník nebo právnickou osobu. Projekt OWASP API Security řadí nedostatečnou autorizaci na úrovni objektů (BOLA) na první místo svého seznamu rizik právě proto, že API odhalují identifikátory objektů, což v případě chybějících kontrol usnadňuje přístup mezi různými nájemci (tenants).
Každý požadavek na finanční API by měl být autorizován na základě kombinace faktorů, nikoli pouze tokenu:
- autentizovaný subjekt,
- klientská aplikace nebo servisní účet,
- nájemce (tenant),
- právnická osoba,
- ID zdroje,
- požadovaná operace,
- aktuální stav zdroje,
- částka nebo transakční limit.
Dvě praktická pravidla jsou důležitější než ostatní. Za prvé, odvozujte kontext nájemce a uživatele z autentizované identity a odmítněte jakýkoli nárok (claim) na nájemce nebo roli dodaný klientem, pokud je s ní v rozporu. Volající by nikdy neměl mít možnost sám tvrdit, pro kterou společnost jedná. Za druhé, uplatňujte výchozí odepření přístupu (deny-by-default). Pokud žádná politika akci výslovně nepovoluje, je zamítnuta.
Testujte oba směry. Horizontální přístup, kdy jeden nájemce přistupuje k datům jiného, a vertikální přístup, kdy běžný uživatel dosáhne na schvalovací, účtovací nebo exportní funkce. Oba jsou běžné a v účetních datech katastrofální.
Model rolí v Nordletu s rolemi vlastníka (owner), administrátora, účetního, manažera, vývojáře a pozorovatele napříč několika společnostmi pod jedním účtem existuje proto, aby toto oddělení bylo vynutitelné, a nikoli pouze zbožným přáním. Role vývojáře, která může vytvářet koncepty faktur, by neměla být schopna uvolňovat platby.
Omezte tokeny na podnikové schopnosti, nikoli na CRUD slovesa
Pro delegovaný přístup používejte aktuální bezpečnostní doporučení pro OAuth 2.0 namísto opakovaně použitelných hesel nebo širokých statických klíčů API. RFC 9700 představuje aktuální doporučenou praxi (Best Current Practice) a stanovuje jasné požadavky pro klienty a autorizační servery: ověřené přesměrovací URI, PKCE tam, kde je to relevantní, kontroly vydavatele a příjemce (issuer and audience), krátkou dobu platnosti přístupových tokenů a rotaci refresh tokenů s možností revokace.
Rozhodování o rozsahu oprávnění (scopes) ukazuje, jak specifické je uvažování v účetnictví. Generická oprávnění jako read a write jsou pro oddělení povinností téměř k ničemu. Oprávnění by měla odpovídat konkrétním schopnostem:
journal:createajournal:approvejsou samostatná oprávnění.- Vytvoření platebního příkazu neuděluje oprávnění k jeho uvolnění.
- Oprávnění k exportu se liší od oprávnění ke čtení, protože hromadný export představuje cestu úniku dat.
Buďte přesní v tom, kde na této škále daný produkt stojí. Klíčová API Nordletu nesou oprávnění pro jednotlivé moduly, jako jsou sales:read a sales:write, která jsou kontrolována při každé akci. To odděluje moduly a čtení od zápisu, ale již ne tvorbu od schvalování; tam, kde dnes potřebujete toto jemnější rozdělení, si jej vynuťte ve vlastní službě před provedením volání.
U operací s vysokou hodnotou stojí za zvážení, zda se uplatní bezpečnostní profil FAPI 2.0. Byl navržen pro API chránící citlivá finanční data a nabízí silnější záruky než základní OAuth. Většina integrací účetnictví pro malé a střední firmy tento plný profil nebude potřebovat, ale toky uvolňování plateb a bankovních připojení jsou místa, kde o něm stojí za to uvažovat.
Přihlášení bez hesla v Nordletu prostřednictvím jednorázových e-mailových odkazů odstraňuje problém s opakovaně použitelnými hesly pro lidské uživatele, což znamená o jeden údaj méně, který je třeba rotovat, nechat uniknout nebo se stát terčem phishingu.
Zajistěte idempotenci finančních zápisů
Sítě uprostřed požadavku selhávají. Klient odešle platební příkaz, spojení se přeruší dříve, než dorazí odpověď, a klient požadavek zopakuje. Bez ochrany máte nyní dvě platby.
Tento problém řeší idempotenční klíče. Klient pošle s každým zápisem unikátní klíč; server si klíč a jeho výsledek uloží. Opakovaný požadavek se stejným klíčem vrátí původní výsledek namísto vytvoření druhého záznamu. Nordlet akceptuje hlavičku Idempotency-Key při každém modifikujícím volání, přičemž při opakování s totožným datovým payloadem přehraje uloženou odpověď a odmítne stejný klíč se změněným payloadem. Každé účetní API, které tuto funkci vynechá, dříve či později vyprodukuje duplicitní záznamy v hlavní knize, které pak někdo musí ručně rozmotávat.
Spojte to s validací na straně serveru. Nikdy nevěřte klientovi, že zkontroluje rovnost má dáti / dal, platnost sazbové DPH pro danou zemi a období nebo existenci účtu. Validace probíhá na serveru, před přechodem stavu, a odmítnutý zápis vytvoří stabilní chybu a záznam v auditním logu.
Auditní logy odolné proti manipulaci a nizozemská retenční pravidla
Auditní log je důkazní vrstvou a klade na něj jiné požadavky než na běžná podniková data. Měl by zaznamenávat rozhodnutí o autorizaci, verzi politiky, subjekt, objekt, operaci, předchozí a nový stav a kód důvodu, aniž by kdykoli zaznamenával tajemství nebo úplné platební údaje.
Klíčovou vlastností je odolnost vůči manipulaci (tamper-evidence). Pokud administrátor může potichu upravit nebo smazat auditní záznamy, log nic neprokazuje. Úložiště pouze pro čtení (append-only), kryptografické zřetězení nebo uchovávání s možností zápisu pouze jednou (WORM) toto vše řeší. Podstatou je, že jakákoli úprava musí být detekovatelná.
Uchovávání dat (retention) si zaslouží vlastní politiku, oddělenou od mazání běžných podnikových záznamů. V Nizozemsku činí obecná retenční lhůta pro administrativní záznamy sedm let a u záznamů týkajících se nemovitého majetku sahá až na deset let. Vaše běžné pracovní postupy pro mazání dat a uplatňování GDPR nesmí mít možnost zničit záznamy nebo auditní důkazy, na které se stále vztahuje zákonné pravidlo uchovávání. Definujte pravidla pro právní zadržení (legal hold), archivaci a mazání auditních důkazů nezávisle a přimějte je odpovídat jurisdikcím, ve kterých působíte.
Uzamykání období (period locking) toto podporuje na účetní vrstvě. Jakmile je měsíc uzavřen, položky v něm nelze měnit bez zvýšeného oprávnění a zaznamenaného důvodu. Nordlet vynucuje uzamykání období tak, aby uzavřené měsíce zůstaly uzavřené, a odmítá jakékoli zaúčtování s datem uvnitř uzamčeného období i přes API, což je jedna z prvních věcí, kterou nizozemský auditor prověřuje.
Zabezpečte okraje: webhooky, exporty a integrace
Jádro API bývá obvykle nejlépe zabezpečenou částí systému. Okraje propouštějí.
Webhooky představují riziko zfalšovaných požadavků. Událost typu sale_invoice.paid by měla být podepsána, aby příjemce mohl ověřit, že pochází od vás, a příjemci by měli doručení považovat za režim alespoň jednou (at-least-once) a pro deduplikaci používat ID události. Nordlet podepisuje každé doručení webhooku podpisem HMAC nad surovým tělem zprávy (raw body) a neúspěšná doručení opakuje s exponenciálním narůstáním intervalů, takže příjemce musí ověřit podpis a provést deduplikaci podle ID události namísto spoléhání se na doručení právě jednou (exactly-once).
Exporty představují cestu hromadného úniku dat. Exporty sestav ve formátu XLSX, PDF nebo JSON stahují velké objemy finančních dat v jedné operaci, a proto vyžadují vlastní oprávnění, limity četnosti (rate limits) a auditní protokolování. Integrace třetích stran, které spotřebováváte, by měly být považovány za nedůvěryhodný vstup a validovány stejným způsobem jako přímé požadavky klientů.
Kontrolní seznam připravenosti na audit pro nizozemské účetní API
Použijte tento seznam, než prohlásíte integraci za připravenou na audit:
- Explicitní stavový stroj u každého finančního zdroje; zaúčtované položky nelze přepsat.
- Autorizace na úrovni objektů a právnické osoby u každého požadavku, výchozí odepření přístupu.
- Kontext nájemce a role odvozený z tokenu, nikoli z klientských nároků.
- OAuth 2.0 podle RFC 9700; rozsahy založené na schopnostech; oddělená tvorba a schvalování.
- Oddělení povinností vynucené v rolích, nejen v dokumentaci.
- Idempotenční klíče u všech finančních zápisů.
- Účetní validace a validace DPH na straně serveru před přechodem stavu.
- Auditní log pouze pro připojování (append-only) a odolný proti manipulaci s kódy důvodů.
- Pravidla uchovávání a právního zadržení zmapovaná na nizozemská období (sedm let, deset pro nemovitosti).
- Uzamykání období pro uzavřené měsíce.
- Podepsané webhooky s deduplikací; exporty s omezenými rozsahy a limity četnosti a logováním.
Co bychom postavili jako první
Pokud bychom zítra integrovali účetnictví do nizozemské platformy, začali bychom modelovým stavem a autorizací na úrovni objektů, ještě před napsáním jediného podnikového koncového bodu. Tato dvě rozhodnutí omezují všechno, co následuje, a jejich dodatečné vkládání do systému, který zacházel s položkami hlavní knihy jako s upravitelnými řádky, je upřímně bolestivé. Idempotence a auditní log přijdou na řadu jako další, protože je levné je přidat brzy a drahé po spuštění toku dat.
Zbytek – tokeny s omezenou platností, podepsané webhooky, uzamykání období – následuje snadněji, jakmile drží základ. Všechno z toho lze vyzkoušet proti sandboxové společnosti, než cokoliv z toho sáhne na produkční data.
FAQ
Znamená API připravené na audit, že firma plní předpisy?
Ne, a v tomto ohledu je dobré být přesný. Připravenost na audit je vlastnost návrhu systému: hlavní kniha, kontrolní mechanismy a řetězec důkazů jsou postaveny tak, aby audit mohl proběhnout úspěšně. Soulad s konkrétním daňovým, účetním nebo bezpečnostním rámcem je samostatným posouzením, které tyto kontrolní mechanismy mapuje na jmenovaný standard. Dobře navržené API usnadňuje práci s dodržováním předpisů, ale nenahrazuje ji.
Jak dlouho je nutné v Nizozemsku uchovávat účetní záznamy?
Obecná povinnost činí sedm let pro administrativní záznamy a prodlužuje se na deset let pro záznamy vázané na nemovitý majetek. Vytvořte pravidla pro uchovávání a právní zadržení, která vaše běžné postupy pro mazání dat nemohou přepsat.
Stačí pro finanční integraci statický klíč API?
Pro automatizaci mezi servery s úzkým vymezením rozsahu to fungovat může, ale opakovaně použitelné statické klíče se špatně rotují a v případě úniku jsou nebezpečné. Pro delegovaný přístup a jakýkoliv tok orientovaný na člověka je silnější volbou OAuth 2.0 s krátkodobými tokeny s vymezeným rozsahem schopností.
Proč je idempotence pro účetnictví tak důležitá?
Protože duplicitní zápis je ve většině systémů nepříjemností, ale duplicitní zápis v účetnictví znamená špatné číslo v knihách, které musí někdo najít a stornovat. Idempotenční klíče dělají opakované pokusy bezpečnými namísto rizikových.
Další čtení
- Projekt OWASP API Security pro aktuální kategorie rizik API
- RFC 9700: Bezpečnostní doporučená praxe pro OAuth 2.0 pro požadavky na delegovaný přístup
- DPH v EU v reálném čase pro vývojáře v Nizozemsku o tom, jak vypadá nizozemská daňová realita v praxi
- Konvence API pro rozsahy, idempotenci, chyby a podepisování webhooků