Checkmarx-ийн дүрмийг галзууруулахгүйгээр хэрхэн бичих вэ

Хөөе Хабр!

Манай компани нь янз бүрийн статик кодын шинжилгээний хэрэгслүүдтэй (SAST) ихэвчлэн ажилладаг. Тэд бүгд дундажаар ажилладаг. Мэдээжийн хэрэг, энэ бүхэн төсөл, түүнд ашиглагдаж буй технологи, түүнчлэн эдгээр технологи нь шинжилгээний дүрэмд хэр нийцэж байгаагаас хамаарна. Миний бодлоор SAST хэрэглүүрийг сонгохдоо хамгийн чухал шалгууруудын нэг бол үүнийг өөрийн хэрэглээний онцлогт тохируулан өөрчлөх, тухайлбал дүн шинжилгээ хийх дүрмийг бичих, өөрчлөх эсвэл тэдгээрийг ихэвчлэн "Захиалгат асуулга" гэж нэрлэдэг.

Checkmarx-ийн дүрмийг галзууруулахгүйгээр хэрхэн бичих вэ

Бид ихэвчлэн Checkmarx ашигладаг - маш сонирхолтой, хүчирхэг код анализатор. Энэ нийтлэлд би дүн шинжилгээ хийх дүрмийг бичих туршлагаа ярих болно.

Агуулга

нэвтрэх

Эхлэхийн тулд би Checkmarx-д асуулга бичих онцлогуудын тухай орос хэл дээрх цөөн хэдэн нийтлэлийн нэгийг санал болгож байна. Энэ нь 2019 оны сүүлээр Habré сайтад дараах гарчигтайгаар нийтлэгдсэн. "Сайн уу, Чекмаркс!" Checkmarx SAST асуулга хэрхэн бичиж, гайхалтай эмзэг байдлыг олох вэ.

Энэ нь зарим тестийн хэрэглээний CxQL (Checkmarx Query Language) дээр эхний асуултуудыг хэрхэн бичихийг нарийвчлан судалж, шинжилгээний дүрэм хэрхэн ажилладаг үндсэн зарчмуудыг харуулдаг.

Зарим уулзварууд хэвээр байх боловч би үүн дээр дурдсан зүйлийг давтахгүй. Миний нийтлэлд би Checkmarx-тай ажиллах явцад тулгарч байсан тодорхой асуудлуудын шийдлүүдийн жагсаалтыг "жоруудын цуглуулга" эмхэтгэхийг хичээх болно. Би эдгээр олон асуудлын талаар тархиа шатаах хэрэгтэй болсон. Заримдаа баримт бичигт хангалттай мэдээлэл байдаггүй, заримдаа шаардлагатай зүйлийг хэрхэн яаж хийхийг ойлгоход хэцүү байдаг. Миний туршлага, нойргүй шөнүүд дэмий хоосон байх болно гэж найдаж байна, энэ "Захиалгат асуулгын жоруудын цуглуулга" нь танд хэдхэн цаг эсвэл хэдэн мэдрэлийн эсийг хэмнэх болно. За, эхэлцгээе!

Дүрмийн талаархи ерөнхий мэдээлэл

