Delirmeden Checkmarx için kurallar nasıl yazılır?

Ey Habr!

Şirketimiz, çalışmalarımızda sıklıkla çeşitli statik kod analiz araçlarıyla (SAST) ilgilenmektedir. Kutunun dışında hepsi ortalama çalışıyor. Elbette her şey projeye, projede kullanılan teknolojilere ve bu teknolojilerin analiz kuralları kapsamında ne kadar iyi kapsandığına bağlıdır. Bana göre, bir SAST aracını seçerken en önemli kriterlerden biri, onu uygulamalarınızın özelliklerine göre özelleştirebilme yeteneğidir, yani analiz kurallarını yazma ve değiştirme veya daha sık olarak adlandırıldığı gibi Özel Sorgular.

Delirmeden Checkmarx için kurallar nasıl yazılır?

Çoğunlukla çok ilginç ve güçlü bir kod analizörü olan Checkmarx'ı kullanırız. Bu yazımda bunun için analiz kuralları yazma deneyimimden bahsedeceğim.

içindekiler

Giriş

Başlangıç ​​olarak Checkmarx'ta sorgu yazmanın özellikleriyle ilgili birkaç Rusça makaleden birini tavsiye etmek istiyorum. Habré'de 2019'un sonunda şu başlıkla yayınlandı: "Merhaba Checkmarx!" Checkmarx SAST sorgusu nasıl yazılır ve harika güvenlik açıkları nasıl bulunur?.

Bazı test uygulamaları için CxQL'de (Checkmarx Sorgu Dili) ilk sorguların nasıl yazılacağını detaylı olarak inceliyor ve analiz kurallarının nasıl çalıştığına dair temel prensipleri gösteriyor.

Bazı kesişme noktaları hala mevcut olsa da, burada anlatılanları tekrarlamayacağım. Makalemde Checkmarx ile çalışırken karşılaştığım belirli sorunların çözümlerinin bir listesi olan bir tür "tarif koleksiyonu" derlemeye çalışacağım. Bu sorunların çoğu üzerinde kafa yormak zorunda kaldım. Bazen belgelerde yeterli bilgi yoktu, bazen de gerekenin nasıl yapılacağını anlamak bile zordu. Umarım deneyimlerim ve uykusuz gecelerim boşa gitmez ve bu "Özel Sorgu tarifleri koleksiyonu" size birkaç saat veya birkaç sinir hücresinden tasarruf sağlar. Öyleyse başlayalım!

Kurallar hakkında genel bilgi

