Paano magsulat ng mga panuntunan para sa Checkmarx nang hindi nababaliw

Hoy Habr!

Sa aming trabaho, ang aming kumpanya ay madalas na nakikitungo sa iba't ibang mga static code analysis tool (SAST). Out of the box lahat sila ay karaniwang gumagana. Siyempre, ang lahat ay nakasalalay sa proyekto at mga teknolohiyang ginamit dito, pati na rin kung gaano kahusay ang mga teknolohiyang ito ay sakop ng mga patakaran ng pagsusuri. Sa palagay ko, isa sa pinakamahalagang pamantayan kapag pumipili ng tool na SAST ay ang kakayahang i-customize ito sa mga detalye ng iyong mga application, ibig sabihin, magsulat at magbago ng mga panuntunan sa pagsusuri o, bilang mas madalas na tawag sa kanila, Mga Custom na Query.

Paano magsulat ng mga panuntunan para sa Checkmarx nang hindi nababaliw

Madalas naming ginagamit ang Checkmarx - isang napaka-interesante at makapangyarihang code analyzer. Sa artikulong ito ay pag-uusapan ko ang aking karanasan sa pagsulat ng mga panuntunan sa pagsusuri para dito.

Talaan ng nilalaman

Pagpasok

Upang magsimula, nais kong magrekomenda ng isa sa ilang mga artikulo sa Russian tungkol sa mga tampok ng pagsusulat ng mga query para sa Checkmarx. Nai-publish ito sa Habré sa pagtatapos ng 2019 sa ilalim ng pamagat: "Hello, Checkmarx!" Paano magsulat ng isang Checkmarx SAST na query at maghanap ng mga cool na kahinaan.

Sinusuri nito nang detalyado kung paano isulat ang mga unang query sa CxQL (Checkmarx Query Language) para sa ilang aplikasyon sa pagsubok at ipinapakita ang mga pangunahing prinsipyo kung paano gumagana ang mga panuntunan sa pagsusuri.

Hindi ko na uulitin ang inilarawan dito, bagama't may mga intersection pa rin. Sa aking artikulo ay susubukan kong mag-compile ng isang uri ng "koleksyon ng mga recipe", isang listahan ng mga solusyon sa mga partikular na problema na naranasan ko sa panahon ng aking trabaho sa Checkmarx. Kinailangan kong i-rack ang aking utak sa marami sa mga problemang ito. Minsan ay walang sapat na impormasyon sa dokumentasyon, at kung minsan ay mahirap ding maunawaan kung paano gawin ang kinakailangan. Sana ay hindi mawawalan ng saysay ang aking karanasan at mga gabing walang tulog, at ang "koleksyon ng mga recipe ng Custom na Query" na ito ay makakatipid sa iyo ng ilang oras o ilang nerve cell. Kaya, magsimula tayo!

Pangkalahatang impormasyon sa mga patakaran

Una, tingnan natin ang ilang pangunahing konsepto at ang proseso ng pagtatrabaho sa mga panuntunan, para sa isang mas mahusay na pag-unawa sa kung ano ang susunod na mangyayari. At dahil din ang dokumentasyon ay walang sinasabi tungkol dito o napakalat sa istraktura, na hindi masyadong maginhawa.

  1. Ang mga panuntunan ay inilalapat sa panahon ng pag-scan depende sa preset na pinili sa simula (isang hanay ng mga aktibong panuntunan). Maaari kang lumikha ng walang limitasyong bilang ng mga preset, at ang eksaktong paraan kung paano ayusin ang mga ito ay depende sa mga detalye ng iyong proseso. Maaari mong pangkatin ang mga ito ayon sa wika o pumili ng mga preset para sa bawat proyekto. Ang bilang ng mga aktibong panuntunan ay nakakaapekto sa bilis at katumpakan ng pag-scan.

    Paano magsulat ng mga panuntunan para sa Checkmarx nang hindi nababaliwPagse-set up ng Preset sa Checkmarx interface

  2. Ang mga patakaran ay na-edit sa isang espesyal na tool na tinatawag na CxAuditor. Ito ay isang desktop application na kumokonekta sa isang server na tumatakbo sa Checkmarx. Ang tool na ito ay may dalawang mode ng operasyon: pag-edit ng mga panuntunan at pagsusuri sa mga resulta ng isang naisagawa na pag-scan.

    Paano magsulat ng mga panuntunan para sa Checkmarx nang hindi nababaliwInterface ng CxAudit

  3. Ang mga panuntunan sa Checkmarx ay hinati ayon sa wika, ibig sabihin, ang bawat wika ay may sariling hanay ng mga query. Mayroon ding ilang pangkalahatang tuntunin na nalalapat anuman ang wika, ito ang tinatawag na mga pangunahing query. Para sa karamihan, ang mga pangunahing query ay kinabibilangan ng paghahanap ng impormasyon na ginagamit ng ibang mga panuntunan.

    Paano magsulat ng mga panuntunan para sa Checkmarx nang hindi nababaliwPaghahati ng mga tuntunin ayon sa wika

  4. Ang mga Panuntunan ay "Mapapatupad" at "Hindi Mapapatupad" (Isinasagawa at Hindi Naisasakatuparan). Hindi masyadong ang tamang pangalan, sa aking opinyon, ngunit iyon ay kung ano ito. Ang bottom line ay ang resulta ng pagpapatupad ng mga "Executable" na panuntunan ay ipapakita sa mga resulta ng pag-scan sa UI, at ang "Non-Executable" na mga panuntunan ay kailangan lang para magamit ang kanilang mga resulta sa iba pang mga kahilingan (sa esensya, isang function lang).

    Paano magsulat ng mga panuntunan para sa Checkmarx nang hindi nababaliwPagtukoy sa uri ng panuntunan kapag lumilikha

  5. Maaari kang lumikha ng mga bagong panuntunan o dagdagan/muling isulat ang mga umiiral na. Upang muling isulat ang isang panuntunan, kailangan mong hanapin ito sa puno, i-right-click at piliin ang "I-override" mula sa drop-down na menu. Mahalagang tandaan dito na ang mga bagong panuntunan ay hindi paunang kasama sa mga preset at hindi aktibo. Upang simulan ang paggamit ng mga ito kailangan mong i-activate ang mga ito sa menu na "Preset Manager" sa instrumento. Pinapanatili ng mga muling isinulat na panuntunan ang kanilang mga setting, ibig sabihin, kung aktibo ang panuntunan, mananatili itong ganoon at ilalapat kaagad.

    Paano magsulat ng mga panuntunan para sa Checkmarx nang hindi nababaliwHalimbawa ng bagong panuntunan sa interface ng Preset Manager

  6. Sa panahon ng pagpapatupad, ang isang "puno" ng mga kahilingan ay itinayo, na depende sa kung ano. Ang mga patakaran na nangongolekta ng impormasyon ay unang isinasagawa, at ang mga gumagamit nito ay pangalawa. Ang resulta ng pagpapatupad ay naka-cache, kaya kung posible na gamitin ang mga resulta ng isang umiiral na panuntunan, kung gayon mas mahusay na gawin ito, mababawasan nito ang oras ng pag-scan.

  7. Maaaring ilapat ang mga panuntunan sa iba't ibang antas:

  • Para sa buong sistema - gagamitin para sa anumang pag-scan ng anumang proyekto

  • Sa antas ng koponan (Koponan) - gagamitin lamang upang i-scan ang mga proyekto sa napiling koponan.

  • Sa antas ng proyekto - Ilalapat sa isang partikular na proyekto

    Paano magsulat ng mga panuntunan para sa Checkmarx nang hindi nababaliwPagtukoy sa antas kung saan ilalapat ang panuntunan