Дараа нь юу болохыг илүү сайн ойлгохын тулд эхлээд хэд хэдэн үндсэн ойлголт, дүрэмтэй ажиллах үйл явцыг харцгаая. Мөн баримт бичигт энэ талаар юу ч хэлээгүй эсвэл бүтцэд маш их тархсан байдаг тул энэ нь тийм ч тохиромжтой биш юм.

  1. Дүрмүүд нь эхлэх үед сонгосон урьдчилан тохируулсан (идэвхтэй дүрмийн багц) -аас хамааран сканнердах явцад хэрэгжинэ. Та хязгааргүй тооны урьдчилан тохируулгыг үүсгэж болох бөгөөд тэдгээрийг хэрхэн яаж зохион байгуулах нь таны үйл явцын онцлогоос хамаарна. Та тэдгээрийг хэлээр нь бүлэглэж эсвэл төсөл тус бүрийн урьдчилсан тохиргоог сонгож болно. Идэвхтэй дүрмийн тоо нь сканнердах хурд, нарийвчлалд нөлөөлдөг.

    Checkmarx-ийн дүрмийг галзууруулахгүйгээр хэрхэн бичих вэCheckmarx интерфэйс дэх Preset-ийг тохируулж байна

  2. Дүрмүүдийг CxAuditor хэмээх тусгай хэрэгслээр засдаг. Энэ нь Checkmarx ажиллуулж байгаа серверт холбогдох ширээний програм юм. Энэ хэрэгсэл нь ажиллах хоёр горимтой: дүрмийг засварлах, аль хэдийн хийсэн сканнерын үр дүнд дүн шинжилгээ хийх.

    Checkmarx-ийн дүрмийг галзууруулахгүйгээр хэрхэн бичих вэCxAudit интерфейс

  3. Checkmarx дээрх дүрмүүд нь хэлээр хуваагддаг, өөрөөр хэлбэл хэл бүр өөрийн гэсэн асуулгатай байдаг. Хэлнээс үл хамааран хэрэгжих зарим ерөнхий дүрмүүд байдаг бөгөөд эдгээр нь үндсэн асуулга гэж нэрлэгддэг. Ихэнх тохиолдолд үндсэн асуулга нь бусад дүрмийн ашигладаг мэдээллийг хайх явдал юм.

    Checkmarx-ийн дүрмийг галзууруулахгүйгээр хэрхэн бичих вэДүрмүүдийг хэлээр нь хуваах

  4. Дүрмүүд нь "Гүйцэтгэх боломжтой" ба "Гүйцэтгэх боломжгүй" (Гүйцэтгэсэн ба Гүйцэтгэгдээгүй). Миний бодлоор тийм ч зөв нэр биш, гэхдээ энэ нь тийм юм. Хамгийн гол нь "Гүйцэтгэх" дүрмийн гүйцэтгэлийн үр дүн нь UI дээрх сканнерын үр дүнд харагдах бөгөөд "Гүйцэтгэх боломжгүй" дүрмүүд нь зөвхөн үр дүнг бусад хүсэлтүүдэд ашиглахад л хэрэгтэй (үндсэндээ зүгээр л функц).

    Checkmarx-ийн дүрмийг галзууруулахгүйгээр хэрхэн бичих вэҮүсгэхдээ дүрмийн төрлийг тодорхойлох

  5. Та шинэ дүрмүүдийг бий болгох эсвэл одоо байгаа дүрмийг нэмж/дахин бичих боломжтой. Дүрмийг дахин бичихийн тулд та үүнийг модноос олж, хулганы баруун товчийг дараад унадаг цэснээс "Дараах" гэснийг сонгох хэрэгтэй. Шинэ дүрмүүд нь анхдагч тохиргоонд ороогүй бөгөөд идэвхтэй биш гэдгийг энд санах нь чухал юм. Тэдгээрийг ашиглаж эхлэхийн тулд та багажны "Preset Manager" цэсэнд идэвхжүүлэх хэрэгтэй. Дахин бичсэн дүрмүүд тохиргоогоо хадгалдаг, өөрөөр хэлбэл, хэрэв дүрэм идэвхтэй байсан бол энэ хэвээр байх бөгөөд тэр даруй хэрэгжинэ.

    Checkmarx-ийн дүрмийг галзууруулахгүйгээр хэрхэн бичих вэУрьдчилан тохируулсан менежерийн интерфейс дэх шинэ дүрмийн жишээ

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

Гэхдээ хэрэв та ямар нэг шалтгааны улмаас скан хийхэд ороогүй өөр хэлээр хайх шаардлагатай бол (жишээ нь, Андройд төсөл дэх groovy) хувьсагчаар дамжуулан бидний объектын орон зайг өргөжүүлж болно:

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-д дүрэм бичиж эхлэх үед танд ихэвчлэн баримт бичиг биш, зарим асуудлыг шийдвэрлэх амьд жишээнүүд болон асуулга хэрхэн ажилладагийг тайлбарлах нь ихэвчлэн дутагддаг.

Би асуулгын хэл рүү орж эхэлж буй хүмүүсийн амьдралыг бага зэрэг хөнгөвчлөхийг хичээж, тодорхой асуудлыг шийдвэрлэхийн тулд Custom Queries ашиглах хэд хэдэн жишээг өгөх болно. Тэдгээрийн зарим нь нэлээд ерөнхий бөгөөд бараг ямар ч өөрчлөлтгүйгээр танай компанид ашиглагдах боломжтой, бусад нь илүү тодорхой боловч програмынхаа онцлогт тохируулан кодыг өөрчлөх замаар ашиглаж болно.

Тиймээс, бидний хамгийн их тулгардаг асуудлууд энд байна:

Даалгавар: Дүрмийг хэрэгжүүлэх үр дүнд хэд хэдэн урсгал байдаг бөгөөд тэдгээрийн нэг нь нөгөөгийнхөө үүр болсон тул та тэдгээрийн аль нэгийг нь орхих ёстой.

шийдэл: Үнэн хэрэгтээ, заримдаа Checkmarx нь давхцаж болох хэд хэдэн өгөгдлийн урсгалыг харуулдаг бөгөөд бусдын товчилсон хувилбар байж болно. Ийм тохиолдлуудад зориулсан тусгай арга байдаг Урсгалыг багасгах. Параметрээс хамааран хамгийн богино эсвэл хамгийн урт урсгалыг сонгоно.

