Hoe regels te skriuwen foar Checkmarx sûnder gek te wurden

Hoi Habr!

Yn ús wurk docht ús bedriuw heul faak mei ferskate ark foar statyske koade-analyse (SAST). Ut it fak wurkje se allegear gemiddeld. Fansels hinget it allegear ôf fan it projekt en de technologyen dy't dêryn brûkt wurde, lykas hoe goed dizze technologyen wurde behannele troch de regels fan analyze. Yn myn miening is ien fan 'e wichtichste kritearia by it kiezen fan in SAST-ark de mooglikheid om it oan te passen oan' e spesifiken fan jo applikaasjes, nammentlik, skriuw en wizigje analyseregels of, sa't se faker wurde neamd, Custom Queries.

Hoe regels te skriuwen foar Checkmarx sûnder gek te wurden

Wy brûke meastentiids Checkmarx - in heul ynteressante en krêftige koadeanalysator. Yn dit artikel sil ik prate oer myn ûnderfining fan it skriuwen fan analyze regels foar it.

Ynhâldsopjefte

Ynlieding

Om te begjinnen wol ik ien fan 'e pear artikels yn it Russysk oanbefelje oer de funksjes fan it skriuwen fan fragen foar Checkmarx. It waard ein 2019 publisearre op Habré ûnder de titel: "Hallo, Checkmarx!" Hoe kinne jo in Checkmarx SAST-query skriuwe en koele kwetsberens fine.

It ûndersiket yn detail hoe't jo de earste fragen skriuwe yn CxQL (Checkmarx Query Language) foar guon testapplikaasje en toant de basisprinsipes fan hoe't analyseregels wurkje.

Ik sil net werhelje wat dêryn beskreaun is, hoewol't guon krusingen noch oanwêzich wêze sille. Yn myn artikel sil ik besykje in soarte fan "kolleksje fan resepten", in list mei oplossingen foar spesifike problemen dy't ik tsjinkaam tidens myn wurk mei Checkmarx, te kompilearjen. Ik moast rack myn harsens oer in protte fan dizze problemen. Soms wie der net genôch ynformaasje yn 'e dokumintaasje, en soms wie it sels lestich om te begripen hoe't jo dwaan wat nedich wie. Ik hoopje dat myn ûnderfining en sliepleaze nachten net om 'e nocht sille wêze, en dizze "kolleksje fan resepten foar oanpaste fragen" sil jo in pear oeren as in pear senuwsellen besparje. Dus, lit ús begjinne!

Algemiene ynformaasje oer de regels

