Hallo, Habr!
In ons werk heeft ons bedrijf vaak te maken met verschillende tools voor statische code-analyse (SAST). Uit de doos werken ze allemaal gemiddeld. Natuurlijk hangt alles af van het project en de gebruikte technologieën, evenals hoe goed deze technologieën worden gedekt door de analyzeregel. Naar mijn mening is een van de belangrijkste criteria bij het kiezen van een SAST-tool de mogelijkheid om deze aan te passen aan de specifieke kenmerken van onze toepassingen, namelijk het schrijven en wijzigen van analyzeregel of, zoals ze vaker worden genoemd, Custom Queries.

We gebruiken meestal Checkmarx - een zeer interessante en krachtige code-analyse-tool. In dit artikel zal ik mijn ervaring delen met het schrijven van analyzeregel voor deze tool.
Inhoudsopgave
Inleiding
Als eerste wil ik een van de weinige artikelen aanbevelen in het Russisch over de bijzonderheden van het schrijven van queries voor Checkmarx. Het werd gepubliceerd op Habr aan het einde van 2019 met de titel: .
Het bespreekt in detail hoe je de eerste queries schrijft in de CxQL (Checkmarx Query Language) voor een testtoepassing en laat de belangrijkste principes van de analyzeregelwerking zien.
Ik zal niet herhalen wat daarin staat, hoewel er toch enkele overlappen zullen zijn. In mijn artikel probeer ik een soort ‘receptenbundel’ samen te stellen, een lijst van oplossingen voor specifieke problemen waarmee ik ben geconfronteerd tijdens mijn werk met Checkmarx. Voor veel van deze taken heb ik veel nagedacht. Soms ontbrak het aan gegevens in de documentatie, en soms was het gewoon moeilijk te begrijpen hoe te doen wat nodig was. Ik hoop dat mijn ervaring en slapeloze nachten niet tevergeefs zijn, en dat deze ‘bundel met Custom Queries-recepten’ je enkele uren of een paar zenuwcellen bespaart. Laten we beginnen!
Algemene informatie over regels
Laten we beginnen met een aantal basisbegrippen en het proces van werken met regels, zodat u een beter begrip krijgt van wat er verder zal gebeuren. En ook omdat dit niet wordt vermeld in de documentatie of sterk verspreid is over de structuur, wat niet echt handig is.
De regels worden toegepast tijdens het scannen, afhankelijk van de geselecteerde preset (set van actieve regels) bij de start. Er kunnen een onbeperkt aantal presets worden aangemaakt en hoe u deze structuurt, hangt af van de kenmerken van uw proces. U kunt ze bijvoorbeeld groeperen op taal of specifieke presets voor elk project maken. Het aantal actieve regels beïnvloedt de snelheid en nauwkeurigheid van het scannen.
Preset configuratie in de Checkmarx-interfaceRegels worden bewerkt met een speciale tool genaamd CxAuditor. Dit is een desktopapplicatie die verbinding maakt met de server van Checkmarx. Dit gereedschap heeft twee werkmodi: regels bewerken en de resultaten analyseren van een eerder uitgevoerde scan.
CxAudit-interfaceRegels in Checkmarx zijn verdeeld op basis van programmeertalen, wat betekent dat voor elke taal een specifieke set van aanvragen bestaat. Daarnaast zijn er enkele algemene regels die onafhankelijk van de taal worden toegepast, de zogenaamde basisregels. Over het algemeen bevatten basisregels het zoeken naar informatie die door andere regels wordt gebruikt.
Verdeling van regels op basis van taalRegels kunnen 'Executable' en 'Non-Executable' zijn (Uitvoerbaar en Niet-uitvoerbaar). Dit is naar mijn mening niet de meest nauwkeurige benaming, maar zo is het. Het komt erop neer dat het resultaat van het uitvoeren van 'Executable' regels zichtbaar is in de scanresultaten in de UI, terwijl 'Non-Executable' regels alleen nodig zijn om hun resultaten in andere aanvragen te gebruiken (feitelijk gezien is het gewoon een functie).
Bepaling van het type regel bij creatieU kunt nieuwe regels aanmaken of bestaande regels aanvullend herschrijven. Om een regel te herschrijven, moet u deze vinden in de boomstructuur, met de rechtermuisknop klikken en in het vervolgkeuzemenu de optie 'Override' selecteren. Het is belangrijk om te onthouden dat nieuwe regels oorspronkelijk niet zijn ingeschakeld in de presets en inactief zijn. Om ze te beginnen gebruiken, moet u ze activeren in het menu 'Preset Manager' in de tool. Herschreven regels behouden hun instellingen, wat betekent dat als een regel actief was, deze actief blijft en onmiddellijk wordt toegepast.
Voorbeeld van een nieuwe regel in de interface van Preset ManagerTijdens de uitvoering wordt een 'boom' van verzoeken opgebouwd, afhankelijk van wat van wat. Eerst worden de regels uitgevoerd die informatie verzamelen, gevolgd door diegenen die deze informatie gebruiken. Het resultaat van de uitvoering wordt gecachet, zodat als het mogelijk is om de resultaten van een bestaand regel te gebruiken, dit de voorkeur heeft. Dit zal de scan tijd verminderen.
Regels kunnen op verschillende niveaus worden toegepast:
Op systeemniveau — wordt gebruikt voor elke scan van elk project
Op teamniveau — wordt alleen toegepast voor scans van projecten binnen het geselecteerde team.
Op projectniveau — zal worden toegepast in een specifiek project
Bepalen van het niveau waarop de regel zal worden toegepast
Een 'woordenschat' voor beginners
En ik begin met een aantal zaken die mijn vragen opriepen, en ik zal ook een aantal technieken laten zien die het leven aanzienlijk zullen vereenvoudigen.
Bewerkingen met lijsten
- aftrekken van de één van de ander (list2 - list1)
* intersectie van lijsten (list1 * list2)
+ optellen van lijsten (list1 + list2)
& (logisch EN) - verbindt lijsten op basis van overeenstemming (list1 & list2), vergelijkbaar met intersectie (list1 * list2)
| (logisch OF) - verbindt lijsten op basis van brede zoekopdracht (list1 | list2)
Werkt niet met lijsten: ^ && || % / Alle gevonden elementen
In het gescande taalgebied kan een lijst van alle elementen die door Checkmarx zijn gedefinieerd (regels, functies, klassen, methoden, enz.) worden verkregen. Dit is een bepaalde ruimte van objecten waarop kan worden aangesproken via Alle. Dus, om een object met een specifieke naam aan te roepen searchMe, kan gezocht worden, bijvoorbeeld, naar de naam in alle gevonden objecten:
// Такой запрос выдаст все элементы
result = All;
// Такой запрос выдаст все элементы, в имени которых присутствует “searchMe“
result = All.FindByName("searchMe");Maar als er gezocht moet worden in een andere taal die om bepaalde redenen niet in de scan is opgenomen (zoals groovy in een project voor Android), kan onze objectruimte worden uitgebreid via een variabele:
result = AllMembers.All.FindByName("searchMe");Functies voor flow-analyse
Deze functies worden in veel regels gebruikt en hier is een kleine spiekbrief dat ze betekenen:
// Какие данные second влияют на first.
// Другими словами - ТО (second) что влияет на МЕНЯ (first).
result = first.DataInfluencedBy(second);
// Какие данные first влияют на second.
// Другими словами - Я (first) влияю на ТО (second).
result = first.DataInfluencingOn(second);Het verkrijgen van de naam/pad van een bestand
Er zijn verschillende attributen die kunnen worden verkregen uit de resultaten van de uitvoering van een verzoek (bestandsnaam waarin de invoering is gevonden, regel, enz.), maar hoe ze te verkrijgen en te gebruiken staat niet in de documentatie. Dus om dit te doen, moet men het eigenschap LinePragma aanspreken en daarbinnen bevinden zich de objecten die we nodig hebben:
// Для примера найдем все методы
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);Het is belangrijk om te onthouden dat Bestandsnaam bevat eigenlijk het pad naar het bestand, omdat we de methode hebben gebruikt GetFirstGraph.
Resultaat van uitvoering
Binnen CxQL is er een speciale variabele voorzien resultaat, die het resultaat van de uitvoering van uw geschreven regel retourneert. Deze is onmiddellijk geïnitialiseerd en je kunt er tussenresultaten in opslaan, waarbij je ze tijdens het werken kunt wijzigen en verfijnen. Maar als er binnen de regel geen toewijzing aan deze variabele of functie is, terug— zal het resultaat van de uitvoering altijd nul zijn.
De volgende query retourneert ons niets in het resultaat en zal altijd leeg zijn:
// Находим элементы foo
CxList libraries = All.FindByName("foo");Maar door het resultaat van de uitvoering aan de magische variabele result toe te wijzen — zullen we zien wat deze aanroep ons retourneert:
// Находим элементы foo
CxList libraries = All.FindByName("foo");
// Выводим, как результат выполнения правила
result = libraries
// Или еще короче
result = All.FindByName("foo");Het gebruik van de uitvoeringsresultaten van andere regels
Regels in Checkmarx kun je vergelijken met functies in een gewone programmeertaal. Bij het schrijven van een regel kun je best de resultaten van andere queries gebruiken. Als voorbeeld, er is geen noodzaak om telkens alle aanroepen van methoden in de code te zoeken, het is voldoende om de benodigde regel aan te roepen:
// Получаем результат выполнения другого правила
CxList methods = Find_Methods();
// Ищем внутри метод foo.
// Второй параметр false означает, что ищем без чувствительности к регистру
result = methods.FindByShortName("foo", false);Deze aanpak stelt je in staat om de code te verkorten en de uitvoeringstijd van de regel aanzienlijk te verminderen.
Probleemoplossing
Logging
Bij het werken met de tool lukt het soms niet om meteen de benodigde query te schrijven en moet je experimenteren door verschillende opties te proberen. Voor dergelijke gevallen is logging in de tool voorzien, wat als volgt wordt aangeroepen:
// Находим что-то
CxList toLog = All.FindByShortName("log");
// Формируем строку и отправляем в лог
cxLog.WriteDebugMessage (“number of DOM elements =” + All.Count);Maar het is belangrijk te onthouden dat deze methode alleen een string, accepteert, dus je kunt de volledige lijst van gevonden elementen in het resultaat van de eerste operatie niet verkrijgen. Een tweede optie, die vaak voor debugging wordt gebruikt — is van tijd tot tijd de magische variabele toe te wijzen resultaat het resultaat van de query en te kijken wat er uitkomt. Deze aanpak is niet erg handig, je moet er zeker van zijn dat er daarna in de code geen herdefiniëring of bewerkingen met deze zijn resultaat of gewoon de onderliggende code commentariëren. Of je kunt zoals ik, vergeten om enkele van dergelijke aanroepen uit de voltooide regel te verwijderen en je af te vragen waarom niets werkt.
Een handige manier is om de methode terug met de juiste parameter aan te roepen. In dat geval zal de uitvoering van de regel eindigen en kunnen we zien wat het resultaat is van wat we hebben geschreven:
// Находим что-то
CxList toLog = All.FindByShortName("log");
// Выводим результат выполнения
return toLog
//Все, что написано дальше не будет выполнено
result = All.DataInfluencedBy(toLog)Inlogprobleem
Er zijn situaties waarin het niet lukt om toegang te krijgen tot het CxAudit-hulpmiddel (dat wordt gebruikt voor het schrijven van regels). De redenen hiervoor kunnen uiteenlopend zijn, van een onverwachte afsluiting, een plotselinge Windows-update, BSOD en andere onvoorziene situaties die buiten onze controle liggen. In dat geval kan er soms een onafgebroken sessie in de database blijven hangen, wat een nieuwe toegang verhindert. Om dit op te lossen, moeten er een aantal verzoeken worden uitgevoerd:
Voor Checkmarx tot 8.6:
// Проверяем, что есть залогиненые пользователи, выполнив запрос в БД
SELECT COUNT(*) FROM [CxDB].[dbo].LoggedinUser WHERE [ClientType] = 6;
// Если что-то есть, а на самом деле даже если и нет, попробовать выполнить запрос
DELETE FROM [CxDB].[dbo].LoggedinUser WHERE [ClientType] = 6;
Voor Checkmarx na 8.6:
// Проверяем, что есть залогиненые пользователи, выполнив запрос в БД
SELECT COUNT(*) FROM LoggedinUser WHERE (ClientType = 'Audit');
// Если что-то есть, а на самом деле даже если и нет, попробовать выполнить запрос
DELETE FROM [CxDB].[dbo].LoggedinUser WHERE (ClientType = 'Audit');Het schrijven van regels
We zijn aangekomen bij het meest interessante deel. Wanneer je begint met het schrijven van regels in CxQL, ontbreekt het vaak niet zozeer aan documentatie, maar aan praktische voorbeelden die bepaalde problemen oplossen en het werkproces van verzoeken in het algemeen beschrijven.
Ik zal proberen het leven iets te vergemakkelijken voor degenen die beginnen met het verkennen van de querytaal en zal enkele voorbeelden geven van het gebruik van Custom Queries voor het oplossen van specifieke taken. Sommigen daarvan zijn vrij algemeen en kunnen in uw bedrijf praktisch zonder veranderingen worden toegepast, terwijl andere meer specifiek zijn, maar ook kunnen worden gebruikt door de code aan te passen aan de specificaties van uw applicaties.
Dus, hier zijn de taken waarmee we het meest geconfronteerd werden:
Taak: In de uitvoerresultaten van de regel zijn er meerdere Flow's en één daarvan is een insluiting van de ander, het is noodzakelijk om er één van te behouden.
Oplossing: Inderdaad, soms toont Checkmarx meerdere Flow's van datastromen die elkaar kunnen kruisen en een verkorte versie van andere kunnen zijn. Voor dergelijke gevallen is er een speciale methode. ReduceFlow. Afhankelijk van de parameter kiest het de kortste of de langste Flow:
// Оставить только длинные Flow
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceSmallFlow);
// Оставить только короткие Flow
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceBigFlow);Taak: De lijst met gevoelige gegevens uitbreiden waar het hulpmiddel op reageert.
Oplossing: In Checkmarx zijn er basisregels, waarvan de uitvoerresultaten door veel andere verzoeken worden gebruikt. Door enkele van deze regels aan te vullen met gegevens die specifiek zijn voor uw applicatie, kunnen de scanresultaten onmiddellijk worden verbeterd. Hieronder een voorbeeld van een regel waar je mee kunt beginnen:
General_privacy_violation_list
Laten we enkele variabelen toevoegen die in onze applicatie worden gebruikt voor het opslaan van gevoelige informatie:
// Получаем результат выполнения базового правила
result = base.General_privacy_violation_list();
// Ищем элементы, которые попадают под простые регулярные выражения. Можно дополнить характерными для вас паттернами.
CxList personalList = All.FindByShortNames(new List<string> {
"*securityToken*", "*sessionId*"}, false);
// Добавляем к конечному результату
result.Add(personalList);Taak: De lijst met variabelen met wachtwoorden uitbreiden.
Oplossing: Ik zou aanraden om meteen aandacht te besteden aan de basisregel voor het definiëren van wachtwoorden in de code en deze uit te breiden met een lijst van variabelnamen die gebruikelijk zijn binnen uw bedrijf.
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*"));
// We voegen de standaard lijst met variabelen toe
List pswdIncludeList = new List{"*password*", "*psw", "psw*", "pwd*", "*pwd", "*authKey*", "pass*", "cipher*", "*cipher", "pass", "adgangskode", "benutzerkennwort", "chiffre", "clave", "codewort", "contrasena", "contrasenya", "geheimcode", "geslo", "heslo", "jelszo", "kennwort", "losenord", "losing", "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 pswdExcludeList = new List{"*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);
}
}Taak: Voeg de gebruikte frameworks toe die niet door Checkmarx worden ondersteund.
Oplossing: Alle verzoeken in Checkmarx zijn verdeeld per taal, dus de regels moeten voor elke taal worden aangevuld. Hieronder enkele voorbeelden van dergelijke regels.
Als er bibliotheken worden gebruikt die de standaardfunctionaliteit aanvullen of vervangen, kunnen deze eenvoudig aan de basisregel worden toegevoegd. Dan weten alle gebruikers ervan meteen over de nieuwe invoeringen. Een voorbeeld zijn de logging-bibliotheken voor Android — Timber en Loggi. In de basislevering van regels voor de identificatie van niet-systeembelletjes zijn er geen regels, dus als een wachtwoord of sessie-id in de log terechtkomt, vernemen we dit niet. Laten we proberen om in de Checkmarx-regels de identificatie van dergelijke methoden toe te voegen.
Testvoorbeeld van code die de bibliotheek Timber gebruikt voor 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");
}
}Hier is een voorbeeld van een verzoek voor Checkmarx, waarmee je de aanroep van Timber-methoden kunt toevoegen als een datapunt uit de applicatie:
FindAndroidOutputs
// Получаем результат выполнения базового правила
result = base.Find_Android_Outputs();
// Дополняем вызовами, которые приходят из библиотеки Timber
CxList timber = All.FindByExactMemberAccess("Timber.*") +
All.FindByShortName("Timber").GetMembersOfTarget();
// Добавляем к конечному результату
result.Add(timber);En je kunt de naburige regel aanvullen, maar deze keer met betrekking tot logging in Android:
FindAndroidLog_Outputs
// Получаем результат выполнения базового правила
result = base.Find_Android_Log_Outputs();
// Дополняем вызовами, которые приходят из библиотеки Timber
result.Add(
All.FindByExactMemberAccess("Timber.*") +
All.FindByShortName("Timber").GetMembersOfTarget()
);Als er ook gebruik wordt gemaakt van voor asynchrone taken, is het goed om dit ook aan Checkmarx te melden door de methode voor het ophalen van gegevens uit de taak toe te voegen: getInputData:
FindAndroidRead
// Получаем результат выполнения базового правила
result = base.Find_Android_Read();
// Дополняем вызовом функции getInputData, которая используется в WorkManager
CxList getInputData = All.FindByShortName("getInputData");
// Добавляем к конечному результату
result.Add(getInputData.GetMembersOfTarget());Taak: Zoek naar gevoelige gegevens in plist voor iOS-projecten
Oplossing: Vaak worden speciale bestanden met de extensie .plist gebruikt voor het opslaan van verschillende variabelen en waarden in iOS. Het opslaan van wachtwoorden, tokens, sleutels en andere gevoelige gegevens in deze bestanden is niet aan te raden, omdat ze zonder veel moeite van het apparaat kunnen worden gehaald.
Plist-bestanden hebben eigenschappen die niet direct zichtbaar zijn, maar belangrijk zijn voor Checkmarx. Laten we een regel schrijven die de gegevens zoekt die we nodig hebben en ons waarschuwt als er ergens wachtwoorden of tokens worden genoemd.
Een voorbeeld van zo'n bestand waarin een token verborgen is voor communicatie met de backend-service:
DeviceDictionary
phone
iPhone 6s
privatekey
MIICXAIBAAKBgQCqGKukO1De7zhZj6+En de regel voor Checkmarx bevat enkele nuances die moeten worden overwogen bij het schrijven:
// Используем результат выполнения правила по поиску файлов 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);Taak: Zoek informatie in XML
Oplossing: Checkmarx heeft zeer handige functies voor het werken met XML en het zoeken naar waarden, tags, attributen en dergelijke. Helaas bevat de documentatie een fout waardoor geen enkel voorbeeld werkt. Ondanks dat deze fout in de nieuwste versie van de documentatie is opgelost — wees voorzichtig als je oudere versies van documenten gebruikt.
Hier is een verkeerd voorbeeld uit de documentatie:
// Код работать не будет
result = All.FindXmlAttributesByNameAndValue("*.app", 8, “id”, "error- section", false, true);Als gevolg van de poging om uit te voeren, zullen we een foutmelding krijgen dat Alle dit is geen methode... En dat is juist, omdat er een speciale, aparte objectruimte is om functies voor het werken met XML te gebruiken - cxXPath. Zo ziet een correcte aanvraag eruit om de instelling in Android te zoeken die het gebruik van HTTP-verkeer toestaat:
// Правильный вариант с использованием cxXPath
result = cxXPath.FindXmlAttributesByNameAndValue("*.xml", 8, "cleartextTrafficPermitted", "true", false, true);Laten we dit iets dieper uitwerken, want de syntaxis van alle functies is vergelijkbaar. Zodra je gewoon bent met één, hoef je alleen de juiste te kiezen. Laten we dus stap voor stap de parameters doornemen:
"*.xml"- het bestandsmasker waarin naar gezocht moet worden.8- de taal-id waarvoor de regel geldt."cleartextTrafficPermitted"- de naam van het attribuut in de xml."true"- de waarde van dit attribuut.false- het gebruik van reguliere expressies bij het zoeken.true- betekent dat de zoekopdracht ongevoelig voor hoofdletters zal zijn, dus case-insensitive.
Voor het voorbeeld is een regel gebruikt die onjuiste, vanuit beveiligingsperspectief, netwerkverbindinginstellingen in Android definieert die communicatie met de server via het HTTP-protocol toestaan. Een voorbeeld van een instelling die het attribuut bevat cleartextTrafficPermitted met de waarde true:
example.com
secure.example.comTaak: Beperk resultaten op naam/pad van het bestand
Oplossing: In een van de grote projecten met betrekking tot de ontwikkeling van een Android-mobiele applicatie stuitten we op valse positieven van de regel die de obfuscatie-instelling definieert. Het probleem is dat de standaardregel zoekt in het bestand build.gradle de instelling die verantwoordelijk is voor het toepassen van obfuscatieregels voor de releaseversie van de applicatie.
Maar in grote projecten komen soms kindbestanden voor build.gradle, die verwijzen naar bibliotheken die in het project zijn opgenomen. Het kenmerk is dat zelfs als in deze bestanden niet wordt aangegeven dat obfuscatie nodig is, de instellingen van het bovenliggende buildbestand tijdens de compilatie worden toegepast.
Daarom is de taak om de valse positieven in kindbestanden die naar de bibliotheken verwijzen, om te leiden. Dit is mogelijk om te identificeren aan de hand van de regel apply 'com.android.library'.
Voorbeeldcode uit het bestand build.gradle, dat de noodzaak van obfuscatie definieert:
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
...
}
buildTypes {
release {
minifyEnabled true
...
}
}
}
dependencies {
...
}Voorbeeldbestand build.gradle voor een bibliotheek die in het project is opgenomen en geen dergelijke instelling heeft:
apply plugin: 'android-library'
dependencies {
compile 'com.android.support:support-v4:18.0.+'
}
android {
compileSdkVersion 14
buildToolsVersion '17.0.0'
...
}En de regel voor 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);
}
}
}Deze aanpak kan vrij universeel zijn en is nuttig, niet alleen voor Android-applicaties, maar ook voor andere situaties waarin het nodig is om de relatie van de resultaten met een bepaald bestand te bepalen.
Taak: Voeg ondersteuning voor een externe bibliotheek toe, als de syntaxis niet volledig wordt ondersteund
Oplossing: Het aantal verschillende frameworks dat wordt gebruikt bij het coderen is enorm. Natuurlijk is Checkmarx niet altijd op de hoogte van hun bestaan, en is het onze taak om deze tool te leren begrijpen dat bepaalde methoden behoren tot dit specifieke framework. Soms wordt dit bemoeilijkt door het feit dat frameworks functiesamenstellingen gebruiken die wijdverbreid zijn en die moeilijk te koppelen zijn aan een specifieke bibliotheek.
De moeilijkheid schuilt in het feit dat de syntaxis van dergelijke bibliotheken niet altijd correct wordt herkend, en het kan nodig zijn te experimenteren om geen overvloed aan valse positieven te krijgen. Er zijn verschillende opties om de scanprecisie te verbeteren en de gestelde taak op te lossen:
De eerste optie is dat we precies weten dat de bibliotheek in een bepaald project wordt gebruikt en we de regel op het teamniveau kunnen toepassen. Maar in het geval dat het team besluit een andere aanpak te gebruiken of meerdere bibliotheken gebruikt waarin functienamen overlappen, kunnen we te maken krijgen met een onplezierig aantal valse positieven.
De tweede optie is om bestanden te doorzoeken waarin de bibliotheek expliciet wordt geïmporteerd. Met deze aanpak kunnen we er zeker van zijn dat in dat bestand de gewenste bibliotheek daadwerkelijk wordt toegepast.
En de derde optie is het combineren van de twee bovenstaande benaderingen.
Als voorbeeld bekijken we een in nichekringen bekende bibliotheek voor de programmeertaal Scala, met name de functionaliteit In het algemeen moet u de operator gebruiken om parameters in een SQL-query door te geven $, die gegevens invoegt in een vooraf samengestelde SQL-query. Dit is in feite een directe overeenkomst van Prepared Statement in Java. Maar in het geval dat het nodig is om een SQL-query dynamisch te construeren, bijvoorbeeld als u tabelnamen moet doorgeven, kan de operator worden gebruikt #$, die gegevens direct in de query invoegt (bijna gelijk aan stringconcatenatie).
Voorbeeldcode:
// В общем случае - значения, контролируемые пользователем
val table = "coffees"
sql"select * from #$table where name = $name".as[Coffee].headOptionCheckmarx kan momenteel geen gebruik van Splicing Literal Values vaststellen en slaat de operators over #$, dus laten we proberen het te leren om potentiële SQL-injecties te identificeren en de nodige delen van de code te markeren:
// Находим все импорты
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));
}Taak: Zoeken naar gebruikte kwetsbare functies in Open Source-bibliotheken
Oplossing: Veel bedrijven gebruiken tools voor het controleren van Open Source (de OSA-praktijk) die helpen bij het ontdekken van het gebruik van kwetsbare versies van bibliotheken in ontwikkelde applicaties. Soms is het niet mogelijk om zo'n bibliotheek naar een veilige versie bij te werken. In sommige gevallen zijn er functionele beperkingen, in andere gevallen is er eenvoudigweg geen veilige versie. In dat geval kan een combinatie van SAST- en OSA-praktijken helpen om te bepalen dat functies die leiden tot de exploitatie van de kwetsbaarheid niet worden gebruikt in de code.
Maar soms, vooral als we JavaScript bekijken, kan dit geen triviale taak zijn. Hieronder staat een oplossing, misschien niet perfect, maar desalniettemin werkend, aan de hand van kwetsbaarheden in de component lodash in de methoden template en *set.
Voorbeelden van testpotentieel kwetsbare code in een JS-bestand:
/**
* 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 bij directe koppeling in html:
<!DOCTYPE html>
<html>
<head>
<title>Lodash Handleiding</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>We zoeken al onze kwetsbare methoden die in de kwetsbaarheden zijn opgesomd:
// Ищем все строки: в которых встречается строка 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));Taak: Zoeken naar ingebedde certificaten in de applicatie
Oplossing: Applicaties, vooral mobiele, gebruiken vaak certificaten of sleutels voor toegang tot verschillende servers of voor SSL-Pinning. Vanuit een beveiligingsoogpunt is het niet de beste praktijk om dergelijke dingen in de code op te slaan. Laten we proberen een regel te schrijven die dergelijke bestanden in de repository zoekt:
// Найдем все сертификаты по маске файла
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;Taak: Zoeken naar gecompromitteerde tokens in de applicatie
Oplossing: Het komt vaak voor dat we gecompromitteerde tokens of andere belangrijke informatie uit de code moeten intrekken. Natuurlijk is het niet de beste idee om ze binnen de bronscripts op te slaan, maar de situaties zijn verschillend. Dankzij CxQL-queries is het relatief eenvoudig om zulke dingen te vinden:
// Получаем все строки, которые содержатся в коде
CxList strings = base.Find_Strings();
// Ищем среди всех строк нужное нам значение. В примере токен в виде строки "qwerty12345"
result = strings.FindByShortName("qwerty12345");Conclusie
Ik hoop dat deze artikel nuttig zal zijn voor degenen die net beginnen met het verkennen van de Checkmarx-tool. Misschien zullen ook degenen die al lang hun eigen regels schrijven, iets waardevols in deze handleiding vinden.
Helaas ontbreekt het momenteel aan een bron waar je nieuwe ideeën kunt opdoen voor het ontwikkelen van regels voor Checkmarx. Daarom hebben we , waar we onze bevindingen zullen delen, zodat iedereen die CxQL gebruikt iets nuttigs kan vinden en ook de mogelijkheid heeft om zijn eigen werk met de gemeenschap te delen. De repository is in ontwikkeling en de inhoud wordt gestructureerd, dus bijdragers zijn welkom!
Bedankt voor uw aandacht!
Bron: habr.com

Preset configuratie in de Checkmarx-interface
CxAudit-interface
Verdeling van regels op basis van taal
Bepaling van het type regel bij creatie
Voorbeeld van een nieuwe regel in de interface van Preset Manager
Bepalen van het niveau waarop de regel zal worden toegepast