"Diksyunaryo" para sa mga nagsisimula

At magsisimula ako sa ilang bagay na nagdulot sa akin ng mga tanong, at magpapakita din ako ng ilang mga diskarte na makabuluhang magpapasimple sa buhay.

Mga operasyon na may mga listahan

- вычитание одного из другого (list2 - list1)
* пересечение списков (list1 * list2)
+ сложение списков (list1 + list2)

& (логическое И) - объединяет списки по совпадению (list1 & list2), аналогично пересечению (list1 * list2)
| (логическое ИЛИ) - объединяет списки по широкому поиску (list1 | list2)

Со списками не работает:  ^  &&  ||  %  / 

Lahat ng nahanap na item

Sa loob ng na-scan na wika, maaari kang makakuha ng isang listahan ng ganap na lahat ng elemento na natukoy ng Checkmarx (mga string, function, klase, pamamaraan, atbp.). Ito ay ilang espasyo ng mga bagay na maaaring ma-access sa pamamagitan ng All. Iyon ay, upang maghanap para sa isang bagay na may isang tiyak na pangalan searchMe, maaari kang maghanap, halimbawa, ayon sa pangalan sa lahat ng nahanap na bagay:

// Такой запрос выдаст все элементы
result = All;

// Такой запрос выдаст все элементы, в имени которых присутствует “searchMe“
result = All.FindByName("searchMe");

Ngunit, kung kailangan mong maghanap sa ibang wika na sa ilang kadahilanan ay hindi kasama sa pag-scan (halimbawa, groovy sa isang Android project), maaari mong palawakin ang aming object space sa pamamagitan ng isang variable:

result = AllMembers.All.FindByName("searchMe");

Mga Pag-andar para sa Pagsusuri ng Daloy

Ang mga function na ito ay ginagamit sa maraming mga panuntunan at narito ang isang maliit na cheat sheet ng kung ano ang ibig sabihin ng mga ito:

// Какие данные second влияют на first.
// Другими словами - ТО (second) что влияет на  МЕНЯ (first).
result = first.DataInfluencedBy(second);

// Какие данные first влияют на second.
// Другими словами - Я (first) влияю на ТО (second).
result = first.DataInfluencingOn(second);

Pagkuha ng pangalan/path ng file

Mayroong ilang mga katangian na maaaring makuha mula sa mga resulta ng isang query (ang pangalan ng file kung saan natagpuan ang entry, string, atbp.), ngunit hindi sinasabi ng dokumentasyon kung paano makuha at gamitin ang mga ito. Kaya, upang magawa ito, kailangan mong i-access ang LinePragma property at ang mga bagay na kailangan namin ay matatagpuan sa loob nito:

// Для примера найдем все методы
CxList methods = Find_Methods();

// В методах найдем по имени метод scope
CxList scope = methods.FindByName("scope");

// Таким образом можо получить путь к файлу
string current_filename = scope.GetFirstGraph().LinePragma.FileName;

// А вот таким - строку, где нашлось срабатывание
int current_line = scope.GetFirstGraph().LinePragma.Line;

// Эти параметры можно использовать по разному
// Например получить все объекты в файле
CxList inFile = All.FindByFileName(current_filename);

// Или найти что происходит в конкретной строке
CxList inLine = inFile.FindByPosition(current_line);

Ito ay nagkakahalaga na isaisip iyon FileName naglalaman talaga ng landas sa file, dahil ginamit namin ang pamamaraan GetFirstGraph.

Resulta ng pagpapatupad

Mayroong isang espesyal na variable sa loob ng CxQL result, na nagbabalik ng resulta ng pagpapatupad ng iyong nakasulat na panuntunan. Agad itong sinisimulan at maaari mong isulat ang mga intermediate na resulta dito, binabago at pinipino ang mga ito habang nagtatrabaho ka. Ngunit, kung walang pagtatalaga sa variable o function na ito sa loob ng panuntunan return— ang resulta ng pagpapatupad ay palaging magiging zero.

Ang sumusunod na query ay hindi magbabalik ng anuman sa amin bilang resulta ng pagpapatupad at palaging walang laman:

// Находим элементы foo
CxList libraries = All.FindByName("foo");

Ngunit, nang italaga ang resulta ng pagpapatupad sa resulta ng magic variable, makikita natin kung ano ang ibinabalik sa atin ng tawag na ito:

// Находим элементы foo
CxList libraries = All.FindByName("foo");

// Выводим, как результат выполнения правила
result = libraries

// Или еще короче
result = All.FindByName("foo");

Gamit ang mga resulta ng iba pang mga patakaran

Ang mga panuntunan sa Checkmarx ay maaaring tawaging kahalintulad sa mga function sa isang regular na programming language. Kapag nagsusulat ng panuntunan, maaari mong gamitin ang mga resulta ng iba pang mga query. Halimbawa, hindi na kailangang maghanap para sa lahat ng mga tawag sa pamamaraan sa code sa bawat oras, tawagan lamang ang nais na panuntunan:

// Получаем результат выполнения другого правила
CxList methods = Find_Methods();

// Ищем внутри метод foo. 
// Второй параметр false означает, что ищем без чувствительности к регистру
result = methods.FindByShortName("foo", false);

Binibigyang-daan ka ng diskarteng ito na paikliin ang code at makabuluhang bawasan ang oras ng pagpapatupad ng panuntunan.

Solusyon ng mga problema

Pagtotroso

Kapag nagtatrabaho sa tool, kung minsan ay hindi posible na agad na isulat ang nais na query at kailangan mong mag-eksperimento, subukan ang iba't ibang mga pagpipilian. Para sa ganoong kaso, ang tool ay nagbibigay ng pag-log, na tinatawag na sumusunod:

// Находим что-то
CxList toLog = All.FindByShortName("log");

// Формируем строку и отправляем в лог
cxLog.WriteDebugMessage (“number of DOM elements =” + All.Count);

Ngunit ito ay nagkakahalaga ng pag-alala na ang pamamaraang ito ay tumatanggap lamang bilang input string, kaya hindi posibleng magpakita ng kumpletong listahan ng mga nahanap na elemento bilang resulta ng unang operasyon. Ang pangalawang opsyon, na ginagamit para sa pag-debug, ay ang magtalaga sa isang magic variable paminsan-minsan result ang resulta ng query at tingnan kung ano ang mangyayari. Ang diskarte na ito ay hindi masyadong maginhawa; kailangan mong tiyakin na walang mga override o pagpapatakbo dito sa code pagkatapos result o magkomento lang ng code sa ibaba. O maaari mong, tulad ko, kalimutang tanggalin ang ilang ganoong mga tawag mula sa isang nakahanda nang panuntunan at magtaka kung bakit walang gumagana.

Ang isang mas maginhawang paraan ay ang pagtawag sa pamamaraan return kasama ang kinakailangang parameter. Sa kasong ito, magtatapos ang pagpapatupad ng panuntunan at makikita natin kung ano ang nangyari bilang resulta ng isinulat natin:

// Находим что-то
CxList toLog = All.FindByShortName("log");

// Выводим результат выполнения
return toLog

//Все, что написано дальше не будет выполнено
result = All.DataInfluencedBy(toLog)

Problema sa pag-login

May mga sitwasyon na hindi mo ma-access ang CxAudit tool (na ginagamit para magsulat ng mga panuntunan). Maaaring magkaroon ng maraming dahilan para dito, kabilang ang mga pag-crash, biglaang pag-update ng Windows, BSOD at iba pang hindi inaasahang sitwasyon na lampas sa aming kontrol. Sa kasong ito, kung minsan ay may hindi natapos na sesyon sa database, na pumipigil sa iyo na mag-log in muli. Upang ayusin ito, kailangan mong magpatakbo ng ilang mga query:

Para sa Checkmarx bago ang 8.6:

// Проверяем, что есть залогиненые пользователи, выполнив запрос в БД
SELECT COUNT(*) FROM [CxDB].[dbo].LoggedinUser WHERE [ClientType] = 6;
 
// Если что-то есть, а на самом деле даже если и нет, попробовать выполнить запрос
DELETE FROM [CxDB].[dbo].LoggedinUser WHERE [ClientType] = 6;

Para sa Checkmarx pagkatapos ng 8.6:

// Проверяем, что есть залогиненые пользователи, выполнив запрос в БД
SELECT COUNT(*) FROM LoggedinUser WHERE (ClientType = 'Audit');
 
// Если что-то есть, а на самом деле даже если и нет, попробовать выполнить запрос
DELETE FROM [CxDB].[dbo].LoggedinUser WHERE (ClientType = 'Audit');

Panuntunan sa pagsulat

Ngayon makarating tayo sa pinakakagiliw-giliw na bahagi. Kapag nagsimula kang magsulat ng mga panuntunan sa CxQL, ang madalas mong kulang ay hindi gaanong dokumentasyon tulad ng ilang buhay na halimbawa ng paglutas ng ilang partikular na problema at paglalarawan sa proseso kung paano gumagana ang mga query sa pangkalahatan.

Susubukan kong gawing mas madali ang buhay para sa mga nagsisimulang sumisid sa wika ng query at magbigay ng ilang halimbawa ng paggamit ng Custom na Mga Query upang malutas ang ilang partikular na problema. Ang ilan sa mga ito ay medyo pangkalahatan at maaaring magamit sa iyong kumpanya nang halos walang mga pagbabago, ang iba ay mas tiyak, ngunit maaari rin silang gamitin sa pamamagitan ng pagbabago ng code upang umangkop sa mga detalye ng iyong mga application.

Kaya, narito ang mga problemang madalas naming nakatagpo:

Problema: Mayroong ilang mga Daloy sa mga resulta ng pagpapatupad ng panuntunan at ang isa sa mga ito ay isang pugad ng isa pa, dapat mong iwanan ang isa sa mga ito.

solusyon: Sa katunayan, minsan ang Checkmarx ay nagpapakita ng ilang daloy ng data na maaaring mag-overlap at maging isang pinaikling bersyon ng iba. Mayroong isang espesyal na pamamaraan para sa mga naturang kaso ReduceFlow. Depende sa parameter, pipiliin nito ang pinakamaikling o pinakamahabang Daloy:

// Оставить только длинные Flow
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceSmallFlow);

// Оставить только короткие Flow
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceBigFlow);

Problema: Palawakin ang listahan ng sensitibong data kung saan tumutugon ang tool

solusyon: Ang Checkmarx ay may mga pangunahing panuntunan, ang mga resulta nito ay ginagamit ng maraming iba pang mga query. Sa pamamagitan ng pagdaragdag sa ilan sa mga panuntunang ito ng data na partikular sa iyong aplikasyon, maaari mong agad na mapabuti ang iyong mga resulta ng pag-scan. Nasa ibaba ang isang halimbawang panuntunan para makapagsimula ka:

Pangkalahatang_privacy_violation_list

Magdagdag tayo ng ilang variable na ginagamit sa ating application para mag-imbak ng sensitibong impormasyon:

// Получаем результат выполнения базового правила
result = base.General_privacy_violation_list();

// Ищем элементы, которые попадают под простые регулярные выражения. Можно дополнить характерными для вас паттернами.
CxList personalList = All.FindByShortNames(new List<string> {
	"*securityToken*", "*sessionId*"}, false);

// Добавляем к конечному результату
result.Add(personalList);

Problema: Palawakin ang listahan ng mga variable na may mga password

solusyon: Iminumungkahi kong bigyang pansin kaagad ang pangunahing panuntunan para sa pagtukoy ng mga password sa code at pagdaragdag dito ng listahan ng mga variable na pangalan na karaniwang ginagamit sa iyong kumpanya.

Password_privacy_violation_list

CxList allStrings = All.FindByType("String"); 
allStrings.Add(All.FindByType(typeof(StringLiteral))); 
allStrings.Add(Find_UnknownReference());
allStrings.Add(All.FindByType(typeof (Declarator)));
allStrings.Add(All.FindByType(typeof (MemberAccess)));
allStrings.Add(All.FindByType(typeof(EnumMemberDecl))); 
allStrings.Add(Find_Methods().FindByShortName("get*"));

// Дополняем дефолтный список переменных
List < string > pswdIncludeList = new List<string>{"*password*", "*psw", "psw*", "pwd*", "*pwd", "*authKey*", "pass*", "cipher*", "*cipher", "pass", "adgangskode", "benutzerkennwort", "chiffre", "clave", "codewort", "contrasena", "contrasenya", "geheimcode", "geslo", "heslo", "jelszo", "kennwort", "losenord", "losung", "losungswort", "lozinka", "modpas", "motdepasse", "parol", "parola", "parole", "pasahitza", "pasfhocal", "passe", "passord", "passwort", "pasvorto", "paswoord", "salasana", "schluessel", "schluesselwort", "senha", "sifre", "wachtwoord", "wagwoord", "watchword", "zugangswort", "PAROLACHIAVE", "PAROLA CHIAVE", "PAROLECHIAVI", "PAROLE CHIAVI", "paroladordine", "verschluesselt", "sisma",
                "pincode",
								"pin"};
								
List < string > pswdExcludeList = new List<string>{"*pass", "*passable*", "*passage*", "*passenger*", "*passer*", "*passing*", "*passion*", "*passive*", "*passover*", "*passport*", "*passed*", "*compass*", "*bypass*", "pass-through", "passthru", "passthrough", "passbytes", "passcount", "passratio"};

CxList tempResult = allStrings.FindByShortNames(pswdIncludeList, false);
CxList toRemove = tempResult.FindByShortNames(pswdExcludeList, false);
tempResult -= toRemove;
tempResult.Add(allStrings.FindByShortName("pass", false));

foreach (CxList r in tempResult)
{
	CSharpGraph g = r.data.GetByIndex(0) as CSharpGraph;
	if(g != null && g.ShortName != null && g.ShortName.Length < 50)
	{
		result.Add(r);
	}
}

Problema: Magdagdag ng mga ginamit na framework na hindi sinusuportahan ng Checkmarx

solusyon: Ang lahat ng mga query sa Checkmarx ay hinati ayon sa wika, kaya kailangan mong magdagdag ng mga panuntunan para sa bawat wika. Nasa ibaba ang ilang halimbawa ng naturang mga patakaran.

Kung ang mga aklatan ay ginagamit na umakma o pumapalit sa karaniwang paggana, madali silang maidaragdag sa pangunahing panuntunan. Pagkatapos ang lahat ng gumagamit nito ay agad na malalaman ang tungkol sa mga bagong pagpapakilala. Bilang halimbawa, ang mga aklatan para sa pag-log in sa Android ay Timber at Loggi. Sa pangunahing package, walang mga panuntunan para sa pagtukoy ng mga hindi system na tawag, kaya kung ang isang password o session identifier ay pumasok sa log, hindi namin malalaman ang tungkol dito. Subukan nating magdagdag ng mga kahulugan ng mga ganitong pamamaraan sa mga panuntunan ng Checkmarx.

Halimbawa ng test code na gumagamit ng Timber library para sa pag-log:

package com.death.timberdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import timber.log.Timber;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Timber.e("Error Message");
        Timber.d("Debug Message");

        Timber.tag("Some Different tag").e("And error message");
    }
}

At narito ang isang halimbawa ng isang kahilingan para sa Checkmarx, na magbibigay-daan sa iyong magdagdag ng kahulugan ng pagtawag sa mga pamamaraan ng Timber bilang exit point para sa data mula sa application:

FindAndroidOutputs

// Получаем результат выполнения базового правила
result = base.Find_Android_Outputs();

// Дополняем вызовами, которые приходят из библиотеки Timber
CxList timber = All.FindByExactMemberAccess("Timber.*") +
    All.FindByShortName("Timber").GetMembersOfTarget();

// Добавляем к конечному результату
result.Add(timber);

At maaari ka ring magdagdag sa kalapit na panuntunan, ngunit ito ay direktang nauugnay sa pag-log in sa Android:

FindAndroidLog_Outputs

// Получаем результат выполнения базового правила
result = base.Find_Android_Log_Outputs();

// Дополняем вызовами, которые приходят из библиотеки Timber
result.Add(
  All.FindByExactMemberAccess("Timber.*") +
  All.FindByShortName("Timber").GetMembersOfTarget()
);

Gayundin, kung gumagamit ang mga Android application WorkManager para sa asynchronous na gawain, magandang ideya na ipaalam sa Checkmarx ang tungkol dito sa pamamagitan ng pagdaragdag ng paraan para sa pagkuha ng data mula sa gawain getInputData:

FindAndroidRead

// Получаем результат выполнения базового правила
result = base.Find_Android_Read();

// Дополняем вызовом функции getInputData, которая используется в WorkManager
CxList getInputData = All.FindByShortName("getInputData");

// Добавляем к конечному результату
result.Add(getInputData.GetMembersOfTarget());

Problema: Naghahanap ng sensitibong data sa plist para sa mga proyekto ng iOS

solusyon: Madalas na gumagamit ang iOS ng mga espesyal na file na may extension na .plist para mag-imbak ng iba't ibang variable at value. Ang pag-iimbak ng mga password, token, key at iba pang sensitibong data sa mga file na ito ay hindi inirerekomenda, dahil maaari silang makuha mula sa device nang walang anumang problema.

Ang mga plist file ay may mga tampok na hindi halata sa mata, ngunit mahalaga sa Checkmarx. Sumulat tayo ng isang panuntunan na hahanapin ang data na kailangan natin at sasabihin sa amin kung ang mga password o token ay nabanggit sa isang lugar.

Isang halimbawa ng ganoong file, na naglalaman ng isang token para sa komunikasyon sa serbisyo ng backend:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>DeviceDictionary</key>
	<dict>
		<key>phone</key>
		<string>iPhone 6s</string>
	</dict>
	<key>privatekey</key>
	<string>MIICXAIBAAKBgQCqGKukO1De7zhZj6+</string>
</dict>
</plist>

At isang panuntunan para sa Checkmarx, na mayroong maraming mga nuances na dapat isaalang-alang kapag nagsusulat:

// Используем результат выполнения правила по поиску файлов plist, чтобы уменьшить время работы правила и 
CxList plist = Find_Plist_Elements();

// Инициализируем новую переменную
CxList dictionarySettings = All.NewCxList();

// Теперь добавим поиск всех интересующих нас значений. В дальнейшем можно расширять этот список.
// Для поиска значений, как ни странно, используется FindByMemberAccess - поиск обращений к методам. Второй параметр внутри функции, false, означает, что поиск нечувствителен к регистру
dictionarySettings.Add(plist.FindByMemberAccess("privatekey", false));
dictionarySettings.Add(plist.FindByMemberAccess("privatetoken", false));

// Для корректного поиска из-за особенностей структуры plist - нужно искать по типу "If statement"
CxList ifStatements = plist.FindByType(typeof(IfStmt));

// Добавляем в результат, перед этим получив родительский узел - для правильного отображения
result = dictionarySettings.FindByFathers(ifStatements);

Problema: Paghahanap ng impormasyon sa XML

solusyon: Ang Checkmarx ay may napakaginhawang function para sa pagtatrabaho sa XML at paghahanap ng mga value, tag, attribute at higit pa. Ngunit, sa kasamaang-palad, nagkaroon ng error sa dokumentasyon dahil sa kung saan walang isang halimbawa ang gumagana. Sa kabila ng katotohanan na ang depektong ito ay inalis sa pinakabagong bersyon ng dokumentasyon, mag-ingat kung gagamit ka ng mga naunang bersyon ng mga dokumento.

Narito ang isang maling halimbawa mula sa dokumentasyon:

// Код работать не будет
result = All.FindXmlAttributesByNameAndValue("*.app", 8, “id”, "error- section", false, true);

Bilang resulta ng pagtatangka sa pagpapatupad, makakatanggap kami ng error na iyon All walang ganoong paraan... At ito ay totoo, dahil mayroong isang espesyal, hiwalay na espasyo ng bagay para sa paggamit ng mga function para sa pagtatrabaho sa XML - cxXPath. Ganito ang hitsura ng tamang query upang makahanap ng setting sa Android na nagbibigay-daan sa paggamit ng trapiko ng HTTP:

// Правильный вариант с использованием cxXPath
result = cxXPath.FindXmlAttributesByNameAndValue("*.xml", 8, "cleartextTrafficPermitted", "true", false, true);

Tingnan natin ito nang mas detalyado, dahil ang syntax para sa lahat ng mga pag-andar ay magkatulad, pagkatapos mong malaman ang isa, kailangan mo lamang piliin ang kailangan mo. Kaya, sunud-sunod ayon sa mga parameter:

  • "*.xml"— mask ng mga file na hahanapin

  • 8 — id ng wika kung saan inilalapat ang panuntunan

  • "cleartextTrafficPermitted"- pangalan ng katangian sa xml

  • "true" — ang halaga ng katangiang ito

  • false — paggamit ng regular na expression kapag naghahanap

  • true — nangangahulugan na isasagawa ang paghahanap nang hindi pinapansin ang case, iyon ay, case-insensitive

Bilang halimbawa, gumamit kami ng panuntunan na tumutukoy sa hindi tama, mula sa punto ng seguridad, mga setting ng koneksyon sa network sa Android na nagbibigay-daan sa pakikipag-ugnayan sa server sa pamamagitan ng HTTP protocol. Halimbawa ng isang setting na naglalaman ng isang katangian cleartextTrafficPermitted may kahulugan true:

<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
        <domain-config cleartextTrafficPermitted="true">
            <domain includeSubdomains="true">secure.example.com</domain>
        </domain-config>
    </domain-config>
</network-security-config>

Problema: Limitahan ang mga resulta ayon sa pangalan/path ng file

solusyon: Sa isa sa malalaking proyektong nauugnay sa pagbuo ng isang mobile application para sa Android, nakatagpo kami ng mga maling positibo sa panuntunang tumutukoy sa setting ng obfuscation. Ang katotohanan ay ang panuntunan sa labas ng kahon ay naghahanap sa file build.gradle isang setting na responsable para sa paglalapat ng mga panuntunan sa obfuscation para sa release na bersyon ng application.

Ngunit sa malalaking proyekto minsan may mga file ng bata build.gradle, na tumutukoy sa mga aklatan na kasama sa proyekto. Ang kakaiba ay kahit na ang mga file na ito ay hindi nagpapahiwatig ng pangangailangan para sa obfuscation, ang mga setting ng parent assembly file ay ilalapat sa panahon ng compilation.

Kaya, ang gawain ay putulin ang mga trigger sa mga child file na kabilang sa mga aklatan. Maaari silang makilala sa pamamagitan ng pagkakaroon ng linya apply 'com.android.library'.

Halimbawang code mula sa file build.gradle, na tumutukoy sa pangangailangan para sa obfuscation:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"
    defaultConfig {
        ...
    }

    buildTypes {
        release {
            minifyEnabled true
            ...
        }
    }
}

dependencies {
  ...
}

Halimbawa ng file build.gradle para sa isang library na kasama sa proyekto na walang ganitong setting:

apply plugin: 'android-library'

dependencies {
  compile 'com.android.support:support-v4:18.0.+'
}

android {
  compileSdkVersion 14
  buildToolsVersion '17.0.0'
  ...
}

At ang panuntunan para sa Checkmarx:

ProGuardObfuscationNotInUse

// Поиск метода release среди всех методов в Gradle файлах
CxList releaseMethod = Find_Gradle_Method("release");

// Все объекты из файлов build.gradle
CxList gradleBuildObjects = Find_Gradle_Build_Objects();

// Поиск того, что находится внутри метода "release" среди всех объектов из файлов build.gradle
CxList methodInvokesUnderRelease = gradleBuildObjects.FindByType(typeof(MethodInvokeExpr)).GetByAncs(releaseMethod);

// Ищем внутри gradle-файлов строку "com.android.library" - это значит, что данный файл относится к библиотеке и его необходимо исключить из правила
CxList android_library = gradleBuildObjects.FindByName("com.android.library");

// Инициализация пустого массива
List<string> libraries_path = new List<string> {};

// Проходим через все найденные "дочерние" файлы
foreach(CxList library in android_library)
{
    // Получаем путь к каждому файлу
	string file_name_library = library.GetFirstGraph().LinePragma.FileName;
    
    // Добавляем его в наш массив
	libraries_path.Add(file_name_library);
}

// Ищем все вызовы включения обфускации в релизных настройках
CxList minifyEnabled = methodInvokesUnderRelease.FindByShortName("minifyEnabled");

// Получаем параметры этих вызовов
CxList minifyValue = gradleBuildObjects.GetParameters(minifyEnabled, 0);

// Ищем среди них включенные
CxList minifyValueTrue = minifyValue.FindByShortName("true");

// Немного магии, если не нашли стандартным способом :D
if (minifyValueTrue.Count == 0) {
	minifyValue = minifyValue.FindByAbstractValue(abstractValue => abstractValue is TrueAbstractValue);
} else {
    // А если всё-таки нашли, то предыдущий результат и оставляем
	minifyValue = minifyValueTrue;	
}

// Если не нашлось таких методов
if (minifyValue.Count == 0)
{
    // Для более корректного отображения места срабатывания в файле ищем или buildTypes или android
	CxList tempResult = All.NewCxList();
	CxList buildTypes = Find_Gradle_Method("buildTypes");
	if (buildTypes.Count > 0) {
		tempResult = buildTypes;
	} else {
		tempResult = Find_Gradle_Method("android");
	}
	
	// Для каждого из найденных мест срабатывания проходим и определяем, дочерний или основной файлы сборки
	foreach(CxList res in tempResult)
	{
        // Определяем, в каком файле был найден buildType или android методы
		string file_name_result = res.GetFirstGraph().LinePragma.FileName;
        
        // Если такого файла нет в нашем списке "дочерних" файлов - значит это основной файл и его можно добавить в результат
		if (libraries_path.Contains(file_name_result) == false){
			result.Add(res);
		}
	}
}

Ang diskarte na ito ay maaaring maging pangkalahatan at kapaki-pakinabang hindi lamang para sa mga application ng Android, kundi pati na rin para sa iba pang mga kaso kung kailan kailangan mong matukoy kung ang isang resulta ay kabilang sa isang partikular na file.

Problema: Magdagdag ng suporta para sa isang third party na library kung ang syntax ay hindi ganap na suportado

