Розробка бухгалтерського API, який витримає аудит
Практичний посібник щодо безпеки та елементів контролю, які необхідні фінансовому API, перш ніж нідерландський бізнес довірить йому свої облікові книги.
Бухгалтерський API, який повертає 200 OK на запис платежу, майже нічого вам не говорить. Він не підтверджує, що головна книга записала проводку, що суб'єкт мав повноваження її здійснити, що сума пройшла валідацію або що хтось із відповідними повноваженнями її схвалив. Для компаній у Нідерландах, де податкова вимагає, щоб записи можна було відтворити та зберігати протягом семи років, цей розрив між „запит успішний“ і „бухгалтерія правильна“ є тим місцем, де провалюються аудити.
Готовність до аудиту (audit-ready) є метою операційного проєктування, а не сертифікатом. Вона означає, що API зберігає повний ланцюжок: автентифікований суб'єкт до авторизованої дії до валідованої зміни стану до схвалення до результату в головній книзі до захищених від втручання доказів. Усе наведене нижче стосується навмисної побудови цього ланцюжка, а не припущення, що автентифікація та TLS самі все вирішують.
Що робить бухгалтерський API „готовим до аудиту“
Аудитор, який відновлює транзакцію, повинен відповісти на певну послідовність запитань. Хто її ініціював? Чи мали вони на це право? Чи пройшла зміна бухгалтерську валідацію? Хто її схвалив? Що зафіксувала головна книга? Чи можна цей запис змінити заднім числом без залишення слідів?
Якщо ваш API не може відповісти на всі шість запитань за допомогою власних логів і моделі даних, він не готовий до аудиту, незалежно від того, наскільки чисто виглядає код. Більшість API добре справляються з першим запитанням і погано з рештою.
Елементи контролю, які заповнюють цю прогалину, поділяються на кілька груп: чіткий бухгалтерський стан, багаторівнева авторизація, обмежена автентифікація, ідемпотентні записи, серверна валідація, розділення обов'язків та захищене від втручання ведення логів. Вони підтримують одне одного. Слабка авторизація підриває надійний аудит-лог, оскільки ви більше не можете бути впевнені, що залогований суб'єкт взагалі мав право виконувати цю дію.
Змоделюйте бухгалтерські стани перед моделюванням ендпоїнтів
Найпоширеніша проєктна помилка полягає у сприйнятті кожної фінансової записи як вільно редагованого об'єкта з операціями create, read, update, delete. Бухгалтерія так не працює, і аудитор одразу помітить, якщо занесений у головну книгу запис можна непомітно перезаписати.
Кожен фінансово значущий ресурс потребує чіткої машини станів. Бухгалтерська проводка або платіж зазвичай проходять такі етапи:
draft → submitted → approved → posted → settled
Виправлення не повертають цей шлях назад. Неправильно занесений запис отримує пов'язане сторнування (reversal) або коригувальну проводку. Оригінал залишається недоторканим.
Працездатний набір правил:
- Чернетки можуть редагуватися уповноваженими користувачами.
- Надіслані записи можуть редагуватися лише ролями з відповідними дозволами або повертатися до чернеток.
- Затверджені записи вимагають контрольованого внесення змін та нового схвалення.
- Занесені в головну книгу записи не можна перезаписувати чи видаляти, лише сторнувати або коригувати.
- Зміни в закритих періодах вимагають підвищених повноважень, зазначення причини та запису в аудит-лозі.
Спроба незаконного переходу, наприклад редагування вже занесеного запису, повинна повертати стабільну помилку і водночас записувати подію в аудит-лог. Невдала спроба — це також доказ.
Саме тут незмінна головна книга подвійного запису (double-entry ledger) виправдовує своє призначення. Головна книга Nordlet побудована так, що кожна транзакція потрапляє як збалансований, постійний запис, причому баланс забезпечується тригером бази даних у момент фіксації (commit), а виправлення відбуваються через сторнування, а не перезаписування. Це обмеження здається обмежувальним під час розробки, але перетворюється на те, чому ваш бухгалтер довіряє під час аудиту.
Авторизуйте кожен запит, а не лише проводьте автентифікацію
Автентифікація доводить, хто робить виклик. Вона нічого не говорить про те, чи має цей викликач право торкатися конкретного інфоблоку, журналу чи юридичної особи. Проєкт OWASP API Security Project ставить порушення авторизації на рівні об'єктів (BOLA) на перше місце у своєму списку ризиків саме тому, що API розкривають ідентифікатори об'єктів, які полегшують доступ між різними тенантами (tenant), якщо перевірки відсутні.
Кожен запит до фінансового API має бути авторизований на основі сукупності факторів, а не просто токена:
- автентифікований суб'єкт
- кліентський застосунок або сервісний обліковий запис
- тенант
- юридична особа
- ідентифікатор ресурсу
- запитувана операція
- поточний стан ресурсу
- сума або поріг транзакції
Два практичні правила важливіші за інші. По-перше, виводьте контекст тенанта та користувача з автентифікованої ідентичності та відхиляйте будь-які заяви (claims) щодо тенанта чи ролі від клієнта, якщо вони з нею конфліктують. Викликач ніколи не повинен мати змоги самостійно заявляти, від імені якої компанії він діє. По-друге, використовуйте принцип заборони за замовчуванням (deny-by-default). Якщо жодна політика прямо не надає дозвіл на дію, вона завершується помилкою.
Тестуйте обидва напрямки. Горизонтальний доступ, коли один тенант отримує доступ до даних іншого тенанта, і вертикальний доступ, коли звичайний користувач отримує доступ до функцій схвалення, проведення чи експорту. Обидва варіанти поширені, і обидва є катастрофічними для бухгалтерських даних.
Модель ролей Nordlet з ролями власника, адміністратора, бухгалтера, менеджера, розробника та спостерігача для кількох компаній в одному обліковому записі створена для того, щоб зробити це розділення обов'язковим, а не декларативним. Роль розробника, яка може створювати чернетки рахунків, не повинна мати можливості проводити платежі.
Обмежуйте токени бізнес-можливостями, а не CRUD-дієсловами
Для делегованого доступу використовуйте сучасні рекомендації з безпеки OAuth 2.0 замість багаторазових паролів або широких статичних API-ключів. RFC 9700 є актуальною найкращою практикою (Best Current Practice) і встановлює чіткі вимоги до клієнтів і серверів авторизації: валідовані URI перенаправлення, PKCE де це доречно, перевірки видавця (issuer) і аудиторії (audience), короткий час життя токенів доступу та ротацію токенів оновлення з їх відкликанням.
Рішення щодо областей дії (scopes) демонструє специфічне для бухгалтерії мислення. Загальні області на кшталт read і write мало чим корисні для розділення обов'язків. Області дії повинні відповідати можливостям:
journal:createтаjournal:approveє різними областями дії.- Створення платіжної інструкції не дає дозволу на її випуск.
- Дозвіл на експорт є окремим від дозволу на читання, оскільки масовий експорт є шляхом витоку даних.
Чітко визначайте, де конкретний продукт знаходиться на цій шкалі. API-ключі Nordlet містять області дії для окремих модулів, такі як sales:read і sales:write, які перевіряються під час кожної дії. Це розділяє модулі, а також читання і запис, але не створення та схвалення; там, де вам потрібен більш детальний поділ, забезпечуйте його у власному сервісі перед викликом.
Для операцій високої вартості варто оцінити, чи застосовується профіль безпеки FAPI 2.0 Security Profile. Він був розроблений для API, що захищають чутливі фінансові дані, і пропонує сильніші гарантії, ніж базовий OAuth. Більшості інтеграцій для малого та середнього бізнесу повний профіль не знадобиться, але випуск платежів та підключення банків — це ті місця, де варто про це подумати.
Вхід до Nordlet без пароля за допомогою одноразових поштових посилань усуває проблему багаторазових паролей для людей, залишаючи на одну облікову дані менше для ротації, витоку чи фішингу.
Зробіть фінансові записи ідемпотентними
Мережі дають збої посеред запиту. Клієнт надсилає платіжну інструкцію, з'єднання обривається до надходження відповіді, і клієнт робить повторну спробу. Без захисту ви тепер маєте два платежі.
Ключі ідемпотентності (idempotency keys) вирішують цю проблему. Клієнт надсилає унікальний ключ із кожним записом; сервер записує ключ та його результат. Повторна спроба з тим самим ключем повертає початковий результат замість створення другого запису. Nordlet приймає заголовок Idempotency-Key для кожного запиту на зміну стану, відтворюючи збережену відповідь під час повторної спроби з тим самим пейлоадом і відхиляючи той самий ключ із зміненим пейлоадом. Будь-який бухгалтерський API, який пропускає цю функцію, зрештою згенерує дублікати записів у головній книзі, які хтось повинен розплутувати вручну.
Поєднуйте це зі серверною валідацією. Ніколи не довіряйте клієнту перевірку того, що дебети дорівнюють кредитам, що ставка ПДВ є чинною для країни та періоду або що рахунок існує. Валідація виконується на сервері, до переходу стану, а відхилений запис формує стабільну помилку та запис в аудит-лозі.
Захищені від втручання аудит-логи та нідерландські правила зберігання
Аудит-лог — це шар доказів, який має інші вимоги, ніж звичайні бізнес-дані. Він повинен фіксувати рішення про авторизацію, версію політики, суб'єкта, об'єкт, операцію, попередній і новий стан, а також код причини, ніколи не записуючи секрети або повні платіжні дані.
Критичною властивістю є захист від несанкціонованого втручання (tamper-evidence). Якщо адміністратор може непомітно відредагувати або видалити записи аудит-логу, лог нічого не доводить. Зберігання з можливістю лише дописування (append-only), криптографічне ланцюжкування або одноразовий запис із захистом від видалення вирішують цю проблему. Головне те, що будь-яка модифікація повинна бути виявленою.
Зберігання даних заслуговує на окрему політику, відокремлену від видалення бізнес-даних. У Нідерландах загальний термін зберігання адміністративних документів становить сім років, а для документів щодо нерухомого майна — десять років. Ваші звичайні робочі процеси видалення даних та GDPR не повинні знищувати записи або докази аудиту, на які поширюється правило юридичного зберігання. Визначайте правила юридичного утримання (legal hold), архівації та видалення доказів незалежно і зв'язуйте їх із юрисдикціями, в яких ви працюєте.
Блокування періодів (period locking) підтримує це на рівні бухгалтерії. Щойно місяць закрито, записи в ньому не можуть змінюватися без підвищених повноважень та зафіксованої причини. Nordlet забезпечує блокування періодів, щоб закриті місяці залишалися закритими, відхиляючи будь-яке проведення з датою всередині заблокованого періоду навіть через API. Це одна з перших речей, які перевіряє нідерландський аудитор.
Захистіть межі: вебхуки, експорт та інтеграції
Основний API зазвичай є найкраще захищеною частиною системи. Межі дають витоки.
Вебхуки несуть ризик підроблених запитів. Така подія, як sale_invoice.paid, повинна бути підписана, щоб отримувач міг перевірити її походження від вас. Отримувачі повинні сприймати доставку як таку, що відбувається щонайменше один раз (at-least-once), і використовувати ідентифікатор події для дедуплікації. Nordlet підписує кожну доставку вебхука HMAC-підписом по сирому тілу (raw body) і повторює невдалі доставки з експоненційним затуханням, тому отримувач має перевірити підпис і виконати дедуплікацію за ідентифікатором події, замість того щоб покладатися на гарантію доставлення рівно один раз (exactly-once).
Експорт є шляхом масового витоку даних. Експорт звітів у XLSX, PDF або JSON витягує великі обсяги фінансових даних за одну операцію, тому він потребує власних дозволів, обмежень частоти запитів (rate limits) та аудиту. Сторонні інтеграції, які ви використовуєте, слід розглядати як неперевірені вхідні дані та валідувати їх так само, як і прямі клієнтські запити.
Чек-лист готовності до аудиту для нідерландського бухгалтерського API
Використовуйте цей чек-лист перед тим, як заявляти про готовність інтеграції до аудиту:
- Чітка машина станів для кожного фінансового ресурсу; проведені записи не можна перезаписувати.
- Авторизація на рівні об'єктів та юридичних осіб для кожного запиту із забороною за замовчуванням.
- Контекст тенанта та ролі виводиться з токена, а не з клієнтських заяв.
- OAuth 2.0 згідно з RFC 9700; області дії на основі можливостей; розділені створення та схвалення.
- Розділення обов'язків закріплене в ролях, а не лише задокументоване.
- Ключі ідемпотентності для всіх фінансових записів.
- Серверна бухгалтерська та податкова валідація (ПДВ) до переходу станів.
- Аудит-лог із можливістю лише дописування, захищений від втручання, із кодами причин.
- Правила зберігання та юридичного утримання узгоджені з нідерландськими періодами (сім років, десять для майна).
- Блокування періодів для закритих місяців.
- Підписані вебхуки з дедуплікацією; обмежені за правами та частотою, залоговані експорти.
Що б ми побудували в першу чергу
Якби ми завтра вбудовували бухгалтерію в нідерландську платформу, ми б почали з моделі станів та авторизації на рівні об'єктів ще до написання будь-якого бізнес-ендпоїнта. Ці два рішення обмежують усе подальше, і їхнє впровадження заднім числом у систему, яка розглядала записи головної книги як редаговані рядки, є надзвичайно болючим. Ідемпотентність і аудит-лог йдуть наступними, оскільки їх дешево додати на ранньому етапі й дорого після того, як дані вже почали текти.
Усе інше — токени з обмеженою областю дії, підписані вебхуки, блокування періодів — дається значно простіше, коли фундамент тримається. Усе це можна випробувати на пісочниці компанії до того, як будь-що торкнеться виробничих даних.
FAQ
Чи означає API, готовий до аудиту, що бізнес відповідає вимогам?
Ні, і про це варто говорити чітко. Готовність до аудиту є проєктною властивістю системи: головна книга, елементи контролю та ланцюжок доказів побудовані так, щоб аудит міг пройти успішно. Дотримання конкретного податкового, бухгалтерського чи безпекового стандарту є окремою оцінкою, яка зіставляє ці елементи контролю з названим стандартом. Добре побудований API полегшує роботу з дотримання вимог, але не замінює її.
Скільки часу потрібно зберігати бухгалтерські записи в Нідерландах?
Загальний обов'язок становить сім років для адміністративних записів із подовженням до десяти років для документів, пов'язаних із нерухомим майном. Створюйте правила зберігання та юридичного утримання, які ваші звичайні робочі процеси видалення не можуть перевищити.
Чи достатньо статичного API-ключа для фінансової інтеграції?
Для автоматизації між серверами з вузькою областю дії це може працювати, але багаторазові статичні ключі важко ротувати, і вони небезпечні у разі витоку. Для делегованого доступу та будь-яких сценаріїв за участю користувачів кращим вибором є OAuth 2.0 з короткоживучими токенами з обмеженими областями дії.
Чому ідемпотентність настільки важлива саме для бухгалтерії?
Оскільки дублювання запису в більшості систем є неприємністю, а дублювання запису в бухгалтерії — це неправильна цифра в облікових книгах, яку хтось повинен шукати та сторнувати. Ключі ідемпотентності роблять повторні спроби безпечними замість ризикованих.
Подальше читання
- OWASP API Security Project щодо актуальних категорій ризиків API
- RFC 9700: OAuth 2.0 Security Best Current Practice щодо вимог до делегованого доступу
- ПДВ ЄС у реальному часі для розробників у Нідерландах про те, як виглядає податкова картина в Нідерландах на практиці
- Угоди про API щодо областей дії, ідемпотентності, помилок і підписання вебхуків