// Оставить только длинные 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 дүрмүүдэд ийм аргуудын тодорхойлолтыг нэмж оруулахыг хичээцгээе.

Мод бэлтгэхэд модон номын санг ашигладаг туршилтын кодын жишээ:

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-ийн хүсэлтийн жишээ энд байгаа бөгөөд энэ нь програмаас өгөгдөл гаргах цэг болгон Timber аргыг дуудах тодорхойлолтыг нэмэх боломжийг танд олгоно.

AndroidOutputs олох

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

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

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

Мөн та хөрш зэргэлдээ дүрэмд нэмж болно, гэхдээ энэ нь Android-д нэвтрэхтэй шууд холбоотой:

AndroidLog_Outputs олох

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

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

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

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

Үүнийг бага зэрэг нарийвчлан авч үзье, учир нь бүх функцүүдийн синтакс ижил төстэй тул та нэгийг нь олж мэдсэнийхээ дараа танд хэрэгтэй зүйлээ сонгох хэрэгтэй. Тиймээс, параметрийн дагуу дарааллаар:

  • "*.xml"— хайлт хийх файлуудын маск

  • 8 — дүрэм хэрэгжиж буй хэлний id

  • "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 програмуудад төдийгүй үр дүн нь тодорхой файлд хамаарах эсэхийг тодорхойлох шаардлагатай бусад тохиолдолд хэрэг болно.

Даалгавар: Синтакс бүрэн дэмжигдээгүй тохиолдолд гуравдагч талын номын сангийн дэмжлэгийг нэмнэ үү

шийдэл: Код бичих явцад ашигладаг янз бүрийн хүрээнүүдийн тоо графикаас гадуур байна. Мэдээжийн хэрэг, Checkmarks тэдний оршин тогтнох талаар тэр бүр мэддэггүй бөгөөд бидний даалгавар бол тодорхой аргууд нь энэ хүрээнд тусгайлан хамаарах гэдгийг ойлгуулах явдал юм. Заримдаа энэ нь фреймворкууд нь маш түгээмэл функцүүдийн нэрийг ашигладаг тул тодорхой дуудлагын тодорхой номын сантай харилцах харилцааг хоёрдмол утгагүйгээр тодорхойлох боломжгүй байдаг тул энэ нь төвөгтэй байдаг.

Хэцүү зүйл бол ийм номын сангийн синтакс нь үргэлж зөв танигддаггүй бөгөөд олон тооны хуурамч эерэг мэдээлэл авахгүйн тулд та туршилт хийх хэрэгтэй болдог. Сканнерын нарийвчлалыг сайжруулах, асуудлыг шийдэх хэд хэдэн сонголт байдаг:

  • Эхний сонголт бол номын сан нь тодорхой төсөлд ашиглагдаж, багийн түвшинд дүрмийг хэрэгжүүлэх боломжтой гэдгийг бид баттай мэддэг. Гэхдээ хэрэв баг өөр арга барил авахаар шийдсэн эсвэл функцүүдийн нэр давхцаж буй хэд хэдэн номын санг ашиглавал бид олон тооны худал эерэг дүр төрхийг олж авах боломжтой.

  • Хоёрдахь сонголт бол номын санг тодорхой импортолсон файлуудыг хайх явдал юм. Энэ аргын тусламжтайгаар бидэнд хэрэгтэй номын сан энэ файлд яг хэрэглэгдэж байгаа гэдэгт итгэлтэй байж болно.

  • Гурав дахь сонголт бол дээрх хоёр аргыг хамтад нь ашиглах явдал юм.

Жишээ болгож, явцуу хүрээнийхэнд танигдсан номын санг авч үзье нарийхан 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));
}

Даалгавар: Нээлттэй эхийн сангаас ашиглагдсан эмзэг функцүүдийг хайх

шийдэл: Олон компаниуд хөгжүүлсэн программуудад номын сангийн эмзэг хувилбаруудыг ашиглахыг илрүүлэхийн тулд Нээлттэй эх сурвалжийн хяналтын хэрэгслийг (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 ашигладаг хүн бүр үүнээс хэрэгтэй зүйл олж авахын тулд бид өөрсдийн ажлаа нийтлэх болно, мөн түүнчлэн олон нийттэй хийсэн ажлаа хуваалцах боломжтой болно. Хадгалах газар нь агуулгыг дүүргэх, бүтэцжүүлэх шатандаа байгаа тул хувь нэмэр оруулагчдыг урьж байна!

Таны анхаарлын төвд баярлалаа!

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх