Checkmarx эрежелерин жинди болбостон кантип жазуу керек

Эй Хабр!

Биздин ишибизде, биздин компания көп учурда ар кандай статикалык кодду талдоо куралдары (SAST) менен алектенет. Алардын бардыгы орто эсеп менен иштешет. Албетте, бардыгы долбоордон жана анда колдонулган технологиялардан, ошондой эле бул технологиялар анализдин эрежелеринде канчалык деңгээлде камтылганынан көз каранды. Менин оюмча, SAST куралын тандоодо эң маанилүү критерийлердин бири - бул аны тиркемелериңиздин өзгөчөлүктөрүнө ылайыкташтыруу, тактап айтканда, талдоо эрежелерин жазуу жана өзгөртүү, же алар көбүнчө "Башка суроо" деп аталат.

Checkmarx эрежелерин жинди болбостон кантип жазуу керек

Биз көбүнчө Checkmarx колдонобуз - абдан кызыктуу жана күчтүү код анализатору. Бул макалада мен ал үчүн талдоо эрежелерин жазуу тажрыйбасы жөнүндө айтып берет.

Мазмуну

кирүү

Баштоо үчүн, мен Checkmarx үчүн суроо жазуу өзгөчөлүктөрү жөнүндө орус тилиндеги бир нече макалалардын бирин сунуштагым келет. Ал Habré сайтында 2019-жылдын аягында төмөнкү аталыш менен жарыяланган: "Салам, Checkmarkx!" Checkmarx SAST суроосун кантип жазууга жана сонун кемчиликтерди табууга болот.

Ал кээ бир тесттик тиркемелер үчүн CxQL (Checkmarx Query Language) биринчи суроо-талаптарын кантип жазууну майда-чүйдөсүнө чейин изилдейт жана талдоо эрежелеринин иштөөсүнүн негизги принциптерин көрсөтөт.

Мен анда айтылгандарды кайталабайм, бирок кээ бир кесилиштер дагы деле бар. Мен өзүмдүн макаламда "рецепттердин жыйнагын" түзүүгө аракет кылам, Checkmarx менен иштөөдө жолуккан конкреттүү көйгөйлөрдү чечүүнүн тизмеси. Мен бул көйгөйлөрдүн көбүндө мээмди тыйыш керек болчу. Кээде документтерде маалымат жетишсиз болуп, кээде талап кылынган нерсени кантип жасоону түшүнүү да кыйын болгон. Менин тажрыйбам жана уйкусуз түндөрүм текке кетпейт деп үмүттөнөм жана бул "Башка суроо рецепттеринин жыйнагы" сизге бир нече саат же бир нече нерв клеткаларын сактап калат. Ошентип, баштайлы!

Эрежелер боюнча жалпы маалымат

Биринчиден, мындан ары эмне болорун жакшыраак түшүнүү үчүн, бир нече негизги түшүнүктөрдү жана эрежелер менен иштөө процессин карап көрөлү. Жана ошондой эле документацияда бул жөнүндө эч нерсе айтылбайт же түзүмдө абдан жайылып кеткендиктен, бул абдан ыңгайлуу эмес.

  1. Эрежелер сканерлөө учурунда башталгычта тандалган алдын ала коюлганга жараша колдонулат (активдүү эрежелердин жыйындысы). Сиз чексиз сандагы алдын ала орнотууларды түзө аласыз жана аларды кантип түзүшүңүз процессиңиздин өзгөчөлүгүнө жараша болот. Сиз аларды тил боюнча топтой аласыз же ар бир долбоор үчүн алдын ала орнотууларды тандай аласыз. Активдүү эрежелердин саны сканерлөөнүн ылдамдыгына жана тактыгына таасирин тийгизет.

    Checkmarx эрежелерин жинди болбостон кантип жазуу керекCheckmarx интерфейсинде Preset орнотуу

  2. Эрежелер CxAuditor деп аталган атайын куралда түзөтүлөт. Бул Checkmarx иштеткен серверге туташкан рабочий колдонмо. Бул куралда эки иштөө режими бар: эрежелерди түзөтүү жана буга чейин аткарылган сканерлөөнүн натыйжаларын талдоо.

    Checkmarx эрежелерин жинди болбостон кантип жазуу керекCxAudit интерфейси

  3. Checkmarx эрежелери тил боюнча бөлүнөт, башкача айтканда, ар бир тилде өзүнүн суроо-талаптары бар. Ошондой эле тилге карабастан колдонулуучу кээ бир жалпы эрежелер бар, булар негизги сурамдар деп аталат. Көпчүлүк учурда, негизги сурамдар башка эрежелер колдонгон маалыматты издөөнү камтыйт.

    Checkmarx эрежелерин жинди болбостон кантип жазуу керекТил боюнча эрежелерди бөлүү

  4. Эрежелер "Аткарылуучу" жана "Аткарылбай турган" (аткарылган жана аткарылбайт). Менин оюмча, так ат эмес, бирок бул. Жыйынтык: "Аткарылуучу" эрежелердин аткарылышынын натыйжасы UIде сканерлөөнүн натыйжаларында көрсөтүлөт, ал эми "Аткарылбоочу" эрежелер алардын натыйжаларын башка суроо-талаптарда колдонуу үчүн гана керек (негизинен, жөн гана функция).

    Checkmarx эрежелерин жинди болбостон кантип жазуу керекТүзүүдө эреженин түрүн аныктоо

  5. Сиз жаңы эрежелерди түзө аласыз же учурдагыларды толуктай аласыз/кайра жаза аласыз. Эрежени кайра жазуу үчүн, аны дарактан таап, чычкандын оң баскычын чыкылдатып, ачылуучу менюдан "Оттуруу" дегенди тандаңыз. Бул жерде жаңы эрежелер алгач алдын ала орнотулган эмес жана активдүү эмес экенин эстен чыгарбоо керек. Аларды колдонууну баштоо үчүн, аларды аспаптын "Алдын ала коюлган менеджер" менюсунда активдештирүү керек. Кайра жазылган эрежелер өз жөндөөлөрүн сактап калат, башкача айтканда, эгер эреже активдүү болсо, ал ошол бойдон калат жана дароо колдонулат.

    Checkmarx эрежелерин жинди болбостон кантип жазуу керекPreset Manager интерфейсиндеги жаңы эреженин мисалы

  6. Аткаруу учурунда суроо-талаптардын "дарагы" курулат, бул эмнеден көз каранды. Биринчи кезекте маалыматты чогулткан эрежелер аткарылат, экинчиден аны колдонгондор аткарылат. Аткаруу натыйжасы кэште сакталган, андыктан учурдагы эреженин натыйжаларын колдонуу мүмкүн болсо, анда муну кылган жакшы, бул сканерлөө убактысын кыскартат.

  7. Эрежелер ар кандай деңгээлде колдонулушу мүмкүн:

  • Бүтүндөй система үчүн - каалаган долбоорду сканерлөө үчүн колдонулат

  • Команда деңгээлинде (Команда) - тандалган командадагы долбоорлорду сканерлөө үчүн гана колдонулат.

  • Долбоордун деңгээлинде - Белгилүү бир долбоордо колдонулат

    Checkmarx эрежелерин жинди болбостон кантип жазуу керекЭреже колдонула турган деңгээлди аныктоо

Жаңы баштагандар үчүн "Сөздүк"

Анан мен мага суроо жараткан бир нече нерселерден баштайм, ошондой эле жашоону кыйла жөнөкөйлөтө турган бир катар ыкмаларды көрсөтөм.

Тизмелер менен операциялар

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

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

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

Бардык табылган буюмдар

Сканерленген тилде сиз Checkmarx аныктаган бардык элементтердин тизмесин ала аласыз (саптар, функциялар, класстар, методдор ж.б.). Бул аркылуу кирүүгө мүмкүн болгон объекттердин кээ бир мейкиндиги All. Башкача айтканда, белгилүү бир ат менен объект издөө searchMe, мисалы, бардык табылган объекттер боюнча аты боюнча издей аласыз:

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

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

Бирок, кандайдыр бир себептерден улам скандоодо кирбей калган башка тилде издөө керек болсо (мисалы, Android долбоорунда укмуштуудай), сиз өзгөрмө аркылуу объект мейкиндигин кеңейте аласыз:

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

Агымды анализдөө үчүн функциялар

Бул функциялар көптөгөн эрежелерде колдонулат жана бул жерде алар эмнени билдирерин бир аз алдамчылык баракчасы:

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

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

Файлдын атын/жолун алуу

Сурамдын натыйжаларынан алынышы мүмкүн болгон бир нече атрибуттар бар (жазуу табылган файлдын аты, сап ж.б.), бирок документацияда аларды кантип алуу жана колдонуу керектиги айтылбайт. Ошентип, бул үчүн сиз LinePragma касиетине кирүүңүз керек жана бизге керектүү объекттер анын ичинде жайгашат:

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

Муну эстен чыгарбоо керек FileName бул ыкманы колдонгондуктан, файлдын жолун камтыйт GetFirstGraph.

Аткаруу натыйжасы

CxQL ичинде атайын өзгөрмө бар result, бул жазуу жүзүндөгү эрежени аткаруунун натыйжасын берет. Ал дароо инициализацияланат жана сиз ага ортодогу жыйынтыктарды жазып, иштеп жатканыңызда аларды өзгөртүп, тактай аласыз. Бирок, эреже ичинде бул өзгөрмө же функцияга дайындоо жок болсо return— аткаруунун натыйжасы дайыма нөл болот.

Төмөнкү суроо аткаруунун натыйжасында бизге эч нерсе кайтарбайт жана дайыма бош болот:

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

Бирок, аткаруунун натыйжасын сыйкырдуу өзгөрмө натыйжага дайындап, биз бул чалуу бизге эмнени кайтарарын көрөбүз:

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

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

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

Башка эрежелердин натыйжаларын колдонуу

Checkmarx эрежелерин кадимки программалоо тилиндеги функцияларга окшош деп атоого болот. Эрежени жазууда сиз башка суроолордун натыйжаларын колдонсоңуз болот. Мисалы, коддогу бардык ыкмаларды издөөнүн кереги жок, жөн гана каалаган эрежени чакырыңыз:

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

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

Бул ыкма кодду кыскартууга жана эрежени аткаруу убактысын олуттуу кыскартууга мүмкүндүк берет.

Маселелерди чечүү

Каттоо

Курал менен иштөөдө, кээде каалаган суроону дароо жазуу мүмкүн болбой калат жана ар кандай варианттарды сынап көрүп, эксперимент жүргүзүүгө туура келет. Мындай учурда, курал төмөнкүдөй деп аталган журналдарды камсыз кылат:

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

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

Бирок бул ыкма киргизүү катары гана кабыл алаарын эстен чыгарбоо керек линия, ошондуктан биринчи операциянын натыйжасында табылган элементтердин толук тизмесин көрсөтүү мүмкүн болбой калат. Мүчүлүштүктөрдү оңдоо үчүн колдонулган экинчи вариант - мезгил-мезгили менен сыйкырдуу өзгөрмөгө дайындоо result суроонун жыйынтыгы жана эмне болорун көр. Бул ыкма абдан ыңгайлуу эмес; сиз коддо эч кандай жокко чыгаруулар же операциялар жок экенине ынанышыңыз керек. result же жөн гана төмөнкү кодду комментарий. Же мага окшоп, даяр эрежеден бир нече мындай чалууларды алып салууну унутуп, эмне үчүн эч нерсе иштебейт деп таң кала аласыз.

Ыңгайлуу жолу - бул ыкманы чакыруу return керектүү параметр менен. Бул учурда, эреженин аткарылышы аяктайт жана биз жазгандардын натыйжасында эмне болгонун көрө алабыз:

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

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

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

Кирүү маселеси

CxAudit куралына (эрежелерди жазуу үчүн колдонулат) кире албай калган жагдайлар бар. Мунун көптөгөн себептери болушу мүмкүн, анын ичинде каталар, күтүлбөгөн Windows жаңыртуулары, BSOD жана башка күтүлбөгөн жагдайлар, алар биздин көзөмөлүбүздөн тышкаркы. Бул учурда, кээде маалымат базасында бүтпөгөн сеанс болот, бул сизге кайра кирүүгө жол бербейт. Аны оңдоо үчүн сиз бир нече сурамдарды аткарышыңыз керек:

8.6 чейин Checkmarx үчүн:

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

8.6дан кийин Checkmarx үчүн:

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

Жазуу эрежелери

Эми биз эң кызыктуу бөлүгүнө өтөбүз. CxQLде эрежелерди жаза баштаганда, сизге көбүнчө документация эмес, кээ бир көйгөйлөрдү чечүүнүн жандуу мисалдары жана сурамдардын жалпысынан кантип иштөө процессин сүрөттөп берүү жетишпейт.

Мен суроо тилине кирип баштагандар үчүн жашоону бир аз жеңилдетүүгө аракет кылам жана белгилүү бир көйгөйлөрдү чечүү үчүн Ыңгайлаштырылган сурамдарды колдонуунун бир нече мисалдарын келтирем. Алардын айрымдары кыйла жалпы жана сиздин компанияңызда дээрлик эч кандай өзгөрүүсүз колдонулушу мүмкүн, башкалары конкреттүү, бирок алар сиздин тиркемелериңиздин өзгөчөлүктөрүнө ылайык кодду өзгөртүү аркылуу да колдонсо болот.

Ошентип, бул жерде биз көп жолуккан көйгөйлөр бар:

Тапшырма: Эрежени аткаруунун жыйынтыгында бир нече Агымдар бар жана алардын бири экинчисинин уясы, алардын бирин калтырышыңыз керек.

чечим: Чынында эле, кээде Checkmarx бири-бирине дал келген жана башкалардын кыскартылган версиясы болушу мүмкүн болгон бир нече маалымат агымын көрсөтөт. Мындай учурлар үчүн атайын ыкма бар ReduceFlow. Параметрге жараша ал эң кыска же эң узун агымды тандайт:

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

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

Тапшырма: Курал жооп берген купуя маалыматтардын тизмесин кеңейтиңиз

чечим: Checkmarx негизги эрежелери бар, анын натыйжалары башка көптөгөн суроолор тарабынан колдонулат. Бул эрежелердин айрымдарын колдонмоңузга тиешелүү маалыматтар менен толуктоо менен сиз скандоочу натыйжаларды дароо жакшыртсаңыз болот. Төмөндө сиз баштоо үчүн үлгү эреже болуп саналат:

Жалпы_купуялык_бузулган_тизме

Биздин колдонмодо купуя маалыматты сактоо үчүн колдонулган бир нече өзгөрмөлөрдү кошолу:

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

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

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

Тапшырма: Сырсөздөр менен өзгөрмөлөрдүн тизмесин кеңейтиңиз

чечим: Мен дароо коддогу сырсөздөрдү аныктоонун негизги эрежесине көңүл бурууну жана ага сиздин компанияңызда кеңири колдонулган өзгөрмөлөрдүн аталыштарынын тизмесин кошууну сунуштайт элем.

Сырсөз_купуялык_бузулган_тизме

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

Тапшырма: Checkmarx тарабынан колдоого алынбаган колдонулган алкактарды кошуңуз

чечим: Checkmarx'теги бардык сурамдар тил боюнча бөлүнөт, андыктан ар бир тил үчүн эрежелерди кошушуңуз керек. Төмөндө мындай эрежелердин кээ бир мисалдары келтирилген.

Эгерде китепканалар стандарттык функцияларды толуктап же алмаштырса, аларды негизги эрежеге оңой эле кошууга болот. Ошондо аны колдонгон ар бир адам дароо жаңы киришүүлөр жөнүндө биле алышат. Мисал катары, Androidге кирүү үчүн китепканалар Timber жана Loggi болуп саналат. Негизги пакетте тутумдук эмес чалууларды аныктоо эрежелери жок, андыктан журналга сырсөз же сеанс идентификатору кирсе, биз бул тууралуу билбей калабыз. Келгиле, Checkmarx эрежелерине мындай ыкмалардын аныктамаларын кошууга аракет кылалы.

Кирүү үчүн Timber китепканасын колдонгон сыноо кодунун мисалы:

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

Бул жерде Checkmarx сурамынын мисалы келтирилген, ал сизге Тибер ыкмаларын чакыруунун аныктамасын тиркемеден маалыматтар үчүн чыгуу чекити катары кошууга мүмкүндүк берет:

FindAndroidOutputs

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

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

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

Ошондой эле сиз кошуна эрежеге кошсоңуз болот, бирок бул Android-ге кирүүгө түздөн-түз тиешелүү:

FindAndroidLog_Outputs

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

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

Ошондой эле, эгерде Android колдонмолору колдонулса WorkManager асинхрондук иш үчүн, тапшырмадан маалыматтарды алуу ыкмасын кошуу менен бул тууралуу Checkmarxка кошумча маалымат берүү жакшы идея. getInputData:

FindAndroidRead

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

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

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

Тапшырма: iOS долбоорлору үчүн plist ичинде купуя дайындар изделүүдө

чечим: iOS көбүнчө ар кандай өзгөрмөлөрдү жана маанилерди сактоо үчүн .plist кеңейтүүсү менен атайын файлдарды колдонот. Бул файлдарда сырсөздөрдү, энбелгилерди, ачкычтарды жана башка купуя маалыматтарды сактоо сунушталбайт, анткени аларды түзмөктөн эч кандай көйгөйсүз чыгарууга болот.

Plist файлдарында көзгө көрүнбөгөн өзгөчөлүктөр бар, бирок Checkmarx үчүн маанилүү. Келгиле, бизге керектүү маалыматтарды издей турган жана сырсөздөр же токендер бир жерде айтылган болсо, айтып бере турган эреже жазалы.

Backend кызматы менен байланыш үчүн белгини камтыган мындай файлдын мисалы:

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

Жана жазууда эске алынышы керек болгон бир нече нюанстар бар Checkmarx эрежеси:

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

Тапшырма: XMLде маалымат табуу

чечим: Checkmarx XML менен иштөө жана баалуулуктарды, тегдерди, атрибуттарды жана башкаларды издөө үчүн абдан ыңгайлуу функцияларга ээ. Бирок, тилекке каршы, документацияда ката кетти, анын айынан бир дагы мисал иштебейт. Документтин акыркы версиясында бул кемчилик жоюлганына карабастан, документтердин мурунку версияларын колдонсоңуз, сак болуңуз.

Бул жерде документациядан туура эмес мисал келтирилген:

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

Аткаруу аракетинин натыйжасында биз ката алабыз All андай ыкма жок... Жана бул туура, анткени XML менен иштөө үчүн функцияларды колдонуу үчүн атайын, өзүнчө объект мейкиндиги бар - cxXPath. Androidде HTTP трафигин колдонууга мүмкүндүк берген жөндөөнү табуу үчүн туура суроо мына ушундай көрүнөт:

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

Келгиле, аны бир аз майда-чүйдөсүнө чейин карап көрөлү, анткени бардык функциялардын синтаксиси окшош болгондуктан, бирөөнү тапкандан кийин, сизге керектүүсүн тандоо керек. Ошентип, ырааттуу түрдө параметрлер боюнча:

  • "*.xml"— изделе турган файлдардын маскасы

  • 8 — эреже колдонулган тилдин идентификатору

  • "cleartextTrafficPermitted"— xmlдеги атрибут аты

  • "true" — бул атрибуттун мааниси

  • false — издөөдө туруктуу сөз айкашын колдонуу

  • true — издөө регистрге көңүл бурбай, башкача айтканда, регистрге маани бербестен жүргүзүлөөрүн билдирет

Мисал катары, биз HTTP протоколу аркылуу сервер менен байланышууга мүмкүндүк берген Android тармагында коопсуздук көз карашынан алганда туура эмес тармактык байланыш орнотууларын аныктаган эрежени колдондук. Атрибутту камтыган жөндөөнүн мисалы cleartextTrafficPermitted мааниси менен 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>

Тапшырма: Натыйжаларды файлдын аты/жолу менен чектеңиз

чечим: Android үчүн мобилдик тиркемени иштеп чыгууга байланышкан ири долбоорлордун биринде биз бүдөмүк орнотууну аныктаган эреженин туура эмес позитивдерине туш болдук. Чындыгында, кутудан тышкары эреже файлда издейт build.gradle колдонмонун релиз версиясы үчүн бүдөмүк эрежелерди колдонуу үчүн жооптуу жөндөө.

Бирок чоң долбоорлордо кээде бала файлдары болот build.gradle, долбоорго кирген китепканаларга тиешелүү. Өзгөчөлүгү, эгерде бул файлдар бүдөмүктөөнүн зарылдыгын көрсөтпөсө дагы, компиляция учурунда ата-энелик ассамблея файлынын орнотуулары колдонулат.

Ошентип, милдет китепканаларга таандык болгон бала файлдардагы триггерлерди кесип салуу. Аларды линиянын болушу менен аныктоого болот apply 'com.android.library'.

Файлдан мисал код build.gradle, бул бүдөмүк кылуунун зарылдыгын аныктайт:

apply plugin: 'com.android.application'

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

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

dependencies {
  ...
}

Мисал файл build.gradle бул жөндөө жок долбоорго киргизилген китепкана үчүн:

apply plugin: 'android-library'

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

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

Жана 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);
		}
	}
}

Бул ыкма абдан универсалдуу жана Android тиркемелери үчүн гана эмес, натыйжанын белгилүү бир файлга таандык экендигин аныктоо керек болгон башка учурларда да пайдалуу болушу мүмкүн.

Тапшырма: Синтаксис толук колдоого алынбаса, үчүнчү тараптын китепканасын колдоону кошуңуз

чечим: Код жазуу процессинде колдонулган ар кандай алкактардын саны диаграммалардан тышкары. Албетте, Checkmarx алардын бар экенин дайыма эле биле бербейт жана биздин милдетибиз аны белгилүү бир методдор ушул алкакка тиешелүү экенин түшүнүүгө үйрөтүү. Кээде бул фреймворктар өтө кеңири таралган функциялардын аталыштарын колдонгону жана белгилүү бир китепканага белгилүү бир чакырыктын байланышын так аныктоо мүмкүн эместиги менен татаалдашат.

Кыйынчылык - мындай китепканалардын синтаксиси дайыма эле туура тааныла бербейт жана көп сандагы жалган позитивдерди албаш үчүн эксперимент жүргүзүү керек. Сканирлөөнүн тактыгын жакшыртуу жана көйгөйдү чечүү үчүн бир нече варианттар бар:

  • Биринчи вариант, биз китепкана белгилүү бир долбоордо колдонуларын жана эрежени команда деңгээлинде колдоно аларын так билебиз. Бирок, эгерде команда башка ыкманы колдонууну чечсе же функциялардын аталыштары бири-бирине дал келген бир нече китепкананы колдонсо, биз көптөгөн жалган позитивдердин анча жагымдуу эмес сүрөтүн ала алабыз.

  • Экинчи вариант - китепкана ачык импорттолгон файлдарды издөө. Бул ыкма менен биз керектүү китепкана дал ушул файлда колдонулганына шектенбесек болот.

  • Ал эми үчүнчү вариант - жогорудагы эки ыкманы чогуу колдонуу.

Мисал катары тар чөйрөдө белгилүү китепкананы карайлы жаркыраган Scala программалоо тили үчүн, атап айтканда, функция Литералдык маанилерди кошуу. Жалпысынан, SQL сурамына параметрлерди өткөрүү үчүн операторду колдонушуңуз керек $, ал алдын ала түзүлгөн SQL сурамына маалыматтарды алмаштырат. Башкача айтканда, бул Javaдагы Даярдалган билдирүүнүн түз аналогу. Бирок, эгер сизге SQL суроосун динамикалык түрдө куруу керек болсо, мисалы, таблица атын өткөрүү керек болсо, операторду колдоно аласыз. #$, ал маалыматтарды түздөн-түз суроого алмаштырат (дээрлик сапты бириктирүү сыяктуу).

Мисал код:

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

Checkmarx азырынча Splicing Literal Values ​​колдонулушун кантип аныктоону билбейт жана операторлорду өткөрүп жиберет #$, ошондуктан келгиле, аны потенциалдуу SQL инъекцияларын аныктоону жана коддогу туура жерлерди бөлүп көрсөтүүнү үйрөтүүгө аракет кылалы:

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

Тапшырма: Ачык булак китепканаларында колдонулган аялуу функцияларды издеңиз

чечим: Көптөгөн компаниялар иштелип чыккан тиркемелерде китепканалардын аялуу версияларын колдонууну аныктоо үчүн Open-Source мониторинг куралдарын (OSA практикасы) колдонушат. Кээде мындай китепкананы коопсуз версияга жаңыртуу мүмкүн эмес. Кээ бир учурларда функционалдык чектөөлөр бар, башкаларында эч кандай коопсуз версия жок. Бул учурда, SAST жана OSA практикаларынын айкалышы коддо аялуулукту пайдаланууга алып келген функциялар колдонулбагандыгын аныктоого жардам берет.

Бирок, кээде, айрыкча, JavaScript карап жатканда, бул толугу менен маанилүү иш эмес болушу мүмкүн. Төмөндө чечим, балким, идеалдуу эмес, бирок, компоненттин алсыздыктарын мисалга алуу менен иштейт. lodash ыкмаларында template и *set.

JS файлындагы сыналышы мүмкүн болгон аялуу коддун мисалдары:

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

Жана түз 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>

Биз бардык аялуу ыкмаларыбызды издеп жатабыз, алар аялуу жерлеринде тизмеленген:

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

Тапшырма: Колдонмого кыстарылган сертификаттарды издөө

чечим: Ар кандай серверлерге кирүү же SSL-Pinningди текшерүү үчүн тиркемелер, өзгөчө мобилдик колдонмолор үчүн сертификаттарды же ачкычтарды колдонуу сейрек эмес. Коопсуздук көз карашынан алганда, мындай нерселерди коддо сактоо эң жакшы тажрыйба эмес. Келгиле, репозиторийден окшош файлдарды издей турган эреже жазууга аракет кылалы:

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

Тапшырма: Колдонмодо бузулган токендерди табуу

чечим: Көбүнчө коддогу бузулган токендерди же башка маанилүү маалыматты жокко чыгаруу керек. Албетте, аларды баштапкы коддун ичинде сактоо жакшы идея эмес, бирок жагдайлар ар кандай. CxQL сурамдарына рахмат, бул сыяктуу нерселерди табуу абдан оңой:

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

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

жыйынтыктоо

Бул макала Checkmarx куралы менен таанышууну баштагандар үчүн пайдалуу болот деп ишенем. Балким, көптөн бери өз эрежелерин жазып жүргөндөр да бул колдонмодон пайдалуу нерсе табат.

Тилекке каршы, учурда Checkmarx эрежелерин иштеп чыгууда жаңы идеяларды чогулта турган ресурс жетишсиз. Ошон үчүн жараттык Github боюнча репозиторий, анда биз CxQLди колдонгон ар бир адам андан пайдалуу нерсени таап, ошондой эле өз иштерин коомчулук менен бөлүшүү мүмкүнчүлүгүнө ээ болушу үчүн биздин ишибизди жарыялайбыз. Репозиторий мазмунду толтуруу жана структуралаштыруу процессинде, андыктан салым кошуучуларды кабыл алабыз!

Конул бурганын учун рахмат!

Source: www.habr.com

Комментарий кошуу