Bundan sonra ne olacağını daha iyi anlamak için öncelikle birkaç temel kavrama ve kurallarla çalışma sürecine bakalım. Ve ayrıca dokümantasyon bu konuda hiçbir şey söylemediği veya yapıya çok yayılmış olduğu için bu pek uygun değil.

  1. Kurallar, başlangıçta seçilen ön ayara (bir dizi etkin kural) bağlı olarak tarama sırasında uygulanır. Sınırsız sayıda ön ayar oluşturabilirsiniz ve bunların tam olarak nasıl yapılandırılacağı, sürecinizin özelliklerine bağlıdır. Bunları dile göre gruplandırabilir veya her proje için ön ayarları seçebilirsiniz. Etkin kuralların sayısı taramanın hızını ve doğruluğunu etkiler.

    Delirmeden Checkmarx için kurallar nasıl yazılır?Checkmarx arayüzünde Ön Ayarı ayarlama

  2. Kurallar, CxAuditor adı verilen özel bir araçta düzenlenir. Bu, Checkmarx çalıştıran bir sunucuya bağlanan bir masaüstü uygulamasıdır. Bu aracın iki çalışma modu vardır: kuralları düzenlemek ve önceden gerçekleştirilmiş bir taramanın sonuçlarını analiz etmek.

    Delirmeden Checkmarx için kurallar nasıl yazılır?CxAudit arayüzü

  3. Checkmarx'taki kurallar dile göre bölünmüştür, yani her dilin kendi sorgu seti vardır. Dilden bağımsız olarak geçerli olan bazı genel kurallar da vardır, bunlar temel sorgulardır. Temel sorgular çoğunlukla diğer kuralların kullandığı bilgilerin aranmasını içerir.

    Delirmeden Checkmarx için kurallar nasıl yazılır?Kuralları dile göre bölme

  4. Kurallar "Yürütülebilir" ve "Yürütülemez"dir (Yürütüldü ve Yürütülmedi). Bana göre pek doğru bir isim değil ama olan bu. Sonuç olarak, "Yürütülebilir" kuralların yürütülmesinin sonucu, kullanıcı arayüzündeki tarama sonuçlarında görüntülenecektir ve "Yürütülemez" kurallara yalnızca sonuçlarını diğer isteklerde kullanmak için ihtiyaç duyulur (özünde, yalnızca bir işlev).

    Delirmeden Checkmarx için kurallar nasıl yazılır?Oluştururken kural tipini belirleme

  5. Yeni kurallar oluşturabilir veya mevcut kuralları tamamlayabilir/yeniden yazabilirsiniz. Bir kuralı yeniden yazmak için onu ağaçta bulmanız, sağ tıklayıp açılır menüden "Geçersiz Kıl" seçeneğini seçmeniz gerekir. Burada yeni kuralların başlangıçta ön ayarlara dahil edilmediğini ve aktif olmadığını hatırlamak önemlidir. Bunları kullanmaya başlamak için cihazdaki “Preset Manager” menüsünden bunları etkinleştirmeniz gerekir. Yeniden yazılan kurallar ayarları korunur; yani kural etkinse öyle kalacak ve hemen uygulanacaktır.

    Delirmeden Checkmarx için kurallar nasıl yazılır?Hazır Ayar Yöneticisi arayüzündeki yeni kural örneği

  6. Yürütme sırasında, neye bağlı olarak bir istek “ağacı” oluşturulur. Bilgi toplayan kurallar ilk önce, onu kullananlar ise ikinci olarak yürütülür. Yürütme sonucu önbelleğe alınır, dolayısıyla mevcut bir kuralın sonuçlarını kullanmak mümkünse bunu yapmak daha iyidir, bu tarama süresini azaltacaktır.

  7. Kurallar farklı düzeylerde uygulanabilir:

  • Tüm sistem için - herhangi bir projenin taranması için kullanılacaktır

  • Ekip düzeyinde (Takım) - yalnızca seçilen ekipteki projeleri taramak için kullanılacaktır.

  • Proje düzeyinde - Belirli bir projede uygulanacaktır

    Delirmeden Checkmarx için kurallar nasıl yazılır?Kuralın uygulanacağı düzeyin belirlenmesi

Yeni başlayanlar için “Sözlük”

Aklımda soru işaretleri bırakan birkaç şeyle başlayacağım ve ayrıca hayatı önemli ölçüde kolaylaştıracak bir dizi teknik de göstereceğim.

Listelerle işlemler

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

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

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

Bulunan tüm öğeler

Taranan dil içerisinde, Checkmarx'ın tanımladığı tüm öğelerin (dizeler, işlevler, sınıflar, yöntemler vb.) kesinlikle bir listesini alabilirsiniz. Bu, üzerinden erişilebilen bazı nesneler alanıdır. All. Yani, belirli bir ada sahip bir nesneyi aramak için searchMeörneğin, bulunan tüm nesneler arasında ada göre arama yapabilirsiniz:

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

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

Ancak, herhangi bir nedenle taramaya dahil edilmeyen başka bir dilde arama yapmanız gerekiyorsa (örneğin, bir Android projesinde mükemmel), nesne alanımızı bir değişken aracılığıyla genişletebilirsiniz:

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

Akış analizine yönelik işlevler

Bu işlevler birçok kuralda kullanılmaktadır ve burada ne anlama geldiklerini açıklayan kısa bir özet bulunmaktadır:

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

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

Dosya adını/yolunu alma

Bir sorgunun sonuçlarından elde edilebilecek çeşitli öznitelikler vardır (girdinin bulunduğu dosyanın adı, dize vb.), ancak belgeler bunların nasıl elde edileceğini ve kullanılacağını söylemez. Yani bunu yapabilmek için LinePragma özelliğine erişmeniz gerekiyor ve ihtiyacımız olan nesneler onun içinde yer alacak:

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

Şunu akılda tutmakta fayda var FileName yöntemi kullandığımız için aslında dosyanın yolunu içerir GetFirstGraph.

Yürütme sonucu

CxQL'in içinde özel bir değişken var result, yazılı kuralınızın yürütülmesinin sonucunu döndürür. Hemen başlatılır ve ara sonuçları buna yazabilir, çalışırken bunları değiştirebilir ve iyileştirebilirsiniz. Ancak kural içinde bu değişkene veya işleve herhangi bir atama yoksa return— yürütme sonucu her zaman sıfır olacaktır.

Aşağıdaki sorgu, yürütme sonucunda bize hiçbir şey döndürmeyecek ve her zaman boş olacaktır:

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

Ancak yürütme sonucunu sihirli değişken result'a atadıktan sonra, bu çağrının bize ne döndürdüğünü göreceğiz:

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

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

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

Diğer kuralların sonuçlarını kullanma

Checkmarx'taki kurallar normal bir programlama dilindeki işlevlere benzer şekilde çağrılabilir. Bir kural yazarken diğer sorguların sonuçlarını da kullanabilirsiniz. Örneğin, her seferinde koddaki tüm yöntem çağrılarını aramanıza gerek yoktur; yalnızca istenen kuralı çağırmanız yeterlidir:

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

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

Bu yaklaşım, kodu kısaltmanıza ve kural yürütme süresini önemli ölçüde azaltmanıza olanak tanır.

sorunlarını çözme

Kerestecilik

Araçla çalışırken bazen istenen sorguyu hemen yazmak mümkün olmayabilir ve farklı seçenekleri deneyerek denemeler yapmanız gerekir. Böyle bir durumda araç, aşağıdaki şekilde adlandırılan günlük kaydı sağlar:

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

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

Ancak bu yöntemin yalnızca girdi olarak kabul ettiğini hatırlamakta fayda var. sicimdolayısıyla ilk işlem sonucunda bulunan öğelerin tam listesini görüntülemek mümkün olmayacaktır. Hata ayıklama için kullanılan ikinci seçenek, zaman zaman sihirli bir değişkene atama yapmaktır. result sorgunun sonucunu ve ne olduğunu görün. Bu yaklaşım pek kullanışlı değil; bundan sonra kodda bununla ilgili herhangi bir geçersiz kılma veya işlem olmadığından emin olmanız gerekir. result veya aşağıdaki kodu yorum olarak bırakın. Veya benim gibi, hazır bir kuraldan bu tür birkaç çağrıyı kaldırmayı unutabilir ve neden hiçbir şeyin işe yaramadığını merak edebilirsiniz.

Daha uygun bir yol, yöntemi çağırmaktır. return Gerekli parametre ile. Bu durumda kuralın yürütülmesi sona erecek ve yazdıklarımız sonucunda ne olduğunu görebileceğiz:

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

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

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

Giriş sorunu

CxAudit aracına (kural yazmak için kullanılır) erişemediğiniz durumlar vardır. Bunun birçok nedeni olabilir; çökmeler, ani Windows güncellemeleri, BSOD ve kontrolümüz dışında olan diğer öngörülemeyen durumlar dahil. Bu durumda bazen veritabanında tamamlanmamış bir oturum olabilir ve bu da tekrar giriş yapmanızı engeller. Bunu düzeltmek için birkaç sorgu çalıştırmanız gerekir:

8.6 öncesi Checkmarx için:

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

8.6'dan sonraki Checkmarx için:

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

Kural yazma

Şimdi en ilginç kısma geliyoruz. CxQL'de kural yazmaya başladığınızda, genellikle eksik olduğunuz şey, belirli sorunları çözmeye ve genel olarak sorgu işlemi sürecini açıklamaya yönelik bazı canlı örnekler kadar dokümantasyon değildir.

Sorgu diline dalmaya başlayanlar için hayatı biraz daha kolaylaştırmaya çalışacağım ve belirli sorunları çözmek için Özel Sorguların kullanımına ilişkin birkaç örnek vereceğim. Bazıları oldukça geneldir ve şirketinizde pratik olarak hiçbir değişiklik yapmadan kullanılabilir, diğerleri ise daha spesifiktir ancak uygulamalarınızın özelliklerine uyacak şekilde kod değiştirilerek de kullanılabilirler.

İşte en sık karşılaştığımız sorunlar:

Sorun: Kuralın yürütülmesinin sonuçlarında birkaç Akış vardır ve bunlardan biri diğerinin iç içe geçmesidir, bunlardan birinden ayrılmalısınız.

Çözüm: Gerçekten de Checkmarx bazen birbiriyle örtüşen ve diğerlerinin kısaltılmış versiyonu olabilecek birkaç veri akışı gösterir. Bu gibi durumlar için özel bir yöntem var Akışı Azaltın. Parametreye bağlı olarak en kısa veya en uzun Akışı seçecektir:

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

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

Sorun: Aracın tepki verdiği hassas verilerin listesini genişletin

Çözüm: Checkmarx'ın sonuçları diğer birçok sorgu tarafından kullanılan temel kuralları vardır. Bu kurallardan bazılarını uygulamanıza özel verilerle destekleyerek tarama sonuçlarınızı anında iyileştirebilirsiniz. Aşağıda başlamanıza yardımcı olacak örnek bir kural verilmiştir:

Genel_gizlilik_ihlali_listesi

Hassas bilgileri depolamak için uygulamamızda kullanılan birkaç değişkeni ekleyelim:

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

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

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

Sorun: Değişkenlerin listesini şifrelerle genişletin

Çözüm: Koddaki şifreleri tanımlamanın temel kuralına hemen dikkat etmenizi ve buna şirketinizde yaygın olarak kullanılan değişken adlarının bir listesini eklemenizi tavsiye ederim.

Şifre_gizlilik_ihlal_listesi

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

Sorun: Checkmarx tarafından desteklenmeyen kullanılmış çerçeveleri ekleyin

Çözüm: Checkmarx'taki tüm sorgular dile göre bölünmüştür, dolayısıyla her dil için kural eklemeniz gerekir. Aşağıda bu tür kurallara bazı örnekler verilmiştir.

Standart işlevselliği tamamlayan veya değiştiren kitaplıklar kullanılırsa, bunlar kolaylıkla temel kurala eklenebilir. Daha sonra onu kullanan herkes yeni tanıtımlardan hemen haberdar olacak. Örnek olarak Android'de oturum açmak için kullanılan kütüphaneler Timber ve Loggi'dir. Temel pakette sistem dışı çağrıları tanımlamaya yönelik herhangi bir kural yoktur, dolayısıyla günlüğe bir parola veya oturum tanımlayıcı girerse bundan haberimiz olmaz. Bu tür yöntemlerin tanımlarını Checkmarx kurallarına eklemeye çalışalım.

Günlük kaydı için Timber kitaplığını kullanan test kodu örneği:

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

Uygulamadan gelen veriler için bir çıkış noktası olarak Timber yöntemlerinin çağrılması tanımını eklemenizi sağlayacak Checkmarx isteğine bir örnek:

AndroidÇıkışlarını Bul

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

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

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

Ayrıca komşu kurala da ekleyebilirsiniz, ancak bu doğrudan Android'de oturum açmayla ilgilidir:

FindAndroidLog_Outputs

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

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

Ayrıca Android uygulamaları kullanılıyorsa Çalışma Yöneticisi eşzamansız çalışma için, görevden veri almaya yönelik bir yöntem ekleyerek Checkmarx'ı bu konuda ek olarak bilgilendirmek iyi bir fikirdir getInputData:

Android'i BulOku

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

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

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

Sorun: İOS projeleri için pliste hassas verileri arama

Çözüm: iOS, çeşitli değişkenleri ve değerleri depolamak için sıklıkla .plist uzantılı özel dosyalar kullanır. Şifrelerin, belirteçlerin, anahtarların ve diğer hassas verilerin bu dosyalarda saklanması önerilmez çünkü bunlar cihazdan sorunsuz bir şekilde çıkarılabilir.

Plist dosyaları çıplak gözle görülemeyen ancak Checkmarx için önemli olan özelliklere sahiptir. İhtiyacımız olan verileri arayacak ve bir yerde şifrelerden veya jetonlardan bahsedilip bahsedilmediğini bize bildirecek bir kural yazalım.

Arka uç hizmetiyle iletişim için bir belirteç içeren böyle bir dosya örneği:

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

Ve yazarken dikkate alınması gereken birkaç nüansı olan Checkmarx için bir kural:

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

Sorun: XML'de bilgi bulma

Çözüm: Checkmarx, XML ile çalışmak ve değerleri, etiketleri, nitelikleri ve daha fazlasını aramak için çok kullanışlı işlevlere sahiptir. Ancak ne yazık ki dokümantasyonda tek bir örneğin bile çalışmamasına neden olan bir hata oluştu. Bu kusur, belgelerin en son sürümünde giderilmiş olmasına rağmen, belgelerin önceki sürümlerini kullanıyorsanız dikkatli olun.

İşte belgelerden yanlış bir örnek:

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

Yürütme girişimi sonucunda şu hatayı alacağız: All böyle bir yöntem yok... Ve bu doğrudur, çünkü XML ile çalışmak üzere işlevlerin kullanılması için özel, ayrı bir nesne alanı vardır - cxXPath. Android'de HTTP trafiğinin kullanımına izin veren bir ayar bulmak için doğru sorgu şu şekilde görünür:

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

