Bagaimana menulis aturan untuk Checkmarx dan tidak menjadi gila

Hei Habr!

Dalam pekerjaan kami, perusahaan kami sangat sering berurusan dengan berbagai alat analisis kode statis (SAST). Di luar kotak, semuanya bekerja rata-rata. Tentu saja, semuanya bergantung pada proyek dan teknologi yang digunakan di dalamnya, serta seberapa baik teknologi tersebut tercakup dalam aturan analisis. Menurut pendapat saya, salah satu kriteria terpenting ketika memilih alat SAST adalah kemampuan untuk menyesuaikannya dengan spesifikasi aplikasi Anda, yaitu menulis dan mengubah aturan analisis atau, lebih sering disebut, Kueri Khusus.

Bagaimana menulis aturan untuk Checkmarx dan tidak menjadi gila

Kami paling sering menggunakan Checkmarx - penganalisis kode yang sangat menarik dan kuat. Pada artikel ini saya akan berbicara tentang pengalaman saya menulis aturan analisis untuk itu.

daftar isi

Masuk

Untuk memulainya, saya ingin merekomendasikan salah satu dari sedikit artikel dalam bahasa Rusia tentang fitur menulis kueri untuk Checkmarx. Diterbitkan di Habré pada akhir tahun 2019 dengan judul: "Halo, Checkmarx!" Cara menulis kueri Checkmarx SAST dan menemukan kerentanan yang keren.

Ini membahas secara rinci cara menulis kueri pertama dalam CxQL (Checkmarx Query Language) untuk beberapa aplikasi pengujian dan menunjukkan prinsip dasar cara kerja aturan analisis.

Saya tidak akan mengulangi apa yang dijelaskan di dalamnya, meskipun beberapa persimpangan akan tetap ada. Dalam artikel saya, saya akan mencoba menyusun semacam “kumpulan resep”, daftar solusi untuk masalah spesifik yang saya temui selama bekerja dengan Checkmarx. Saya harus memutar otak mengatasi banyak masalah ini. Kadang-kadang informasi dalam dokumentasi tidak cukup, dan kadang-kadang bahkan sulit untuk memahami bagaimana melakukan apa yang diperlukan. Saya harap pengalaman dan malam tanpa tidur saya tidak sia-sia, dan “kumpulan resep Kueri Khusus” ini akan menghemat beberapa jam atau beberapa sel saraf. Jadi, mari kita mulai!

Informasi umum tentang peraturan

Pertama, mari kita lihat beberapa konsep dasar dan proses penggunaan aturan, untuk pemahaman yang lebih baik tentang apa yang akan terjadi selanjutnya. Dan juga karena dokumentasinya tidak menjelaskan apa pun tentang hal ini atau strukturnya sangat tersebar, sehingga sangat tidak nyaman.

  1. Aturan diterapkan selama pemindaian tergantung pada preset yang dipilih di awal (seperangkat aturan aktif). Anda dapat membuat preset dalam jumlah tidak terbatas, dan cara menyusunnya bergantung pada proses spesifik Anda. Anda dapat mengelompokkannya berdasarkan bahasa atau memilih preset untuk setiap proyek. Banyaknya aturan yang aktif mempengaruhi kecepatan dan keakuratan pemindaian.

    Bagaimana menulis aturan untuk Checkmarx dan tidak menjadi gilaMenyiapkan Preset di antarmuka Checkmarx

  2. Aturan diedit dalam alat khusus yang disebut CxAuditor. Ini adalah aplikasi desktop yang terhubung ke server yang menjalankan Checkmarx. Alat ini memiliki dua mode operasi: mengedit aturan dan menganalisis hasil pemindaian yang sudah dilakukan.

    Bagaimana menulis aturan untuk Checkmarx dan tidak menjadi gilaAntarmuka CxAudit

  3. Aturan di Checkmarx dibagi berdasarkan bahasa, yaitu setiap bahasa memiliki kumpulan kuerinya sendiri. Ada juga beberapa aturan umum yang berlaku apa pun bahasanya, inilah yang disebut kueri dasar. Umumnya, kueri dasar melibatkan pencarian informasi yang digunakan oleh aturan lain.

    Bagaimana menulis aturan untuk Checkmarx dan tidak menjadi gilaMembagi aturan berdasarkan bahasa

  4. Aturannya bersifat “Executable” dan “Non-Executable” (Dieksekusi dan Tidak Dieksekusi). Menurut saya, bukan nama yang tepat, tapi begitulah adanya. Intinya adalah bahwa hasil eksekusi aturan "Executable" akan ditampilkan dalam hasil pemindaian di UI, dan aturan "Non-Executable" hanya diperlukan untuk menggunakan hasilnya dalam permintaan lain (pada dasarnya, hanya sebuah fungsi).

    Bagaimana menulis aturan untuk Checkmarx dan tidak menjadi gilaMenentukan jenis aturan saat membuat

  5. Anda dapat membuat aturan baru atau melengkapi/menulis ulang aturan yang sudah ada. Untuk menulis ulang aturan, Anda perlu menemukannya di pohon, klik kanan dan pilih “Override” dari menu drop-down. Penting untuk diingat di sini bahwa aturan baru pada awalnya tidak disertakan dalam preset dan tidak aktif. Untuk mulai menggunakannya, Anda perlu mengaktifkannya di menu “Preset Manager” pada instrumen. Aturan yang ditulis ulang akan mempertahankan pengaturannya, artinya, jika aturan tersebut aktif, maka aturan tersebut akan tetap aktif dan akan segera diterapkan.

    Bagaimana menulis aturan untuk Checkmarx dan tidak menjadi gilaContoh aturan baru di antarmuka Preset Manager

  6. Selama eksekusi, "pohon" permintaan dibuat, yang bergantung pada apa. Aturan yang mengumpulkan informasi dijalankan terlebih dahulu, dan mereka yang menggunakannya kedua. Hasil eksekusinya di-cache, jadi jika bisa menggunakan hasil rule yang ada sebaiknya dilakukan saja, hal ini akan mempersingkat waktu scanning.

  7. Aturan dapat diterapkan pada tingkat yang berbeda:

  • Untuk keseluruhan sistem - akan digunakan untuk pemindaian proyek apa pun

  • Di tingkat tim (Team) - hanya akan digunakan untuk memindai proyek di tim yang dipilih.

  • Di tingkat proyek - Akan diterapkan dalam proyek tertentu

    Bagaimana menulis aturan untuk Checkmarx dan tidak menjadi gilaMenentukan tingkat penerapan aturan tersebut

"Kamus" untuk pemula

Dan saya akan mulai dengan beberapa hal yang membuat saya bertanya-tanya, dan saya juga akan menunjukkan sejumlah teknik yang akan menyederhanakan hidup secara signifikan.

Operasi dengan daftar

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

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

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

Semua item yang ditemukan

Dalam bahasa yang dipindai, Anda bisa mendapatkan daftar semua elemen yang telah diidentifikasi Checkmarx (string, fungsi, kelas, metode, dll.). Ini adalah beberapa ruang objek yang dapat diakses melaluinya All. Artinya, untuk mencari suatu benda dengan nama tertentu searchMe, Anda dapat mencari, misalnya, berdasarkan nama di semua objek yang ditemukan:

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

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

Namun, jika Anda perlu mencari dalam bahasa lain yang karena alasan tertentu tidak disertakan dalam pemindaian (misalnya, groovy di proyek Android), Anda dapat memperluas ruang objek melalui variabel:

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

Fungsi untuk analisis Aliran

Fungsi-fungsi ini digunakan dalam banyak aturan dan berikut ini sedikit contekan tentang maksudnya:

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

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

Mendapatkan nama/jalur file

Ada beberapa atribut yang dapat diperoleh dari hasil kueri (nama file tempat entri ditemukan, string, dll.), tetapi dokumentasi tidak menyebutkan cara memperoleh dan menggunakannya. Jadi, untuk melakukan ini, Anda perlu mengakses properti LinePragma dan objek yang kita perlukan akan ditempatkan di dalamnya:

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

Perlu diingat hal itu FileName sebenarnya berisi path ke file tersebut, karena kami menggunakan metode ini GetFirstGraph.

Hasil eksekusi

Ada variabel khusus di dalam CxQL result, yang mengembalikan hasil pelaksanaan aturan tertulis Anda. Ini segera diinisialisasi dan Anda dapat menulis hasil antara ke dalamnya, mengubah dan menyempurnakannya saat Anda bekerja. Namun, jika tidak ada penetapan ke variabel atau fungsi ini di dalam aturan return— hasil eksekusi akan selalu nol.

Kueri berikut tidak akan mengembalikan apa pun kepada kami sebagai hasil eksekusi dan akan selalu kosong:

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

Namun, dengan menetapkan hasil eksekusi ke hasil variabel ajaib, kita akan melihat apa yang dikembalikan oleh panggilan ini kepada kita:

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

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

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

Menggunakan hasil aturan lain

Aturan dalam Checkmarx bisa disebut analog dengan fungsi dalam bahasa pemrograman biasa. Saat menulis aturan, Anda mungkin menggunakan hasil kueri lainnya. Misalnya, tidak perlu mencari semua pemanggilan metode dalam kode setiap saat, cukup panggil aturan yang diinginkan:

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

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

Pendekatan ini memungkinkan Anda mempersingkat kode dan secara signifikan mengurangi waktu eksekusi aturan.

Pemecahan masalah

Penebangan

Saat bekerja dengan alat ini, terkadang tidak mungkin untuk segera menulis kueri yang diinginkan dan Anda harus bereksperimen, mencoba opsi yang berbeda. Untuk kasus seperti ini, alat ini menyediakan pencatatan log, yang disebut sebagai berikut:

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

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

Namun perlu diingat bahwa metode ini hanya menerima sebagai masukan rangkaian, jadi tidak mungkin menampilkan daftar lengkap elemen yang ditemukan sebagai hasil operasi pertama. Opsi kedua, yang digunakan untuk debugging, adalah menetapkan variabel ajaib dari waktu ke waktu result hasil kueri dan lihat apa yang terjadi. Pendekatan ini sangat tidak nyaman; Anda perlu memastikan bahwa tidak ada penggantian atau operasi dengan kode ini setelahnya result atau cukup beri komentar pada kode di bawah ini. Atau Anda bisa, seperti saya, lupa menghapus beberapa panggilan seperti itu dari aturan yang sudah jadi dan bertanya-tanya mengapa tidak ada yang berhasil.

Cara yang lebih mudah adalah dengan memanggil metode tersebut return dengan parameter yang diperlukan. Dalam hal ini, pelaksanaan aturan akan berakhir dan kita akan dapat melihat apa yang terjadi sebagai akibat dari apa yang kita tulis:

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

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

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

Masalah masuk

Ada situasi ketika Anda tidak dapat mengakses alat CxAudit (yang digunakan untuk menulis aturan). Ada banyak alasan untuk hal ini, termasuk crash, pembaruan Windows yang tiba-tiba, BSOD, dan situasi tak terduga lainnya yang berada di luar kendali kami. Dalam hal ini, terkadang ada sesi yang belum selesai di database, sehingga menghalangi Anda untuk login lagi. Untuk memperbaikinya, Anda perlu menjalankan beberapa pertanyaan:

Untuk Checkmarx sebelum 8.6:

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

Untuk Checkmarx setelah 8.6:

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

Aturan penulisan

Sekarang kita sampai pada bagian yang paling menarik. Saat Anda mulai menulis aturan di CxQL, yang sering Anda kekurangan bukanlah dokumentasi, melainkan beberapa contoh nyata dalam memecahkan masalah tertentu dan menjelaskan proses operasi kueri secara umum.

Saya akan mencoba membuat hidup sedikit lebih mudah bagi mereka yang mulai mendalami bahasa kueri dan memberikan beberapa contoh penggunaan Kueri Khusus untuk memecahkan masalah tertentu. Beberapa di antaranya cukup umum dan dapat digunakan di perusahaan Anda secara praktis tanpa perubahan, yang lain lebih spesifik, tetapi juga dapat digunakan dengan mengubah kode agar sesuai dengan spesifikasi aplikasi Anda.

Nah, berikut masalah yang paling sering kami temui:

Tugas: Ada beberapa Alur hasil eksekusi aturan dan salah satunya merupakan sarang dari yang lain, Anda harus meninggalkan salah satunya.

solusi: Memang benar, terkadang Checkmarx menampilkan beberapa aliran data yang mungkin tumpang tindih dan merupakan versi singkat dari aliran data lainnya. Ada metode khusus untuk kasus seperti ini Kurangi Aliran. Tergantung pada parameternya, ia akan memilih Aliran terpendek atau terpanjang:

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

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

Tugas: Perluas daftar data sensitif yang menjadi reaksi alat tersebut

solusi: Checkmarx memiliki aturan dasar, yang hasilnya digunakan oleh banyak kueri lainnya. Dengan melengkapi beberapa aturan ini dengan data khusus untuk aplikasi Anda, Anda dapat segera meningkatkan hasil pemindaian. Di bawah ini adalah contoh aturan untuk Anda mulai:

Daftar_pelanggaran_privasi_umum

Mari tambahkan beberapa variabel yang digunakan dalam aplikasi kita untuk menyimpan informasi sensitif:

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

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

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

Tugas: Perluas daftar variabel dengan kata sandi

solusi: Saya akan merekomendasikan segera memperhatikan aturan dasar untuk menentukan kata sandi dalam kode dan menambahkan daftar nama variabel yang biasa digunakan di perusahaan Anda.

Daftar_pelanggaran_privasi_kata sandi

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

Tugas: Tambahkan kerangka kerja bekas yang tidak didukung oleh Checkmarx

solusi: Semua kueri di Checkmarx dibagi berdasarkan bahasa, jadi Anda perlu menambahkan aturan untuk setiap bahasa. Di bawah ini adalah beberapa contoh aturan tersebut.

Jika perpustakaan digunakan untuk melengkapi atau menggantikan fungsionalitas standar, mereka dapat dengan mudah ditambahkan ke aturan dasar. Kemudian setiap orang yang menggunakannya akan segera mengetahui tentang perkenalan baru tersebut. Sebagai contoh, perpustakaan untuk login di Android adalah Timber dan Loggi. Dalam paket dasar, tidak ada aturan untuk mengidentifikasi panggilan non-sistem, jadi jika kata sandi atau pengidentifikasi sesi masuk ke log, kami tidak akan mengetahuinya. Mari kita coba menambahkan definisi metode tersebut ke aturan Checkmarx.

Contoh kode pengujian yang menggunakan pustaka Timber untuk logging:

package com.death.timberdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import timber.log.Timber;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Timber.e("Error Message");
        Timber.d("Debug Message");

        Timber.tag("Some Different tag").e("And error message");
    }
}

Dan berikut adalah contoh permintaan Checkmarx, yang memungkinkan Anda menambahkan definisi pemanggilan metode Timber sebagai titik keluar data dari aplikasi:

Temukan Output Android

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

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

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

Dan Anda juga dapat menambahkan aturan tetangga, tetapi aturan ini berhubungan langsung dengan login ke Android:

TemukanAndroidLog_Outputs

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

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

Juga, jika aplikasi Android digunakan Manajer Kerja untuk pekerjaan asinkron, ada baiknya untuk memberi tahu Checkmarx tambahan tentang hal ini dengan menambahkan metode untuk mendapatkan data dari tugas getInputData:

TemukanAndroidBaca

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

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

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

Tugas: Mencari data sensitif di plist untuk proyek iOS

solusi: iOS sering kali menggunakan file khusus dengan ekstensi .plist untuk menyimpan berbagai variabel dan nilai. Tidak disarankan menyimpan kata sandi, token, kunci, dan data sensitif lainnya dalam file ini, karena dapat diekstraksi dari perangkat tanpa masalah.

File plist memiliki fitur yang tidak terlihat dengan mata telanjang, tetapi penting bagi Checkmarx. Mari kita tulis aturan yang akan mencari data yang kita perlukan dan memberi tahu kita jika kata sandi atau token disebutkan di suatu tempat.

Contoh file tersebut, yang berisi token untuk komunikasi dengan layanan 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>

Dan aturan untuk Checkmarx, yang memiliki beberapa nuansa yang harus diperhatikan saat menulis:

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

Tugas: Menemukan informasi dalam XML

solusi: Checkmarx memiliki fungsi yang sangat nyaman untuk bekerja dengan XML dan mencari nilai, tag, atribut, dan lainnya. Namun sayangnya, terdapat kesalahan dalam dokumentasi sehingga tidak ada satu pun contoh yang berfungsi. Meskipun cacat ini telah diperbaiki pada dokumentasi versi terbaru, berhati-hatilah jika Anda menggunakan dokumen versi sebelumnya.

Berikut adalah contoh yang salah dari dokumentasi:

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

Sebagai hasil dari upaya eksekusi, kami akan menerima kesalahan itu All tidak ada metode seperti itu... Dan ini benar, karena ada ruang objek khusus dan terpisah untuk menggunakan fungsi untuk bekerja dengan XML - cxXPath. Ini adalah tampilan kueri yang benar untuk menemukan setelan di Android yang mengizinkan penggunaan lalu lintas HTTP:

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

Mari kita lihat lebih detail, karena sintaks untuk semua fungsi serupa, setelah Anda mengetahuinya, Anda tinggal memilih salah satu yang Anda perlukan. Jadi, secara berurutan sesuai dengan parameternya:

  • "*.xml"— topeng file yang akan dicari

  • 8 — id bahasa yang menerapkan aturan tersebut

  • "cleartextTrafficPermitted"— nama atribut dalam xml

  • "true" — nilai atribut ini

  • false — penggunaan ekspresi reguler saat mencari

  • true — berarti pencarian akan dilakukan dengan mengabaikan huruf besar-kecil, yaitu tidak peka huruf besar-kecil

Sebagai contoh, kami menggunakan aturan yang mengidentifikasi salah, dari sudut pandang keamanan, pengaturan koneksi jaringan di Android yang memungkinkan komunikasi dengan server melalui protokol HTTP. Contoh setting yang berisi atribut cleartextTrafficPermitted dengan makna 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>

Tugas: Batasi hasil berdasarkan nama file/jalur

solusi: Dalam salah satu proyek besar yang terkait dengan pengembangan aplikasi seluler untuk Android, kami menemukan kesalahan positif dari aturan yang menentukan pengaturan kebingungan. Faktanya adalah bahwa aturan di luar kotak mencari file build.gradle pengaturan yang bertanggung jawab untuk menerapkan aturan kebingungan untuk versi rilis aplikasi.

Namun dalam proyek besar terkadang terdapat file anak build.gradle, yang mengacu pada perpustakaan yang disertakan dalam proyek. Keunikannya adalah meskipun file-file ini tidak menunjukkan perlunya kebingungan, pengaturan file perakitan induk akan diterapkan selama kompilasi.

Jadi, tugasnya adalah memotong pemicu di file anak milik perpustakaan. Mereka dapat diidentifikasi dengan adanya garis apply 'com.android.library'.

Contoh kode dari file build.gradle, yang menentukan perlunya kebingungan:

apply plugin: 'com.android.application'

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

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

dependencies {
  ...
}

Contoh file build.gradle untuk perpustakaan yang disertakan dalam proyek yang tidak memiliki pengaturan ini:

apply plugin: 'android-library'

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

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

Dan aturan untuk 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);
		}
	}
}

Pendekatan ini bisa sangat universal dan berguna tidak hanya untuk aplikasi Android, tetapi juga untuk kasus lain ketika Anda perlu menentukan apakah suatu hasil milik file tertentu.

Tugas: Tambahkan dukungan untuk perpustakaan pihak ketiga jika sintaksisnya tidak didukung sepenuhnya

solusi: Jumlah berbagai kerangka kerja yang digunakan dalam proses penulisan kode sungguh luar biasa. Tentu saja, Checkmarx tidak selalu mengetahui keberadaannya, dan tugas kita adalah mengajarkannya untuk memahami bahwa metode tertentu secara khusus termasuk dalam kerangka ini. Terkadang hal ini diperumit oleh fakta bahwa kerangka kerja menggunakan nama fungsi yang sangat umum dan tidak mungkin untuk secara jelas menentukan hubungan panggilan tertentu ke perpustakaan tertentu.

Kesulitannya adalah sintaks perpustakaan tersebut tidak selalu dikenali dengan benar dan Anda harus bereksperimen untuk menghindari banyak kesalahan positif. Ada beberapa opsi untuk meningkatkan akurasi pemindaian dan mengatasi masalah:

  • Opsi pertama, kita tahu pasti bahwa perpustakaan digunakan dalam proyek tertentu dan dapat menerapkan aturan di tingkat tim. Namun jika tim memutuskan untuk mengambil pendekatan yang berbeda atau menggunakan beberapa perpustakaan yang nama fungsinya tumpang tindih, kita bisa mendapatkan gambaran yang tidak terlalu menyenangkan tentang banyak kesalahan positif.

  • Opsi kedua adalah mencari file yang perpustakaannya jelas diimpor. Dengan pendekatan ini, kita dapat yakin bahwa perpustakaan yang kita perlukan benar-benar digunakan dalam file ini.

  • Dan opsi ketiga adalah menggunakan kedua pendekatan di atas secara bersamaan.

Sebagai contoh, mari kita lihat sebuah perpustakaan yang terkenal di kalangan sempit licin untuk bahasa pemrograman Scala yaitu fungsionalitasnya Menyatukan Nilai-Nilai Literal. Secara umum, untuk meneruskan parameter ke kueri SQL, Anda harus menggunakan operator $, yang menggantikan data ke dalam kueri SQL yang telah dibentuk sebelumnya. Faktanya, ini adalah analogi langsung dari Pernyataan yang Disiapkan di Java. Namun, jika Anda perlu membuat kueri SQL secara dinamis, misalnya, jika Anda perlu meneruskan nama tabel, Anda bisa menggunakan operator #$, yang akan langsung mengganti data ke dalam kueri (hampir seperti penggabungan string).

ер ода:

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

Checkmarx belum mengetahui cara mendeteksi penggunaan Splicing Literal Values ​​​​dan melewatkan operator #$, jadi mari kita coba mengajarkannya untuk mengidentifikasi potensi injeksi SQL dan menyorot tempat yang tepat dalam kode:

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

Tugas: Cari fungsi rentan yang digunakan di perpustakaan Sumber Terbuka

solusi: Banyak perusahaan menggunakan alat pemantauan Sumber Terbuka (praktik OSA) untuk mendeteksi penggunaan versi perpustakaan yang rentan dalam aplikasi yang dikembangkan. Terkadang tidak mungkin memperbarui perpustakaan tersebut ke versi yang aman. Dalam beberapa kasus terdapat keterbatasan fungsional, dalam kasus lain tidak ada versi aman sama sekali. Dalam hal ini, kombinasi praktik SAST dan OSA akan membantu menentukan bahwa fungsi yang mengarah pada eksploitasi kerentanan tidak digunakan dalam kode.

Namun terkadang, terutama ketika mempertimbangkan JavaScript, ini mungkin bukan tugas yang sepele. Di bawah ini adalah solusi, mungkin tidak ideal, namun tetap berfungsi, dengan menggunakan contoh kerentanan pada komponen lodash dalam metode template и *set.

Contoh pengujian kode yang berpotensi rentan dalam file 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!'

Dan saat menghubungkan langsung di 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>

Kami mencari semua metode rentan kami, yang tercantum dalam kerentanan:

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

Tugas: Mencari sertifikat yang tertanam dalam aplikasi

solusi: Tidak jarang aplikasi, terutama yang mobile, menggunakan sertifikat atau kunci untuk mengakses berbagai server atau memverifikasi SSL-Pinning. Dari sudut pandang keamanan, menyimpan hal-hal seperti itu dalam kode bukanlah praktik terbaik. Mari kita coba menulis aturan yang akan mencari file serupa di repositori:

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

Tugas: Menemukan token yang disusupi dalam aplikasi

solusi: Seringkali perlu untuk mencabut token yang disusupi atau informasi penting lainnya yang ada dalam kode. Tentu saja, menyimpannya di dalam kode sumber bukanlah ide yang baik, tetapi situasinya berbeda-beda. Berkat kueri CxQL, menemukan hal seperti ini cukup mudah:

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

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

Kesimpulan

Saya harap artikel ini bermanfaat bagi mereka yang baru mulai mengenal alat Checkmarx. Mungkin mereka yang sudah lama menulis aturannya sendiri juga akan menemukan sesuatu yang berguna dalam panduan ini.

Sayangnya, saat ini terdapat kekurangan sumber daya dimana ide-ide baru dapat diperoleh selama pengembangan aturan untuk Checkmarx. Itu sebabnya kami menciptakan repositori di Github, dimana kami akan memposting karya kami sehingga setiap orang yang menggunakan CxQL dapat menemukan sesuatu yang berguna di dalamnya, dan juga memiliki kesempatan untuk berbagi karyanya dengan komunitas. Repositori sedang dalam proses pengisian dan penataan konten, jadi kontributor dipersilakan!

Terima kasih!

Sumber: www.habr.com

Tambah komentar