Chekmarks qoidalarini aqldan ozgan holda qanday yozish kerak

Hey Xabr!

Bizning ishimizda kompaniyamiz ko'pincha turli xil statik kodlarni tahlil qilish vositalari (SAST) bilan shug'ullanadi. Qutidan tashqari ularning barchasi o'rtacha ishlaydi. Albatta, barchasi loyiha va unda qo'llaniladigan texnologiyalarga, shuningdek, ushbu texnologiyalar tahlil qoidalari bilan qanchalik qamrab olinganligiga bog'liq. Menimcha, SAST vositasini tanlashda eng muhim mezonlardan biri bu uni ilovalaringizning o'ziga xos xususiyatlariga moslashtirish, ya'ni tahlil qoidalarini yozish va o'zgartirish yoki, odatda, maxsus so'rovlar.

Chekmarks qoidalarini aqldan ozgan holda qanday yozish kerak

Biz ko'pincha Checkmarx-dan foydalanamiz - juda qiziqarli va kuchli kod analizatori. Ushbu maqolada men buning uchun tahlil qoidalarini yozish tajribam haqida gapiraman.

Mundarija

kirish

Boshlash uchun men Checkmarx uchun so'rovlarni yozish xususiyatlari haqida rus tilidagi bir nechta maqolalardan birini tavsiya qilmoqchiman. U Habré-da 2019 yil oxirida quyidagi sarlavha ostida nashr etilgan: "Salom, Checkmarks!" Checkmarx SAST so'rovini qanday yozish va ajoyib zaifliklarni topish.

U ba'zi test ilovalari uchun CxQL (Checkmark Query Language) da birinchi so'rovlarni qanday yozishni batafsil ko'rib chiqadi va tahlil qoidalari qanday ishlashining asosiy tamoyillarini ko'rsatadi.

Men unda tasvirlangan narsalarni takrorlamayman, garchi ba'zi chorrahalar hali ham mavjud bo'lsa. Maqolamda men "retseptlar to'plami" ni, Checkmarx bilan ishlaganimda duch kelgan muayyan muammolarning echimlari ro'yxatini tuzishga harakat qilaman. Men bu muammolarning ko'pchiligi ustida miyamni to'plashim kerak edi. Ba'zida hujjatlarda etarli ma'lumot yo'q edi va ba'zida talab qilinadigan narsani qanday qilishni tushunish qiyin edi. Umid qilamanki, mening tajribam va uyqusiz tunlar behuda ketmaydi va bu "Maxsus so'rovlar retseptlari to'plami" sizni bir necha soat yoki bir nechta nerv hujayralarini qutqaradi. Shunday ekan, boshlaylik!

Qoidalar haqida umumiy ma'lumot

Birinchidan, keyin nima bo'lishini yaxshiroq tushunish uchun bir nechta asosiy tushunchalarni va qoidalar bilan ishlash jarayonini ko'rib chiqaylik. Shuningdek, hujjatlarda bu haqda hech narsa aytilmaganligi yoki tuzilishda juda keng tarqalganligi sababli, bu juda qulay emas.

  1. Qoidalar skanerlash vaqtida boshida tanlangan oldindan o'rnatilgan (faol qoidalar to'plami)ga qarab qo'llaniladi. Siz cheksiz miqdordagi oldindan sozlashlarni yaratishingiz mumkin va ularni qanday tuzilish jarayoni sizning jarayoningizning o'ziga xos xususiyatlariga bog'liq. Siz ularni til bo'yicha guruhlashingiz yoki har bir loyiha uchun oldindan sozlashlarni tanlashingiz mumkin. Faol qoidalar soni skanerlash tezligi va aniqligiga ta'sir qiladi.

    Chekmarks qoidalarini aqldan ozgan holda qanday yozish kerakCheckmarx interfeysida oldindan o'rnatishni sozlash

  2. Qoidalar CxAuditor deb nomlangan maxsus vositada tahrirlanadi. Bu Checkmarx ishlaydigan serverga ulanadigan ish stoli ilovasi. Ushbu vosita ikkita ish rejimiga ega: tartibga solish qoidalari va allaqachon bajarilgan skanerlash natijalarini tahlil qilish.

    Chekmarks qoidalarini aqldan ozgan holda qanday yozish kerakCxAudit interfeysi

  3. Checkmarx-dagi qoidalar til bo'yicha bo'linadi, ya'ni har bir tilning o'ziga xos so'rovlar to'plami mavjud. Tildan qat'iy nazar qo'llaniladigan ba'zi umumiy qoidalar ham mavjud, bular asosiy so'rovlar deb ataladi. Ko'pincha, asosiy so'rovlar boshqa qoidalar ishlatadigan ma'lumotlarni qidirishni o'z ichiga oladi.

    Chekmarks qoidalarini aqldan ozgan holda qanday yozish kerakQoidalarni til bo'yicha ajratish

  4. Qoidalar "Bajariladigan" va "Bajarilmaydigan" (bajarilgan va bajarilmagan). Menimcha, unchalik to'g'ri ism emas, lekin bu shunday. Xulosa shuki, "Bajariladigan" qoidalarni bajarish natijasi UIda skanerlash natijalarida ko'rsatiladi va "Bajarilmaydigan" qoidalar faqat ularning natijalarini boshqa so'rovlarda ishlatish uchun kerak (mohiyatida, shunchaki funktsiya).

    Chekmarks qoidalarini aqldan ozgan holda qanday yozish kerakYaratishda qoida turini aniqlash

  5. Siz yangi qoidalar yaratishingiz yoki mavjudlarini to'ldirishingiz/qayta yozishingiz mumkin. Qoidani qayta yozish uchun siz uni daraxtda topishingiz kerak, sichqonchaning o'ng tugmachasini bosing va ochiladigan menyudan "O'chirish" ni tanlang. Bu erda shuni esda tutish kerakki, yangi qoidalar dastlab oldindan sozlamalarga kiritilmagan va faol emas. Ulardan foydalanishni boshlash uchun siz ularni asbobdagi "Preset Manager" menyusida faollashtirishingiz kerak. Qayta yozilgan qoidalar o'z sozlamalarini saqlab qoladi, ya'ni agar qoida faol bo'lsa, u shunday qoladi va darhol qo'llaniladi.

    Chekmarks qoidalarini aqldan ozgan holda qanday yozish kerakPreset Manager interfeysidagi yangi qoidaga misol

  6. Amalga oshirish jarayonida so'rovlarning "daraxt"i quriladi, bu nimaga bog'liq. Ma'lumot to'playdigan qoidalar birinchi navbatda, undan foydalanadiganlar esa bajariladi. Bajarish natijasi keshlangan, shuning uchun agar mavjud qoida natijalaridan foydalanish mumkin bo'lsa, unda buni qilish yaxshidir, bu skanerlash vaqtini qisqartiradi.

  7. Qoidalar turli darajalarda qo'llanilishi mumkin:

  • Butun tizim uchun - har qanday loyihani har qanday skanerlash uchun ishlatiladi

  • Jamoa darajasida (Jamoa) - faqat tanlangan jamoadagi loyihalarni skanerlash uchun foydalaniladi.

  • Loyiha darajasida - Muayyan loyihada qo'llaniladi

    Chekmarks qoidalarini aqldan ozgan holda qanday yozish kerakQoida qo'llanilishi darajasini aniqlash

Yangi boshlanuvchilar uchun "lug'at"

Va men savollarga sabab bo'lgan bir nechta narsalardan boshlayman, shuningdek, hayotni sezilarli darajada soddalashtiradigan bir qator texnikalarni ko'rsataman.

Ro'yxatlar bilan operatsiyalar

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

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

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

Barcha topilgan narsalar

Skanerlangan tilda siz Checkmarx aniqlagan mutlaqo barcha elementlarning ro'yxatini olishingiz mumkin (satrlar, funktsiyalar, sinflar, usullar va boshqalar). Bu orqali kirish mumkin bo'lgan ba'zi ob'ektlar maydoni All. Ya'ni, ma'lum bir nomga ega ob'ektni qidirish searchMe, siz, masalan, barcha topilgan ob'ektlar bo'ylab nom bo'yicha qidirishingiz mumkin:

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

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

Ammo, agar biron sababga ko'ra skanerlashga kiritilmagan boshqa tilda qidirishingiz kerak bo'lsa (masalan, Android loyihasidagi ajoyib), siz o'zgaruvchi orqali ob'ekt maydonini kengaytirishingiz mumkin:

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