Tüm işlevlerin sözdizimi benzer olduğundan, biraz daha ayrıntılı olarak bakalım, bir tanesini çözdükten sonra ihtiyacınız olanı seçmeniz yeterli. Yani, parametrelere göre sırayla:

  • "*.xml"— aranacak dosyaların maskesi

  • 8 — kuralın uygulandığı dilin kimliği

  • "cleartextTrafficPermitted"— xml'deki özellik adı

  • "true" — bu özelliğin değeri

  • false — arama yaparken normal ifadenin kullanılması

  • true — aramanın büyük/küçük harf dikkate alınmadan, yani büyük/küçük harfe duyarlı olmadan gerçekleştirileceği anlamına gelir

Örnek olarak, Android'de, HTTP protokolü aracılığıyla sunucuyla iletişime izin veren, güvenlik açısından hatalı ağ bağlantısı ayarlarını tanımlayan bir kural kullandık. Öznitelik içeren bir ayar örneği cleartextTrafficPermitted anlamı olan 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>

Sorun: Sonuçları dosya adına/yoluna göre sınırlayın

Çözüm: Android için mobil uygulama geliştirmeyle ilgili büyük projelerden birinde, gizleme ayarını belirleyen kuralın yanlış pozitifleriyle karşılaştık. Gerçek şu ki, kutunun dışındaki kural dosyada arama yapıyor build.gradle uygulamanın yayın sürümü için gizleme kurallarının uygulanmasından sorumlu bir ayar.

Ancak büyük projelerde bazen alt dosyalar da olabilir build.gradle, projeye dahil edilen kütüphanelere atıfta bulunur. Buradaki tuhaflık, bu dosyalar gizleme ihtiyacını belirtmese bile, ana derleme dosyasının ayarlarının derleme sırasında uygulanacağıdır.

Bu nedenle görev, kütüphanelere ait alt dosyalardaki tetikleyicileri kesmektir. Çizginin varlığıyla tanımlanabilirler apply 'com.android.library'.

Dosyadan örnek kod build.gradle, bu, şaşırtma ihtiyacını belirler:

apply plugin: 'com.android.application'

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

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

dependencies {
  ...
}

Örnek dosya build.gradle projede yer alan ve bu ayara sahip olmayan bir kütüphane için:

apply plugin: 'android-library'

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

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

Ve Checkmarx'ın kuralı:

ProGuardGizlemeKullanılmıyor

// Поиск метода 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);
		}
	}
}

Bu yaklaşım yalnızca Android uygulamaları için değil, aynı zamanda sonucun belirli bir dosyaya ait olup olmadığını belirlemeniz gereken diğer durumlar için de oldukça evrensel ve yararlı olabilir.

Sorun: Söz dizimi tam olarak desteklenmiyorsa üçüncü taraf kitaplığı için destek ekleyin

Çözüm: Kod yazma sürecinde kullanılan çeşitli çerçevelerin sayısı alışılmışın dışındadır. Elbette Checkmarx bunların varlığından her zaman haberdar olmayabilir ve bizim görevimiz ona belirli yöntemlerin özellikle bu çerçeveye ait olduğunu anlamasını öğretmektir. Bazen çerçevelerin çok yaygın olan işlev adlarını kullanması ve belirli bir çağrının belirli bir kitaplıkla ilişkisini kesin olarak belirlemenin imkansız olması nedeniyle bu durum karmaşık hale gelir.

Buradaki zorluk, bu tür kitaplıkların sözdiziminin her zaman doğru şekilde tanınmamasıdır ve çok sayıda yanlış pozitif sonuç elde etmekten kaçınmak için deneme yapmanız gerekir. Tarama doğruluğunu artırmak ve sorunu çözmek için birkaç seçenek vardır:

  • İlk seçenekte, kütüphanenin belirli bir projede kullanıldığından ve kuralı ekip düzeyinde uygulayabildiğinden eminiz. Ancak ekip farklı bir yaklaşım benimsemeye karar verirse veya işlev adlarının örtüştüğü birkaç kitaplık kullanırsa, çok sayıda yanlış pozitifin pek de hoş olmayan bir resmini elde edebiliriz.

  • İkinci seçenek, kitaplığın açıkça içe aktarıldığı dosyaları aramaktır. Bu yaklaşımla ihtiyacımız olan kütüphanenin tam olarak bu dosyada kullanıldığından emin olabiliriz.

  • Üçüncü seçenek ise yukarıdaki iki yaklaşımı birlikte kullanmaktır.

Örnek olarak dar çevrelerde iyi bilinen bir kütüphaneye bakalım. kaygan Scala programlama dili için, yani işlevsellik Değişmez Değerleri Ekleme. Genel olarak, parametreleri bir SQL sorgusuna aktarmak için operatörü kullanmanız gerekir. $, verileri önceden oluşturulmuş bir SQL sorgusuyla değiştirir. Yani aslında Java'daki Hazır Açıklamanın doğrudan bir benzeridir. Ancak dinamik olarak bir SQL sorgusu oluşturmanız gerekiyorsa, örneğin tablo adlarını iletmeniz gerekiyorsa operatörünü kullanabilirsiniz. #$, verileri doğrudan sorgunun yerine koyacaktır (neredeyse dize birleştirme gibi).

Örnek кода:

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

Checkmarx, Ekleme Değişmez Değerlerinin kullanımını nasıl tespit edeceğini henüz bilmiyor ve operatörleri atlıyor #$, o halde ona potansiyel SQL enjeksiyonlarını tanımlamayı ve koddaki doğru yerleri vurgulamayı öğretmeye çalışalım:

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

Sorun: Açık Kaynak kitaplıklarında kullanılan güvenlik açığı bulunan işlevleri arayın

Çözüm: Pek çok şirket, geliştirilen uygulamalarda kütüphanelerin savunmasız sürümlerinin kullanımını tespit etmek için Açık Kaynak izleme araçlarını (OSA uygulaması) kullanıyor. Bazen böyle bir kütüphaneyi güvenli bir versiyona güncellemek mümkün olmayabilir. Bazı durumlarda işlevsel sınırlamalar vardır, diğerlerinde ise güvenli bir sürüm yoktur. Bu durumda SAST ve OSA uygulamalarının bir kombinasyonu, güvenlik açığından yararlanılmasına yol açan işlevlerin kodda kullanılmadığının belirlenmesine yardımcı olacaktır.

Ancak bazen, özellikle de JavaScript söz konusu olduğunda bu tamamen önemsiz bir görev olmayabilir. Aşağıda, bileşendeki güvenlik açıkları örneğini kullanarak ideal olmasa da yine de işe yarayan bir çözüm bulunmaktadır. lodash yöntemlerde template и *set.

Bir JS dosyasındaki potansiyel olarak güvenlik açığı bulunan test kodlarına örnekler:

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

Ve doğrudan html'ye bağlanırken:

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

Güvenlik açıklarında listelenen tüm güvenlik açığı yöntemlerimizi arıyoruz:

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

Sorun: Uygulamaya katıştırılmış sertifikaları arama

Çözüm: Uygulamaların, özellikle de mobil uygulamaların, çeşitli sunuculara erişmek veya SSL Sabitlemeyi doğrulamak için sertifika veya anahtar kullanması alışılmadık bir durum değildir. Güvenlik açısından bakıldığında bu tür şeyleri kodda saklamak en iyi uygulama değildir. Depodaki benzer dosyaları arayacak bir kural yazmaya çalışalım:

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

Sorun: Uygulamada güvenliği ihlal edilmiş belirteçleri bulma

Çözüm: Güvenliği ihlal edilmiş belirteçleri veya kodda bulunan diğer önemli bilgileri iptal etmek genellikle gereklidir. Elbette bunları kaynak kodun içinde saklamak iyi bir fikir değildir ancak durumlar farklılık gösterir. CxQL sorguları sayesinde buna benzer şeyleri bulmak oldukça kolaydır:

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

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

Sonuç

Bu makalenin Checkmarx aracıyla tanışmaya başlayanlar için faydalı olacağını umuyorum. Belki uzun süredir kendi kurallarını yazanlar da bu kılavuzda faydalı bir şeyler bulacaktır.

Ne yazık ki şu anda Checkmarx kurallarının geliştirilmesi sırasında yeni fikirlerin toplanabileceği bir kaynak eksikliği var. Bu yüzden yarattık Github'daki depoCxQL kullanan herkesin içinde yararlı bir şeyler bulabilmesi ve aynı zamanda çalışmalarını toplulukla paylaşma fırsatına sahip olabilmesi için çalışmalarımızı yayınlayacağımız yer. Depo içeriği doldurma ve yapılandırma sürecinde olduğundan katkıda bulunanları bekliyoruz!

Teşekkürler!

Kaynak: habr.com

Yorum ekle