solusyon: Ang bilang ng iba't ibang mga frameworks na ginagamit sa proseso ng pagsulat ng code ay wala sa mga chart. Siyempre, hindi palaging alam ng Checkmarx ang tungkol sa kanilang pag-iral, at ang aming gawain ay turuan itong maunawaan na ang ilang mga pamamaraan ay partikular na nabibilang sa balangkas na ito. Minsan ito ay kumplikado sa pamamagitan ng katotohanan na ang mga balangkas ay gumagamit ng mga pangalan ng function na napakakaraniwan at imposibleng malinaw na matukoy ang kaugnayan ng isang partikular na tawag sa isang partikular na library.

Ang kahirapan ay ang syntax ng naturang mga aklatan ay hindi palaging kinikilala nang tama at kailangan mong mag-eksperimento upang maiwasan ang pagkuha ng isang malaking bilang ng mga maling positibo. Mayroong ilang mga opsyon upang mapabuti ang katumpakan ng pag-scan at malutas ang problema:

  • Ang unang opsyon, alam naming sigurado na ang library ay ginagamit sa isang partikular na proyekto at maaaring ilapat ang panuntunan sa antas ng koponan. Ngunit kung magpasya ang team na kumuha ng ibang diskarte o gumamit ng ilang library kung saan nagsasapawan ang mga pangalan ng function, makakakuha tayo ng hindi masyadong magandang larawan ng maraming maling positibo

  • Ang pangalawang opsyon ay ang paghahanap ng mga file kung saan malinaw na na-import ang library. Sa diskarteng ito, makatitiyak tayo na eksaktong ginagamit ang library na kailangan natin sa file na ito.

  • At ang pangatlong opsyon ay ang paggamit ng dalawang paraan sa itaas nang magkasama.

Bilang halimbawa, tingnan natin ang isang aklatan na kilala sa makitid na bilog makinis para sa Scala programming language, ibig sabihin, ang functionality Pagdugtong ng mga Literal na Halaga. Sa pangkalahatan, upang maipasa ang mga parameter sa isang SQL query, dapat mong gamitin ang operator $, na nagpapalit ng data sa isang paunang nabuong query sa SQL. Iyon ay, sa katunayan, ito ay isang direktang analogue ng Inihanda na Pahayag sa Java. Ngunit, kung kailangan mong dynamic na bumuo ng isang SQL query, halimbawa, kung kailangan mong ipasa ang mga pangalan ng talahanayan, maaari mong gamitin ang operator #$, na direktang papalitan ang data sa query (halos tulad ng string concatenation).

Halimbawang code:

// В общем случае - значения, контролируемые пользователем
val table = "coffees"
sql"select * from #$table where name = $name".as[Coffee].headOption

Hindi pa alam ni Checkmarx kung paano matutukoy ang paggamit ng Splicing Literal Values ​​at paglaktaw sa mga operator #$, kaya subukan nating ituro ito upang matukoy ang mga potensyal na SQL injection at i-highlight ang mga tamang lugar sa code:

// Находим все импорты
CxList imports = All.FindByType(typeof(Import));

// Ищем по имени, есть ли в импортах slick
CxList slick = imports.FindByShortName("slick");

// Некоторый флаг, определяющий, что импорт библиотеки в коде присутствует
// Для более точного определения - можно применить подход с именем файла
bool not_empty_list = false;
foreach (CxList r in slick)
{
    // Если встретили импорт, считаем, что slick используется
	not_empty_list = true;
}

if (not_empty_list) {
    // Ищем вызовы, в которые передается SQL-строка
	CxList sql = All.FindByShortName("sql");
	sql.Add(All.FindByShortName("sqlu"));
	
	// Определяем данные, которые попадают в эти вызовы
	CxList data_sql = All.DataInfluencingOn(sql);
	
	// Так как синтакис не поддерживается, можно применить подход с регулярными выражениями
	// RegExp стоит использовать крайне осторожно и не применять его на большом количестве данных, так как это может сильно повлиять на производительность
	CxList find_possible_inj = data_sql.FindByRegex(@"#$", true, true, true);

    // Избавляемся от лишних срабатываний, если они есть и выводим в результат
	result = find_possible_inj.FindByType(typeof(BinaryExpr));
}

Problema: Maghanap ng mga ginamit na vulnerable na function sa Open-Source library

solusyon: Maraming kumpanya ang gumagamit ng Open-Source monitoring tool (OSA practice) para makita ang paggamit ng mga vulnerable na bersyon ng mga library sa mga binuong application. Minsan hindi posibleng i-update ang naturang library sa isang secure na bersyon. Sa ilang mga kaso, may mga limitasyon sa pagganap, sa iba ay walang ligtas na bersyon. Sa kasong ito, ang kumbinasyon ng mga kasanayan sa SAST at OSA ay makakatulong upang matukoy na ang mga function na humahantong sa pagsasamantala sa kahinaan ay hindi ginagamit sa code.

Ngunit kung minsan, lalo na kapag isinasaalang-alang ang JavaScript, maaaring hindi ito isang ganap na walang kabuluhang gawain. Nasa ibaba ang isang solusyon, marahil ay hindi perpekto, ngunit gayunpaman gumagana, gamit ang halimbawa ng mga kahinaan sa bahagi lodash sa mga pamamaraan template и *set.

Mga halimbawa ng pagsubok na posibleng masugatan na code sa isang JS file:

/**
 * Template example
 */

'use strict';
var _ = require("./node_modules/lodash.js");


// Use the "interpolate" delimiter to create a compiled template.
var compiled = _.template('hello <%= js %>!');
console.log(compiled({ 'js': 'lodash' }));
// => 'hello lodash!'

// Use the internal `print` function in "evaluate" delimiters.

var compiled = _.template('<% print("hello " + js); %>!');
console.log(compiled({ 'js': 'lodash' }));
// => 'hello lodash!'

At kapag direktang kumokonekta sa html:

<!DOCTYPE html>
<html>
<head>
    <title>Lodash Tutorial</title>
    <script src="./node_modules/lodash.js"></script>
    <script type="text/javascript">
  // Lodash chunking array
        nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];

        let c1 = _.template('<% print("hello " + js); %>!');
        console.log(c1);

        let c2 = _.template('<% print("hello " + js); %>!');
        console.log(c2);
    </script>
</head>
<body></body>
</html>

Hinahanap namin ang lahat ng aming mahihinang pamamaraan, na nakalista sa mga kahinaan:

// Ищем все строки: в которых встречается строка lodash (предполагаем, что это объявление импорта библиотеки
CxList lodash_strings = Find_String_Literal().FindByShortName("*lodash*");

// Ищем все данные: которые взаимодействуют с этими строками
CxList data_on_lodash = All.InfluencedBy(lodash_strings);


// Задаем список уязвимых методов
List<string> vulnerable_methods = new List<string> {"template", "*set"};

// Ищем все наши уязвимые методы, которые перечисленны в уязвимостях и отфильтровываем их только там, где они вызывались
CxList vulnerableMethods = All.FindByShortNames(vulnerable_methods).FindByType(typeof(MethodInvokeExpr));

//Находим все данные: которые взаимодействуют с данными методами
CxList vulnFlow = All.InfluencedBy(vulnerableMethods);

// Если есть пересечение по этим данным - кладем в результат
result = vulnFlow * data_on_lodash;

// Формируем список путей по которым мы уже прошли, чтобы фильтровать в дальнейшем дубли
List<string> lodash_result_path = new List<string> {};

foreach(CxList lodash_result in result)
{
    // Очередной раз получаем пути к файлам
	string file_name = lodash_result.GetFirstGraph().LinePragma.FileName;
	lodash_result_path.Add(file_name);
}

// Дальше идет часть относящаяся к html файлам, так как в них мы не можем проследить откуда именно идет вызов
// Формируем массив путей файлов, чтобы быть уверенными, что срабатывания уязвимых методов были именно в тех файлах, в которых объявлен lodash
List<string> lodash_path = new List<string> {};
foreach(CxList string_lodash in lodash_strings)
{
	string file_name = string_lodash.GetFirstGraph().LinePragma.FileName;
	lodash_path.Add(file_name);
}

// Перебираем все уязвимые методы и убеждаемся, что они вызваны в тех же файлах, что и объявление/включение lodash
foreach(CxList method in vulnerableMethods)
{
	string file_name_method = method.GetFirstGraph().LinePragma.FileName;
	if (lodash_path.Contains(file_name_method) == true && lodash_result_path.Contains(file_name_method) == false){
		result.Add(method);
	}
}

// Убираем все UknownReferences и оставляем самый "длинный" из путей, если такие встречаются
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceSmallFlow) - result.FindByType(typeof(UnknownReference));

Problema: Naghahanap ng mga certificate na naka-embed sa application

solusyon: Karaniwan para sa mga application, lalo na sa mga mobile, na gumamit ng mga certificate o key upang ma-access ang iba't ibang mga server o i-verify ang SSL-Pinning. Mula sa isang pananaw sa seguridad, ang pag-iimbak ng mga naturang bagay sa code ay hindi ang pinakamahusay na kasanayan. Subukan nating magsulat ng isang panuntunan na maghahanap ng mga katulad na file sa repositoryo:

// Найдем все сертификаты по маске файла
CxList find_certs = All.FindByShortNames(new List<string> {"*.der", "*.cer", "*.pem", "*.key"}, false);

// Проверим, где в приложении они используются
CxList data_used_certs = All.DataInfluencedBy(find_certs);

// И для мобильных приложений - можем поискать методы, где вызывается чтение сертификатов
// Для других платформ и приложений могут быть различные методы
CxList methods = All.FindByMemberAccess("*.getAssets");

// Пересечение множеств даст нам результат по использованию локальных сертификатов в приложении
result = methods * data_used_certs;

Problema: Paghahanap ng mga nakompromisong token sa application

solusyon: Kadalasang kinakailangan na bawiin ang mga nakompromisong token o iba pang mahalagang impormasyon na nasa code. Siyempre, ang pag-iimbak ng mga ito sa loob ng source code ay hindi magandang ideya, ngunit iba-iba ang mga sitwasyon. Salamat sa mga query sa CxQL, ang paghahanap ng mga bagay na tulad nito ay medyo madali:

// Получаем все строки, которые содержатся в коде
CxList strings = base.Find_Strings();

// Ищем среди всех строк нужное нам значение. В примере токен в виде строки "qwerty12345"
result = strings.FindByShortName("qwerty12345");

Konklusyon

Umaasa ako na ang artikulong ito ay magiging kapaki-pakinabang sa mga nagsisimula ng kanilang kakilala sa tool na Checkmarx. Marahil ang mga matagal nang nagsusulat ng kanilang sariling mga patakaran ay makakahanap din ng kapaki-pakinabang sa gabay na ito.

Sa kasamaang palad, kasalukuyang may kakulangan ng mapagkukunan kung saan maaaring makapulot ng mga bagong ideya sa panahon ng pagbuo ng mga panuntunan para sa Checkmarx. Kaya naman tayo gumawa imbakan sa Github, kung saan ipo-post namin ang aming trabaho upang ang lahat ng gumagamit ng CxQL ay makahanap ng isang bagay na kapaki-pakinabang dito, at magkaroon din ng pagkakataong ibahagi ang kanilang gawain sa komunidad. Ang repository ay nasa proseso ng pagpuno at pagbubuo ng nilalaman, kaya malugod na tinatanggap ang mga kontribyutor!

Salamat sa iyo!

Pinagmulan: www.habr.com

Magdagdag ng komento