Oqim tahlili uchun funksiyalar

Ushbu funktsiyalar ko'plab qoidalarda qo'llaniladi va bu erda ular nimani anglatishini ko'rsatadigan kichik varaq:

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

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

Fayl nomi/yoʻli olinmoqda

So'rov natijalari bo'yicha olinishi mumkin bo'lgan bir nechta atributlar mavjud (yozuv topilgan fayl nomi, string va boshqalar), lekin hujjatlarda ularni qanday olish va ishlatish haqida aytilmagan. Shunday qilib, buning uchun siz LinePragma xususiyatiga kirishingiz kerak va bizga kerak bo'lgan ob'ektlar uning ichida joylashgan bo'ladi:

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

Shuni yodda tutish kerak FileName aslida faylga yo'lni o'z ichiga oladi, chunki biz usuldan foydalanganmiz GetFirstGraph.

Amalga oshirish natijasi

CxQL ichida maxsus o'zgaruvchi mavjud result, bu sizning yozma qoidangizni bajarish natijasini qaytaradi. U darhol ishga tushiriladi va siz unga oraliq natijalarni yozishingiz, ishlayotganingizda ularni o'zgartirishingiz va takomillashtirishingiz mumkin. Ammo, qoida ichida ushbu o'zgaruvchiga yoki funktsiyaga tayinlanmagan bo'lsa return- bajarish natijasi har doim nolga teng bo'ladi.

Quyidagi so'rov bajarilish natijasida bizga hech narsa qaytarmaydi va har doim bo'sh qoladi:

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

Ammo, bajarilish natijasini sehrli o'zgaruvchan natijaga belgilab, biz ushbu qo'ng'iroq bizga nimani qaytarishini ko'ramiz:

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

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

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

Boshqa qoidalar natijalaridan foydalanish

Checkmarx-dagi qoidalarni oddiy dasturlash tilidagi funktsiyalarga o'xshash deb atash mumkin. Qoidani yozishda siz boshqa so'rovlar natijalaridan yaxshi foydalanishingiz mumkin. Misol uchun, har safar koddagi barcha usul chaqiruvlarini qidirishning hojati yo'q, faqat kerakli qoidani chaqiring:

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

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

Ushbu yondashuv kodni qisqartirish va qoidani bajarish vaqtini sezilarli darajada qisqartirish imkonini beradi.

Muammolarni bartaraf qilish

Jurnal yozish

Asbob bilan ishlashda ba'zida kerakli so'rovni darhol yozishning iloji bo'lmaydi va siz turli xil variantlarni sinab ko'rishingiz kerak. Bunday holda, asbob quyidagi tarzda nomlanadigan jurnalni qayd qilishni ta'minlaydi:

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

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

Ammo shuni esda tutish kerakki, bu usul faqat kirish sifatida qabul qilinadi ip, shuning uchun birinchi operatsiya natijasida topilgan elementlarning to'liq ro'yxatini ko'rsatish mumkin bo'lmaydi. Nosozliklarni tuzatish uchun ishlatiladigan ikkinchi variant vaqti-vaqti bilan sehrli o'zgaruvchiga tayinlashdir result so'rov natijasi va nima sodir bo'lishini ko'ring. Ushbu yondashuv unchalik qulay emas; kodda bu bilan hech qanday bekor qilish yoki operatsiyalar yo'qligiga ishonch hosil qilishingiz kerak. result yoki oddiygina quyidagi kodni sharhlang. Yoki siz men kabi bir nechta bunday qo'ng'iroqlarni tayyor qoidadan olib tashlashni unutib qo'yishingiz va nima uchun hech narsa ishlamasligiga hayron bo'lishingiz mumkin.

Usulni chaqirish yanada qulayroqdir return kerakli parametr bilan. Bunday holda, qoidaning bajarilishi tugaydi va biz yozganimiz natijasida nima bo'lganini ko'rishimiz mumkin bo'ladi:

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

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

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

Kirish muammosi

CxAudit vositasiga (qoidalarni yozish uchun foydalaniladigan) kira olmaydigan holatlar mavjud. Buning sabablari ko'p bo'lishi mumkin, jumladan, nosozliklar, to'satdan Windows yangilanishlari, BSOD va boshqa kutilmagan vaziyatlar, bu bizning nazoratimizdan tashqarida. Bunday holda, ba'zida ma'lumotlar bazasida tugallanmagan seans mavjud bo'lib, bu sizni qayta kirishga to'sqinlik qiladi. Buni tuzatish uchun siz bir nechta so'rovlarni bajarishingiz kerak:

8.6 dan oldingi Checkmarx uchun:

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

8.6 dan keyin Checkmarx uchun:

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

Yozish qoidalari

Endi biz eng qiziqarli qismga o'tamiz. CxQL-da qoidalarni yozishni boshlaganingizda, sizga ko'pincha hujjatlar emas, balki ma'lum muammolarni hal qilish va so'rovlar qanday ishlashini tasvirlashning jonli misollari.

Men so'rovlar tiliga sho'ng'ishni boshlaganlar uchun hayotni biroz osonlashtirishga harakat qilaman va muayyan muammolarni hal qilish uchun Maxsus so'rovlardan foydalanishga bir nechta misollar keltiraman. Ulardan ba'zilari juda umumiy bo'lib, kompaniyangizda deyarli o'zgarishsiz qo'llanilishi mumkin, boshqalari aniqroq, ammo ular ilovalaringizning o'ziga xos xususiyatlariga mos ravishda kodni o'zgartirish orqali ham ishlatilishi mumkin.

Shunday qilib, biz tez-tez duch keladigan muammolar:

Vazifa: Qoidani bajarish natijalarida bir nechta oqimlar mavjud va ulardan biri boshqasining uyasi bo'lib, ulardan birini tark etishingiz kerak.

yechim: Darhaqiqat, ba'zida Checkmarx bir-biriga mos kelishi va boshqalarning qisqartirilgan versiyasi bo'lishi mumkin bo'lgan bir nechta ma'lumotlar oqimlarini ko'rsatadi. Bunday holatlar uchun maxsus usul mavjud Oqimni kamaytirish. Parametrga qarab, u eng qisqa yoki eng uzun oqimni tanlaydi:

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

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

Vazifa: Asbob javob beradigan maxfiy ma'lumotlar ro'yxatini kengaytiring

yechim: Checkmarx asosiy qoidalariga ega, ularning natijalari ko'plab boshqa so'rovlar tomonidan qo'llaniladi. Ushbu qoidalarning ba'zilarini ilovangizga xos ma'lumotlar bilan to'ldirish orqali siz skanerlash natijalarini darhol yaxshilashingiz mumkin. Quyida siz boshlash uchun misol qoidasi keltirilgan:

Umumiy_maxfiylik_buzilishi_ro'yxati

Ilovada maxfiy ma'lumotlarni saqlash uchun ishlatiladigan bir nechta o'zgaruvchilarni qo'shamiz:

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

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

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

Vazifa: Parollar bilan o'zgaruvchilar ro'yxatini kengaytiring

yechim: Men darhol koddagi parollarni aniqlashning asosiy qoidasiga e'tibor qaratishni va unga kompaniyangizda tez-tez ishlatiladigan o'zgaruvchilar nomlari ro'yxatini qo'shishni tavsiya qilaman.

Parol_maxfiylik_buzilishi_ro'yxati

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

Vazifa: Checkmarx tomonidan qo'llab-quvvatlanmaydigan ishlatilgan ramkalarni qo'shing

yechim: Checkmarx-dagi barcha so'rovlar tilga bo'lingan, shuning uchun har bir til uchun qoidalar qo'shishingiz kerak. Quyida bunday qoidalarga misollar keltirilgan.

Agar standart funktsiyalarni to'ldiradigan yoki o'rnini bosadigan kutubxonalar ishlatilsa, ularni asosiy qoidaga osongina qo'shish mumkin. Shunda uni ishlatadigan har bir kishi darhol yangi tanishuvlar haqida bilib oladi. Misol tariqasida, Android tizimiga kirish uchun kutubxonalar Timber va Loggi hisoblanadi. Asosiy paketda tizimdan tashqari qo'ng'iroqlarni aniqlash qoidalari yo'q, shuning uchun parol yoki sessiya identifikatori jurnalga kirsa, biz bu haqda bilmaymiz. Keling, bunday usullarning ta'riflarini Checkmarx qoidalariga qo'shishga harakat qilaylik.

Timber kutubxonasini jurnalga yozish uchun ishlatadigan test kodi misoli:

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

Va bu erda Checkmarx so'rovining misoli keltirilgan, bu sizga dasturdan ma'lumotlar uchun chiqish nuqtasi sifatida Timber usullarini chaqirish ta'rifini qo'shish imkonini beradi:

FindAndroidOutputs

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

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

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

Va siz qo'shni qoidaga ham qo'shishingiz mumkin, ammo bu to'g'ridan-to'g'ri Android tizimiga kirish bilan bog'liq:

FindAndroidLog_Outputs

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

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

Bundan tashqari, agar Android ilovalari ishlatilsa Ish menejeri asinxron ish uchun, topshiriqdan ma'lumotlarni olish usulini qo'shish orqali bu haqda Checkmarx-ga qo'shimcha ma'lumot berish yaxshidir. getInputData:

FindAndroidRead

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

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

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

Vazifa: iOS loyihalari uchun plistda maxfiy maʼlumotlar qidirilmoqda

yechim: iOS ko'pincha turli o'zgaruvchilar va qiymatlarni saqlash uchun .plist kengaytmali maxsus fayllardan foydalanadi. Ushbu fayllarda parollar, tokenlar, kalitlar va boshqa nozik ma'lumotlarni saqlash tavsiya etilmaydi, chunki ularni hech qanday muammosiz qurilmadan chiqarib olish mumkin.

Plist fayllari yalang'och ko'zga ko'rinmaydigan, ammo Checkmarx uchun muhim bo'lgan xususiyatlarga ega. Keling, bizga kerakli ma'lumotlarni qidiradigan va parollar yoki tokenlar qaerdadir eslatib o'tilganligini aytib beradigan qoida yozaylik.

Backend xizmati bilan aloqa qilish uchun tokenni o'z ichiga olgan bunday faylga misol:

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

Va yozishda e'tiborga olinishi kerak bo'lgan bir nechta nuanslarga ega bo'lgan Checkmarx qoidasi:

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

Vazifa: XML-da ma'lumotlarni qidirish

yechim: Checkmarx XML bilan ishlash va qiymatlar, teglar, atributlar va boshqalarni qidirish uchun juda qulay funksiyalarga ega. Ammo, afsuski, hujjatlarda xatolik yuz berdi, shuning uchun bitta misol ishlamaydi. Hujjatlarning so'nggi versiyasida ushbu nuqson bartaraf etilganiga qaramay, hujjatlarning oldingi versiyalaridan foydalansangiz ehtiyot bo'ling.

Hujjatlardan noto'g'ri misol:

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

Amalga oshirishga urinish natijasida biz xatoga duch kelamiz All Bunday usul yo'q ... Va bu to'g'ri, chunki XML bilan ishlash uchun funktsiyalardan foydalanish uchun maxsus, alohida ob'ekt maydoni mavjud - cxXPath. Androidda HTTP trafigidan foydalanish imkonini beruvchi sozlamani topish uchun toʻgʻri soʻrov shunday koʻrinadi:

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

Keling, buni biroz batafsilroq ko'rib chiqaylik, chunki barcha funktsiyalarning sintaksisi o'xshash, siz bittasini aniqlaganingizdan so'ng, sizga kerak bo'lgan narsani tanlashingiz kerak. Shunday qilib, parametrlarga ko'ra ketma-ket:

  • "*.xml"— qidiriladigan fayllar niqobi

  • 8 — qoida qo‘llaniladigan tilning identifikatori

  • "cleartextTrafficPermitted"— xml formatidagi atribut nomi

  • "true" - bu atributning qiymati

  • false — qidiruvda muntazam ifodalardan foydalanish

  • true — qidiruv katta-kichik harflarga e'tibor bermasdan, ya'ni katta-kichik harflarsiz amalga oshirilishini bildiradi

Misol sifatida biz HTTP protokoli orqali server bilan aloqa o'rnatishga imkon beruvchi Android-da xavfsizlik nuqtai nazaridan noto'g'ri tarmoq ulanishi sozlamalarini aniqlaydigan qoidadan foydalandik. Atributni o'z ichiga olgan sozlamaga misol cleartextTrafficPermitted ma'nosi bilan 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>

Vazifa: Natijalarni fayl nomi/yo‘li bilan cheklang

yechim: Android uchun mobil ilovani ishlab chiqish bilan bog'liq yirik loyihalardan birida biz chalkashlik sozlamalarini belgilaydigan qoidaning noto'g'ri ijobiy tomonlariga duch keldik. Haqiqat shundaki, qoida tashqarida faylni qidiradi build.gradle ilovaning reliz versiyasi uchun chalkashlik qoidalarini qo'llash uchun mas'ul bo'lgan sozlama.

Ammo yirik loyihalarda ba'zan bolalar fayllari mavjud build.gradle, bu loyihaga kiritilgan kutubxonalarga tegishli. O'ziga xosligi shundaki, agar bu fayllar chalkashlik zarurligini ko'rsatmasa ham, kompilyatsiya paytida ota-montaj faylining sozlamalari qo'llaniladi.

Shunday qilib, vazifa kutubxonalarga tegishli bo'lgan bolalar fayllaridagi triggerlarni kesishdir. Ular chiziqning mavjudligi bilan aniqlanishi mumkin apply 'com.android.library'.

Fayldan namuna kodi build.gradle, bu chalkashlik zarurligini belgilaydi:

apply plugin: 'com.android.application'

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

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

dependencies {
  ...
}

Fayl misol build.gradle ushbu sozlamaga ega bo'lmagan loyihaga kiritilgan kutubxona uchun:

apply plugin: 'android-library'

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

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

Va Checkmarx qoidasi:

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

Ushbu yondashuv juda universal va nafaqat Android ilovalari uchun, balki natija ma'lum bir faylga tegishli yoki yo'qligini aniqlash kerak bo'lgan boshqa holatlar uchun ham foydali bo'lishi mumkin.

Vazifa: Sintaksis to'liq qo'llab-quvvatlanmasa, uchinchi tomon kutubxonasiga yordam qo'shing

yechim: Kod yozish jarayonida foydalaniladigan turli xil ramkalar soni shunchaki jadvallardan tashqarida. Albatta, Checkmarks har doim ham ularning mavjudligi haqida bilmaydi va bizning vazifamiz unga ma'lum usullar ushbu ramkaga tegishli ekanligini tushunishga o'rgatishdir. Ba'zida bu ramkalar juda keng tarqalgan funksiya nomlaridan foydalanishi va ma'lum bir kutubxonaga ma'lum bir qo'ng'iroqning munosabatini aniq aniqlash mumkin emasligi bilan murakkablashadi.

Qiyinchilik shundaki, bunday kutubxonalarning sintaksisi har doim ham to'g'ri tan olinmaydi va siz ko'p sonli noto'g'ri pozitivlarni olmaslik uchun tajriba qilishingiz kerak. Skanerlashning aniqligini oshirish va muammoni hal qilish uchun bir nechta variant mavjud:

  • Birinchi variant, biz aniq bilamizki, kutubxona ma'lum bir loyihada qo'llaniladi va qoidani jamoaviy darajada qo'llashi mumkin. Ammo agar jamoa boshqa yondashuvni tanlashga qaror qilsa yoki funksiya nomlari bir-biriga mos keladigan bir nechta kutubxonalardan foydalansa, biz ko'plab noto'g'ri pozitivlarning unchalik yoqimli bo'lmagan rasmini olishimiz mumkin.

  • Ikkinchi variant - kutubxona aniq import qilinadigan fayllarni qidirish. Ushbu yondashuv yordamida bizga kerak bo'lgan kutubxona aynan ushbu faylda ishlatilganligiga ishonch hosil qilishimiz mumkin.

  • Uchinchi variant esa yuqoridagi ikkita yondashuvni birgalikda ishlatishdir.

