Дефекти в бизнес логиката: защо е нужна ръчна проверка
Дефектите в бизнес логиката нарушават правила като лимити за плащания, разделение на одобрения или еднократни действия. Отделните заявки могат да са синтактично валидни, а процесът да допуска неразрешен резултат. Общите скенери често нямат този контекст; човешки анализ и конфигурирани автоматични тестове могат да допринасят след изясняване на правилата.
Как изглеждат
- Заобикаляне на работния процес. Запис прескача направо до крайно състояние „завършено“ без предходните стъпки — а датата му е зададена на каквото нападателят избере.
- Липсващо разделяне на отговорностите. Един оператор може и да инициира, и да одобри действие с голямо въздействие — пренасочване или спиране на плащания — без да е нужен втори участник.
- Злоупотреба с цени и отстъпки. Натрупване на купони, отрицателни количества или гранични случаи с валутите, за да бъде платено далеч по-малко от предвиденото.
- Разминаване между роля и обозначение. Роля, представена като безвредно „самообслужване“, в действителност дава административна власт.
Защо скенерите не могат да ги намерят
Откриването на процесни дефекти изисква разбиране на правилата и смислени опити за нарушаването им. Общите проби могат да пропуснат контекста. Тестери могат да разработват хипотези и да конфигурират инструменти за преходи, конкурентност, replay и известни инварианти; автоматизацията подпомага, но не определя покритието.
Как да се защитите
- Налагайте преходите между състоянията от страна на сървъра. Сървърът, не клиентът, решава в какъв ред могат да се случват нещата.
- Валидирайте правдоподобността на стойностите, подадени от клиента. Датите, количествата, цените и статусите от клиента са предложения, не факти.
- Проектирайте разделянето на отговорностите отвътре. Действията с голямо въздействие изискват втори участник; направете го правило в системата, а не политика в документ.
- Тествайте логиката изрично. Помолете тестерите си да атакуват бизнес правилата, не само OWASP Top 10.
Защо остават незабелязани и при прегледа на кода
Дефектите в бизнес логиката се промъкват покрай прегледа на кода по същата причина, по която се промъкват покрай скенерите: всеки отделен ред е правилен. Дефектът не живее във функция; той живее във взаимодействието между стъпки, всяка от които сама по себе си прави точно това, за което е била помолена. Улавянето му изисква някой да мисли като нападател за целия работен процес наведнъж — да пита „какво ако тези се случат в погрешен ред или със стойности, които проектантът никога не си е представял?“. Точно това осигурява ръчното тестване и затова, преди да тестваме, молим клиентите да ни кажат какво приложението трябва да предотвратява — не само какво трябва да прави.
Тестването на логиката изисква познаване на предметната област
Най-ефективни сме при бизнес логиката, когато клиентът просто ни каже правилата, които приложението трябва да налага — изискването за изпълнител и проверяващ, редът, в който стъпките трябва да се случват, лимитите, които трябва да важат. Разполагайки с предвидената логика, нарушаването ѝ става методично. Без нея дори умел тестер отчасти гадае какво означава „погрешно“. Най-доброто тестване на логика е сътрудничество: вие описвате правилата, ние се опитваме да ги нарушим.
Състояния на състезание (race conditions), TOCTOU и повторно възпроизвеждане: когато уязвимостта е времето
Освен какво има право да направи една заявка, важно е кога и колко пъти може да го направи. При последователно тестване на отделни заявки могат да останат незабелязани проблеми, които се проявяват при едновременно изпълнение или многократно повторение. Например две заявки могат да преминат една и съща проверка, преди системата да запише резултата от първата. Такива състезателни условия могат да нарушат бизнес правилата и да доведат до финансови загуби.
Каноничната форма: баланс, квота, ваучер или наличност се прочита, преценява се като достатъчна и после се намалява — но между прочитането и намаляването втора заявка се промъква през същата проверка спрямо същата непроменена стойност. Всяка заявка поединично минава. Заедно те надхвърлят допустимия лимит. „Прилагай по един купон на поръчка“ става десет купона, когато десет заявки се състезават с единствената проверка.
T0 заявка A: прочита баланс = 100, нужни 100 -> ОК
T0 заявка B: прочита баланс = 100, нужни 100 -> ОК (A още не е потвърдила)
T1 заявка A: дебит 100 -> баланс 0
T1 заявка B: дебит 100 -> баланс -100 стойността е извлечена два пъти
Повторното възпроизвеждане е същият проблем без едновременността: заявка, която трябва да е валидна веднъж — прихващане на плащане, еднократен токен, кредит за препоръка — просто се изпраща отново, защото нищо не я отбелязва като изразходвана. Това не са дефекти в реализацията на една функция; това са липсващи гаранции за атомарност и уникалност през целия работен процес.
Как да укрепите защитата
- Направете проверката и промяната на състоянието атомарни: условно обновяване или заключване на реда, което намалява само ако стойността все още е в сила, а не прочитане, последвано от отделен запис.
- Дайте на операциите, носещи стойност, ключ за идемпотентност, така че повторена или възпроизведена заявка да води до същия единствен резултат вместо до втори ефект.
- Гарантирайте уникалността и еднократната употреба в хранилището за данни, където едновременните транзакции действително се подреждат, а не в код на приложението, който два работни процеса изпълняват паралелно.
- Тествайте умишлено със серии от едновременни идентични заявки — успешният случай с една заявка не ви казва нищо за състезанието.
Моделиране на работния процес като автомат на състоянията
Бизнес логиката е, в основата си, автомат на състоянията: поръчката е кошница → направена → платена → изпратена, акаунтът е поканен → активен → спрян. Дефектите се появяват, когато реализацията позволява преходи, които действителният процес забранява — плащане на поръчка, която вече е била възстановена, изпращане на такава, която никога не е била платена, повторно отваряне на затворен спор, за да бъде получен кредит два пъти. Приложението налага всяка стъпка изолирано, но никога не проверява, че стъпката е законна от текущото състояние.
Начинът да бъдат намерени е предвиденият автомат да бъде нарисуван изрично — състоянията като върхове, законните преходи като ребра — и после за всяко състояние да се попита кои заявки системата действително ще изпълни там. Всяко ребро, което можете да обходите и което не е на диаграмата, е констатация. Това е структурираният братовчед на принудителното обхождане: вместо да гадаете URL адреси, минавате през процеса в погрешен ред и наблюдавате за стъпка, която е трябвало да бъде отказана.
направена --> платена --> изпратена
| |
v v
отменена възстановена незаконно: възстановена --> изпратена
незаконно: изпратена --> платена (двойно прихващане)
Целите с висока стойност са преходите, които движат пари, дават права или прекосяват граница на доверие. Потвърдете, че всеки от тях е охраняван не само от „валидни ли са тези входни данни“, но и от „разрешен ли е този преход от състоянието, в което обектът се намира точно сега, за този участник“.
Как да укрепите защитата
- Представяйте състоянието на работния процес изрично и валидирайте всеки преход спрямо текущото състояние от страна на сървъра, като отхвърляте всяко ребро, което не е в модела.
- Охранявайте преходите, които прехвърлят средства или предоставят права, с най-строгите проверки и ги правете идемпотентни, така че повторение да не може да даде правата отново.
- Дръжте автомата на състоянията на едно авторитетно място, вместо да извеждате отново „какво е разрешено след това“ при всяка крайна точка, където правилата се разминават.
Подправяне на параметри и опасността от доверие по подразбиране
Дефектите в логиката се крият и в допускането, че каквото клиентът е изпратил, отразява това, което му е било предложено. Цени, количества, отстъпки, идентификатори на нива, акаунти на получатели и суми за доставка, които пристигат в заявката, често се приемат на доверие, защото стандартният клиентски интерфейс никога не би изпратила погрешна. Нападателят не използва стандартният клиентски интерфейс. Отрицателни количества, които кредитират вместо да начисляват, поле с цена, върнато от скрита стойност във формуляр, процент отстъпка, вдигнат над 100, ниво, повишено чрез редактиране на един параметър — всяко от тях е случай, в който сървърът се доверява на число, което е трябвало сам да изчисли или да валидира отново.
Дисциплината е всяка стойност, относима към сигурността или цената, да бъде третирана като нещо, което сървърът притежава. Клиентът може да каже кой продукт и колко; сървърът решава цената, проверява отново границите и преизчислява сумата от авторитетни данни. Всичко, което клиентът има право да твърди, трябва да бъде валидирано спрямо това, което в действителност му е било позволено да твърди.
Как да укрепите защитата
- Преизчислявайте цените, сумите, отстъпките и правата от страна на сървъра от доверени записи; никога не ги приемайте от заявката като факт.
- Ограничавайте всяко количество (без отрицателни, разумни максимуми) и валидирайте идентификаторите, твърдени от клиента, спрямо това, което този участник има право да избира.
- Не прекарвайте чувствителни стойности през скрити полета или състояние от страна на клиента, където могат да бъдат редактирани в движение.
Тестване на собствената ви логика, преди някой друг да го направи
Тъй като тези дефекти са специфични за това как работи вашият процес, никой готов инструмент няма да ги намери за вас — но собственият ви екип може, евтино, преди една оценка да го направи. Методът е разговор, не скенер: поставете хората, които разбират бизнес правилата, до хората, които разбират заявките, и за всяко правило попитайте „какво се случва, ако потребителят просто не го спази?“.
- Изброете правилата като изречения. „Купонът се прилага веднъж.“ „Не можете да възстановите повече, отколкото сте платили.“ „Само собственикът отменя резервацията.“ Всяко изречение е тест: опитайте да го нарушите директно срещу API-то.
- Обходете автомата на състоянията в погрешен ред. Прекарайте действителен обект през стъпките му, после опитайте всяка стъпка от погрешното състояние и от сесията на втори акаунт.
- Изпълнявайте едновременно действията с финансово значение. Изстреляйте едновременни и повтарящи се идентични заявки към всичко, което харчи, кредитира, осребрява или резервира, и сравнете крайното състояние с това, което е трябвало да се случи.
- Подправяйте всяко число. Възпроизведете заявките с редактирани цени, количества, идентификатори и суми и потвърдете, че сървърът е преизчислил, вместо да се е доверил.
Напишете тези проверки като повторяеми тестове, които проверяват резултата — крайния баланс, статуса на поръчката, дадените права — а не само HTTP кода, и ги изпълнявайте в CI. Дефект в логиката, който регресионен тест може да улови, е такъв, който нападателят никога няма да успее да отчете.
Къде е дефектът в логиката на вашето приложение?
Ръчното тестване за проникване е насочено към пропуските в бизнес логиката и разделянето на отговорностите, които скенерите не могат да видят.
Заявете предложение