Lit ús earst sjen nei in pear basisbegripen en it proses fan wurkjen mei de regels, foar in better begryp fan wat sil barre neist. En ek om't de dokumintaasje hjir neat oer seit of tige ferspraat is yn 'e struktuer, wat net heul handich is.

  1. De regels wurde tapast by it scannen ôfhinklik fan de foarynstelling dy't selekteare is by it begjin (in set aktive regels). Jo kinne in ûnbeheind oantal presets oanmeitsje, en krekt hoe't jo se strukturearje hinget ôf fan 'e spesifikaasjes fan jo proses. Jo kinne se groepearje op taal of presets selektearje foar elk projekt. It oantal aktive regels beynfloedet de snelheid en krektens fan it skennen.

    Hoe regels te skriuwen foar Checkmarx sûnder gek te wurdenIt ynstellen fan Preset yn 'e Checkmarx-ynterface

  2. De regels wurde bewurke yn in spesjaal ark neamd CxAuditor. Dit is in buroblêdapplikaasje dy't ferbynt mei in server mei Checkmarx. Dit ark hat twa wurkwizen: regels bewurkje en de resultaten fan in al útfierde scan analysearje.

    Hoe regels te skriuwen foar Checkmarx sûnder gek te wurdenCxAudit ynterface

  3. Regels yn Checkmarx binne ferdield troch taal, dat is, elke taal hat syn eigen set fan fragen. Der binne ek inkele algemiene regels dy't jilde nettsjinsteande de taal, dit binne de saneamde basisfragen. Foar it meastepart omfetsje basisfragen it sykjen nei ynformaasje dy't oare regels brûke.

    Hoe regels te skriuwen foar Checkmarx sûnder gek te wurdenRegels diele troch taal

  4. Regels binne "Utfierber" en "Net-útfierber" (Utfierd en net útfierd). Net hielendal de juste namme, neffens my, mar dat is wat it is. De ûnderste rigel is dat it resultaat fan it útfieren fan "Utfierbere" regels sille wurde werjûn yn 'e scanresultaten yn' e UI, en "Net-útfierbere" regels binne allinich nedich om har resultaten te brûken yn oare oanfragen (yn wêzen, gewoan in funksje).

    Hoe regels te skriuwen foar Checkmarx sûnder gek te wurdenIt bepalen fan it regeltype by it meitsjen

  5. Jo kinne nije regels oanmeitsje of besteande oanfolje/herskriuwe. Om in regel te herskriuwen, moatte jo it fine yn 'e beam, rjochts-klikke en selektearje "Override" út it dellûk menu. It is wichtich om hjir te betinken dat de nije regels net yn earste ynstânsje opnommen binne yn 'e presets en net aktyf binne. Om se te brûken, moatte jo se aktivearje yn it menu "Preset Manager" yn it ynstrumint. Herskreaune regels behâlde har ynstellings, dat wol sizze, as de regel aktyf wie, sil it sa bliuwe en wurde fuortendaliks tapast.

    Hoe regels te skriuwen foar Checkmarx sûnder gek te wurdenFoarbyld fan in nije regel yn de Preset Manager ynterface

  6. Tidens de útfiering wurdt in "beam" fan oanfragen boud, wat hinget fan wat. De regels dy't ynformaasje sammelje wurde earst útfierd, en dejingen dy't it brûke twadde. It útfieringsresultaat wurdt yn 't cache bewarre, dus as it mooglik is om de resultaten fan in besteande regel te brûken, dan is it better om dat te dwaan, dit sil de scantiid ferminderje.

  7. Regels kinne tapast wurde op ferskate nivo's:

  • Foar it heule systeem - sil brûkt wurde foar elke skennen fan elk projekt

  • Op teamnivo (Team) - sil allinich brûkt wurde om projekten yn it selekteare team te scannen.

  • Op projektnivo - Sil tapast wurde yn in spesifyk projekt

    Hoe regels te skriuwen foar Checkmarx sûnder gek te wurdenIt fêststellen fan it nivo wêrop de regel tapast wurde sil

"Wurdboek" foar begjinners

En ik sil begjinne mei in pear dingen dy't feroarsake my fragen, en ik sil ek sjen litte in oantal techniken dy't sil gâns ferienfâldigjen it libben.

Operaasjes mei listen

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

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

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

Alle fûn items

Binnen de skande taal kinne jo in list krije fan absolút alle eleminten dy't Checkmarx hat identifisearre (strings, funksjes, klassen, metoaden, ensfh.). Dit is wat romte fan objekten dy't tagonklik wurde kinne troch All. Dat is, om te sykjen nei in objekt mei in spesifike namme searchMe, kinne jo bygelyks sykje op namme yn alle fûn objekten:

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

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

Mar as jo sykje moatte yn in oare taal dy't om ien of oare reden net yn 'e scan opnommen is (bygelyks groovy yn in Android-projekt), kinne jo ús objektromte útwreidzje troch in fariabele:

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

Funksjes foar Flow analyze

Dizze funksjes wurde brûkt yn in protte regels en hjir is in lyts cheat sheet fan wat se betsjutte:

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

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

Bestânsnamme/paad krije

D'r binne ferskate attributen dy't kinne wurde krigen fan 'e resultaten fan in query (de namme fan it bestân wêryn't de yngong fûn is, tekenrige, ensfh.), Mar de dokumintaasje seit net hoe't jo se krije en brûke. Dat, om dit te dwaan, moatte jo tagong krije ta it LinePragma-eigendom en de objekten dy't wy nedich binne sille deryn lizze:

// Для примера найдем все методы
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);

It is it wurdich te hâlden dat FileName befettet eins it paad nei it bestân, om't wy de metoade brûkten GetFirstGraph.

Eksekúsje resultaat

D'r is in spesjale fariabele binnen CxQL result, dy't it resultaat jout fan it útfieren fan jo skriftlike regel. It wurdt fuortendaliks inisjalisearre en jo kinne tuskenresultaten deryn skriuwe, se feroarje en ferfine as jo wurkje. Mar, as d'r gjin opdracht is oan dizze fariabele of funksje binnen de regel return- it útfieringsresultaat sil altyd nul wêze.

De folgjende query sil ús neat werombringe as gefolch fan útfiering en sil altyd leech wêze:

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

Mar, nei't wy it útfieringsresultaat hawwe tawiisd oan it resultaat fan 'e magyske fariabele, sille wy sjen wat dizze oprop ús weromkomt:

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

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

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

Mei help fan de resultaten fan oare regels

Regels yn Checkmarx kinne analoog wurde neamd oan funksjes yn in gewoane programmeartaal. By it skriuwen fan in regel kinne jo de resultaten fan oare fragen brûke. Bygelyks, it is net nedich om elke kear nei alle metoadeoproppen yn 'e koade te sykjen, skilje gewoan de winske regel:

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

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

Dizze oanpak lit jo de koade koarter meitsje en de útfieringstiid fan regel signifikant ferminderje.

Oplossing fan problemen

Logging

By it wurkjen mei it ark is it soms net mooglik om de winske fraach daliks te skriuwen en jo moatte eksperimintearje, ferskate opsjes besykje. Foar sa'n gefal biedt it ark logging, dat wurdt neamd as folget:

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

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

Mar it is de muoite wurdich ûnthâlden dat dizze metoade allinnich akseptearret as ynput string, dus it sil net mooglik wêze om in folsleine list fan fûn eleminten te sjen as gefolch fan 'e earste operaasje. De twadde opsje, dy't brûkt wurdt foar debuggen, is om fan tiid ta tiid oan in magyske fariabele te jaan result it resultaat fan 'e fraach en sjoch wat der bart. Dizze oanpak is net heul handich; jo moatte der wis fan wêze dat d'r gjin oerskriuwings of operaasjes binne mei dizze yn 'e koade nei result of kommintaar gewoan de koade hjirûnder. Of jo kinne, lykas ik, ferjitte om ferskate sokke oproppen te ferwiderjen fan in ready-made regel en jo ôffreegje wêrom't neat wurket.

In handiger manier is om de metoade te neamen return mei de fereaske parameter. Yn dit gefal sil de útfiering fan 'e regel einigje en sille wy sjen kinne wat der bard is as gefolch fan wat wy skreaun hawwe:

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

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

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

Oanmelde probleem

Der binne situaasjes as jo gjin tagong krije ta it CxAudit-ark (dat wurdt brûkt om regels te skriuwen). D'r kinne in protte redenen foar wêze, ynklusyf crashes, hommels Windows-fernijings, BSOD en oare ûnfoarsjoene situaasjes dy't bûten ús kontrôle binne. Yn dit gefal is d'r soms in ûnfoltôge sesje yn 'e databank, dy't foarkomt dat jo opnij ynlogge. Om it te reparearjen, moatte jo ferskate fragen útfiere:

Foar Checkmarx foar 8.6:

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

Foar Checkmarx nei 8.6:

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

Skriuwregels

No komme wy ta it meast nijsgjirrige diel. As jo ​​​​begjinne mei it skriuwen fan regels yn CxQL, wat jo faaks misse is net sa folle dokumintaasje as guon libbene foarbylden fan it oplossen fan bepaalde problemen en it beskriuwen fan it proses fan hoe't queries yn 't algemien wurkje.

Ik sil besykje it libben in bytsje makliker te meitsjen foar dyjingen dy't begjinne te dûken yn 'e query-taal en jouwe ferskate foarbylden fan it brûken fan Custom Queries om bepaalde problemen op te lossen. Guon fan harren binne frij algemien en kinne brûkt wurde yn jo bedriuw praktysk sûnder feroarings, oaren binne mear spesifyk, mar se kinne ek brûkt wurde troch it feroarjen fan de koade om te passe by de spesifiken fan jo applikaasjes.

Dat, hjir binne de problemen dy't wy it meast tsjinkamen:

In taak: D'r binne ferskate Flows yn 'e resultaten fan it útfieren fan' e regel en ien fan har is in nêst fan in oar, jo moatte ien fan har ferlitte.

oplossing: Yndied, soms toant Checkmarx ferskate gegevensstreamen dy't oerlappe kinne en in ferkoarte ferzje fan oaren wêze. Der is in spesjale metoade foar sokke gefallen Ferminderje Flow. Ofhinklik fan 'e parameter sil it de koartste of langste Flow selektearje:

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

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

In taak: Wreidzje de list út mei gefoelige gegevens wêrop it ark reagearret

oplossing: Checkmarx hat basisregels, wêrfan de resultaten wurde brûkt troch in protte oare fragen. Troch guon fan dizze regels oan te foljen mei gegevens spesifyk foar jo applikaasje, kinne jo jo scanresultaten fuortendaliks ferbetterje. Hjirûnder is in foarbyldregel om jo te begjinnen:

General_privacy_violation_list

Litte wy ferskate fariabelen tafoegje dy't wurde brûkt yn ús applikaasje om gefoelige ynformaasje op te slaan:

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

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

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

In taak: Wreidzje de list fan fariabelen út mei wachtwurden

oplossing: Ik soe riede om fuortendaliks omtinken te jaan oan de basisregel foar it definiearjen fan wachtwurden yn koade en it tafoegjen fan in list mei fariabele nammen dy't gewoanlik brûkt wurde yn jo bedriuw.

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);
	}
}

In taak: Foegje brûkte kaders ta dy't net wurde stipe troch Checkmarx

oplossing: Alle fragen yn Checkmarx binne ferdield troch taal, dus jo moatte regels tafoegje foar elke taal. Hjirûnder binne inkele foarbylden fan sokke regels.

As biblioteken wurde brûkt dy't de standertfunksjonaliteit oanfolje of ferfange, kinne se maklik tafoege wurde oan 'e basisregel. Dan leart elkenien dy't it brûkt fuortendaliks oer de nije ynliedingen. As foarbyld binne biblioteken foar ynloggen yn Android Timber en Loggi. Yn it basispakket binne d'r gjin regels foar it identifisearjen fan net-systeemoproppen, dus as in wachtwurd of sesje-identifikaasje yn it log komt, sille wy it net witte. Litte wy besykje definysjes fan sokke metoaden ta te foegjen oan 'e Checkmarx-regels.

Foarbyld fan testkoade dat de Timber-bibleteek brûkt foar logging:

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");
    }
}

En hjir is in foarbyld fan in fersyk foar Checkmarx, wêrtroch jo in definysje kinne tafoegje fan Timber-metoaden oproppe as útgongspunt foar gegevens fan 'e applikaasje:

FindAndroidOutputs

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

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

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

En jo kinne ek tafoegje oan 'e oanbuorjende regel, mar dizze is direkt relatearre oan it ynloggen yn Android:

FindAndroidLog_Outputs

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

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

Ek as Android-applikaasjes brûke Wurkbehearder foar asynchrone wurken is it in goed idee om Checkmarx hjiroer ek te ynformearjen troch in metoade ta te foegjen foar it heljen fan gegevens út de taak getInputData:

FindAndroidRead

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

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

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

In taak: Sykje nei gefoelige gegevens yn plist foar iOS-projekten

oplossing: iOS brûkt faak spesjale bestannen mei de .plist-útwreiding om ferskate fariabelen en wearden op te slaan. It opslaan fan wachtwurden, tokens, kaaien en oare gefoelige gegevens yn dizze bestannen is net oan te rieden, om't se sûnder problemen út it apparaat kinne wurde ekstrahearre.

Plist-bestannen hawwe funksjes dy't net dúdlik binne foar it bleate each, mar binne wichtich foar Checkmarx. Litte wy in regel skriuwe dy't sil sykje nei de gegevens dy't wy nedich binne en ús fertelle as wachtwurden of tokens earne neamd wurde.

In foarbyld fan sa'n bestân, dat in token befettet foar kommunikaasje mei de backend-tsjinst:

<?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>

En in regel foar Checkmarx, dy't ferskate nuânses hat dy't rekken holden wurde moatte by it skriuwen:

// Используем результат выполнения правила по поиску файлов 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);

In taak: It finen fan ynformaasje yn XML

oplossing: Checkmarx hat heul handige funksjes foar wurkjen mei XML en sykjen nei wearden, tags, attributen en mear. Mar, spitigernôch, wie d'r in flater yn 'e dokumintaasje wêrtroch gjin inkeld foarbyld wurket. Nettsjinsteande it feit dat dit defekt is eliminearre yn 'e lêste ferzje fan' e dokumintaasje, wês foarsichtich as jo eardere ferzjes fan dokuminten brûke.

Hjir is in ferkeard foarbyld út 'e dokumintaasje:

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

As gefolch fan de útfiering besykjen, wy krije in flater dat All d'r is gjin sa'n metoade ... En dit is wier, om't d'r in spesjale, aparte objektromte is foar it brûken fan funksjes foar wurkjen mei XML - cxXPath. Dit is hoe't de juste query derút sjocht om in ynstelling yn Android te finen dy't it gebrûk fan HTTP-ferkear mooglik makket:

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

Litte wy it yn in bytsje mear detailje besjen, om't de syntaksis foar alle funksjes ferlykber is, nei't jo ien hawwe útfûn, dan moatte jo gewoan dejinge selektearje dy't jo nedich binne. Dus, sequentially neffens de parameters:

  • "*.xml"- masker fan te sykjen bestannen

  • 8 - id fan de taal dêr't de regel foar tapast wurdt

  • "cleartextTrafficPermitted"- attribút namme yn xml

  • "true" - de wearde fan dit attribút

  • false - gebrûk fan reguliere ekspresje by it sykjen

  • true - betsjut dat it sykjen sil wurde útfierd mei it negearjen fan gefal, dat is, net-gefoelige

As foarbyld hawwe wy in regel brûkt dy't ferkeard identifisearret, út in feiligenspunt, netwurkferbiningynstellingen yn Android dy't kommunikaasje mei de tsjinner fia it HTTP-protokol mooglik meitsje. Foarbyld fan in ynstelling mei in attribút cleartextTrafficPermitted mei de betsjutting 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>

In taak: Beheine resultaten troch triemnamme/paad

oplossing: Yn ien fan 'e grutte projekten yn ferbân mei de ûntwikkeling fan in mobile applikaasje foar Android, wy tsjinkaam falske positiven fan de regel dy't bepaalt de obfuscation ynstelling. It feit is dat de regel út it fak siket yn it bestân build.gradle in ynstelling ferantwurdlik foar it tapassen fan obfuscation regels foar de release ferzje fan de applikaasje.

Mar yn grutte projekten binne soms bernebestannen build.gradle, dy't ferwize nei de biblioteken dy't yn it projekt opnommen binne. De eigenaardichheid is dat sels as dizze bestannen de needsaak foar obfuscaasje net oanjaan, de ynstellings fan 'e parent-assembly-bestân sille tapast wurde by kompilaasje.

Sa is de taak om triggers yn bernebestannen dy't by biblioteken hearre ôf te snijen. Se kinne wurde identifisearre troch de oanwêzigens fan 'e line apply 'com.android.library'.

Foarbyld koade út triem build.gradle, dy't de needsaak foar obfuscaasje bepaalt:

apply plugin: 'com.android.application'

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

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

dependencies {
  ...
}

Foarbyld triem build.gradle foar in biblioteek opnommen yn it projekt dy't dizze ynstelling net hat:

apply plugin: 'android-library'

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

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

En de regel foar 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);
		}
	}
}

Dizze oanpak kin frij universele en nuttich wêze net allinich foar Android-applikaasjes, mar ek foar oare gefallen as jo moatte bepale oft in resultaat ta in spesifyk bestân heart.

In taak: Foegje stipe ta foar in bibleteek fan tredden as de syntaksis net folslein stipe wurdt

oplossing: It oantal ferskate kaders dat wurdt brûkt yn it proses fan it skriuwen fan koade is gewoan út 'e charts. Fansels wit Checkmarx net altyd fan har bestean, en ús taak is it te learen om te begripen dat bepaalde metoaden spesifyk ta dit ramt hearre. Soms wurdt dit komplisearre troch it feit dat kaders funksjenammen brûke dy't heul gewoan binne en it ûnmooglik is om de relaasje fan in bepaalde oprop nei in spesifike bibleteek ûndûbelsinnich te bepalen.

De muoite is dat de syntaksis fan sokke biblioteken net altyd goed erkend wurdt en jo moatte eksperimintearje om foar te kommen dat jo in grut oantal falske positiven krije. D'r binne ferskate opsjes om de krektens fan skennen te ferbetterjen en it probleem op te lossen:

  • De earste opsje, wy witte foar wis dat de bibleteek wurdt brûkt yn in spesifyk projekt en kin tapasse de regel op it team nivo. Mar as it team beslút in oare oanpak te nimmen of ferskate biblioteken brûkt wêryn funksjenammen oerlappe, kinne wy ​​in net heul noflik byld krije fan in protte falske positives

  • De twadde opsje is om te sykjen nei bestannen wêryn de bibleteek dúdlik ymportearre is. Mei dizze oanpak kinne wy ​​der wis fan wêze dat de bibleteek dy't wy nedich binne krekt brûkt wurdt yn dit bestân.

  • En de tredde opsje is om de twa boppesteande oanpakken tegearre te brûken.

As foarbyld, litte wy sjen nei in bibleteek bekend yn smelle sirkels glêd foar de programmeartaal Scala, nammentlik de funksjonaliteit Splicing letterlike wearden. Yn it algemien, om parameters troch te jaan oan in SQL-query, moatte jo de operator brûke $, dy't gegevens ferfangt yn in foarfoarme SQL-query. Dat is, yn feite, it is in direkte analoog fan Prepared Statement yn Java. Mar, as jo in SQL-query dynamysk moatte konstruearje, bygelyks, as jo tabelnammen moatte trochjaan, kinne jo de operator brûke #$, dy't de gegevens direkt yn 'e query sil ferfange (hast as string-konkatenaasje).

Foarbyld fan koade:

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

Checkmarx wit noch net hoe't it gebrûk fan Splicing Literal Values ​​​​detektearje en operators oerslaan #$, dus litte wy besykje it te learen om potensjele SQL-ynjeksjes te identifisearjen en de juste plakken yn 'e koade te markearjen:

// Находим все импорты
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));
}

In taak: Sykje nei brûkte kwetsbere funksjes yn Open-Source bibleteken

oplossing: In protte bedriuwen brûke iepen boarne-monitoring-ark (OSA-praktyk) om it gebrûk fan kwetsbere ferzjes fan bibleteken yn ûntwikkele applikaasjes te detektearjen. Soms is it net mooglik om sa'n bibleteek te fernijen nei in feilige ferzje. Yn guon gefallen binne d'r funksjonele beheiningen, yn oaren is d'r hielendal gjin feilige ferzje. Yn dit gefal sil in kombinaasje fan SAST- en OSA-praktiken helpe om te bepalen dat de funksjes dy't liede ta eksploitaasje fan 'e kwetsberens net brûkt wurde yn' e koade.

Mar soms, foaral as jo JavaScript beskôgje, kin dit net in folslein triviale taak wêze. Hjirûnder is in oplossing, miskien net ideaal, mar dochs wurket, mei it foarbyld fan kwetsberens yn 'e komponint lodash yn metoaden template и *set.

Foarbylden fan test potensjeel kwetsbere koade yn in JS-bestân:

/**
 * 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!'

En as jo direkt ferbine yn 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>

Wy binne op syk nei al ús kwetsbere metoaden, dy't wurde neamd yn kwetsberens:

// Ищем все строки: в которых встречается строка 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));

In taak: Sykje nei sertifikaten ynbêde yn 'e applikaasje

oplossing: It is net ûngewoan dat applikaasjes, benammen mobile, sertifikaten of kaaien brûke om tagong te krijen ta ferskate servers of SSL-pinning te ferifiearjen. Fanút in feiligensperspektyf is it opslaan fan sokke dingen yn koade net de bêste praktyk. Litte wy besykje in regel te skriuwen dy't sil sykje nei ferlykbere bestannen yn it repository:

// Найдем все сертификаты по маске файла
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;

In taak: It finen fan kompromittearre tokens yn 'e applikaasje

oplossing: It is faak nedich om kompromittearre tokens of oare wichtige ynformaasje dy't oanwêzich is yn 'e koade werom te lûken. Fansels is it opslaan fan se yn 'e boarnekoade gjin goed idee, mar situaasjes ferskille. Mei tank oan CxQL-fragen is it finen fan dingen lykas dit frij maklik:

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

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

konklúzje

Ik hoopje dat dit artikel nuttich sil wêze foar dyjingen dy't har kunde begjinne mei it Checkmarx-ark. Miskien sille dejingen dy't har eigen regels al in lange tiid skriuwe, ek wat nuttich fine yn dizze gids.

Spitigernôch is der op it stuit in tekoart oan in boarne dêr't nije ideeën by de ûntwikkeling fan regels foar Checkmarx ophelle wurde kinne. Dêrom hawwe wy makke repository op Github, wêr't wy ús wurk pleatse sille, sadat elkenien dy't CxQL brûkt, der wat nuttichs yn fine kin, en ek de kâns hawwe om har wurk mei de mienskip te dielen. De repository is yn it proses fan it ynfoljen en strukturearjen fan ynhâld, dus bydragen binne wolkom!

Спасибо за внимание!

Boarne: www.habr.com

Add a comment