Misol tariqasida, tor doiralarda taniqli kutubxonani ko'rib chiqaylik silliq Scala dasturlash tili uchun, ya'ni funksionallik Literal qiymatlarni birlashtirish. Umuman olganda, parametrlarni SQL so'roviga o'tkazish uchun siz operatordan foydalanishingiz kerak $, bu ma'lumotlarni oldindan tuzilgan SQL so'roviga almashtiradi. Ya'ni, aslida, bu Java-dagi Prepared Statementning bevosita analogidir. Ammo, agar siz dinamik ravishda SQL so'rovini yaratishingiz kerak bo'lsa, masalan, jadval nomlarini kiritishingiz kerak bo'lsa, operatordan foydalanishingiz mumkin. #$, bu ma'lumotlarni to'g'ridan-to'g'ri so'rovga almashtiradi (deyarli qatorni birlashtirish kabi).

Kod namunasi:

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

Checkmarx hali Splicing Literal Values-dan foydalanishni qanday aniqlashni bilmaydi va operatorlarni o'tkazib yuboradi. #$, shuning uchun keling, uni potentsial SQL in'ektsiyalarini aniqlash va koddagi to'g'ri joylarni ajratib ko'rsatishni o'rgatishga harakat qilaylik:

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

Vazifa: Ochiq kodli kutubxonalarda foydalanilgan zaif funksiyalarni qidiring

yechim: Ko'pgina kompaniyalar ishlab chiqilgan ilovalarda kutubxonalarning zaif versiyalaridan foydalanishni aniqlash uchun Open Source monitoring vositalaridan (OSA amaliyoti) foydalanadilar. Ba'zan bunday kutubxonani xavfsiz versiyaga yangilash mumkin emas. Ba'zi hollarda funktsional cheklovlar mavjud, boshqalarida esa xavfsiz versiya umuman yo'q. Bunday holda, SAST va OSA amaliyotlarining kombinatsiyasi zaiflikdan foydalanishga olib keladigan funktsiyalar kodda ishlatilmasligini aniqlashga yordam beradi.

Ammo ba'zida, ayniqsa JavaScript-ni ko'rib chiqayotganda, bu mutlaqo ahamiyatsiz ish bo'lmasligi mumkin. Quyida, ehtimol ideal emas, lekin shunga qaramay, komponentdagi zaifliklar misolida ishlaydigan yechim mavjud. lodash usullarda template и *set.

JS faylida potentsial zaif kod sinoviga misollar:

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

Va to'g'ridan-to'g'ri html-ga ulanganda:

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

Biz zaifliklar qatorida keltirilgan barcha zaif usullarimizni qidiramiz:

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

Vazifa: Ilovaga o'rnatilgan sertifikatlar qidirilmoqda

yechim: Ilovalar, ayniqsa mobil ilovalar uchun turli serverlarga kirish yoki SSL-Pinningni tekshirish uchun sertifikatlar yoki kalitlardan foydalanish odatiy hol emas. Xavfsizlik nuqtai nazaridan bunday narsalarni kodda saqlash eng yaxshi amaliyot emas. Keling, omborda o'xshash fayllarni qidiradigan qoida yozishga harakat qilaylik:

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

Vazifa: Ilovada buzilgan tokenlarni topish

yechim: Ko'pincha buzilgan tokenlarni yoki kodda mavjud bo'lgan boshqa muhim ma'lumotlarni bekor qilish kerak. Albatta, ularni manba kodida saqlash yaxshi fikr emas, lekin vaziyatlar turlicha. CxQL so'rovlari tufayli shunga o'xshash narsalarni topish juda oson:

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

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

xulosa

Umid qilamanki, ushbu maqola Checkmarx vositasi bilan tanishishni boshlaganlar uchun foydali bo'ladi. Ehtimol, uzoq vaqt davomida o'z qoidalarini yozayotganlar ham ushbu qo'llanmada foydali narsalarni topadilar.

Afsuski, hozirda Checkmarx qoidalarini ishlab chiqishda yangi g'oyalarni to'plash mumkin bo'lgan resurs yo'q. Shuning uchun biz yaratdik Github-dagi ombor, bu erda biz o'z ishimizni joylashtiramiz, shunda CxQL-dan foydalanadigan har bir kishi unda foydali narsalarni topishi va o'z ishlarini hamjamiyat bilan baham ko'rish imkoniyatiga ega bo'lishi mumkin. Repozitariy tarkibni to‘ldirish va tizimlashtirish jarayonida, shuning uchun hissa qo‘shuvchilarni xush kelibsiz!

E'tiboringiz uchun tashakkur!

Manba: www.habr.com

a Izoh qo'shish