- FlowTracker adalah Java agent yang melacak proses program Java membaca, memanipulasi, dan menulis data, serta menampilkan keterkaitan output dengan input, file, jaringan, atau konstanta kode asalnya
- Dengan mengamati program yang sedang berjalan, FlowTracker menampilkan I/O file dan jaringan, dan khususnya melacak hubungan korespondensi antara input dan output sehingga membantu memahami apa arti output program Java dan mengapa output itu dibuat
- Dalam demo Spring PetClinic, pengguna dapat menelusuri berbagai lapisan stack perangkat lunak, mulai dari header respons HTTP, template Thymeleaf, nilai database, hingga skrip penyisipan SQL
- Secara internal, FlowTracker menggabungkan instrumentasi bytecode saat JVM memuat kelas, hook metode JDK, analisis aliran data, pelacakan panggilan berbasis
ThreadLocal, danClassOriginTrackeruntuk mempertahankan pemetaan asal-usul yang berfokus pada string, karakter, dan byte - Statusnya saat ini lebih dekat ke proof-of-concept daripada siap produksi; meski bekerja baik pada beberapa program contoh, FlowTracker tidak cocok untuk semua program dan overhead yang besar membuat eksekusi jauh lebih lambat
Apa yang Dilacak FlowTracker
- FlowTracker adalah Java agent yang melacak bagaimana data dibaca, diteruskan, diubah, dan ditulis di dalam program Java
- Tidak berhenti pada menampilkan I/O file dan jaringan saja, FlowTracker juga menunjukkan output program berasal dari input apa
- Tujuannya adalah membantu memahami apa makna output program Java, dan mengapa program menuliskan output tersebut
- Saat ini proyek ini merupakan proof-of-concept untuk mengeksplorasi wawasan apa yang bisa diperoleh ketika perilaku program dilihat dari sudut pandang ini
Demo: Melacak Asal Respons HTTP di Spring PetClinic
- Demo FlowTracker PetClinic memungkinkan Anda melihat di browser bagaimana Spring PetClinic memproses request HTTP dan menghasilkan halaman HTML berdasarkan template serta data database
- Layar menampilkan respons HTTP yang dikirim PetClinic melalui jaringan; jika Anda mengklik sebagian isi body respons, tampilan di bagian bawah akan menunjukkan dari mana bagian tersebut berasal
- Di tree sebelah kiri, atau tombol kiri bawah pada perangkat mobile, Anda dapat memilih input/asalan atau output/sink yang telah dilacak
-
Lapisan Pemrosesan HTTP
- Jika mengklik
"HTTP/1.1"atau header HTTP, Anda dapat melihat bahwa bagian respons ini dibuat oleh kelas Apache Coyote dalam paketorg.apache.coyote - FlowTracker menampilkan kode mana yang menghasilkan output tertentu
- Jika mengklik
-
Lapisan Template Thymeleaf
- Jika mengklik nama tag HTML seperti
"html"atau"head", Anda dapat melihat bahwa bagian HTML tersebut berasal dari filelayout.html - Setelah mengklik
layout.html, lalu menekan tombol warna+di bagian bawah, semua bagian yang berasal dari file tersebut akan ditampilkan dengan warna yang sama - Jika menggulir ke bawah, Anda dapat melihat bahwa sebagian respons berasal dari file lain, yaitu
ownerDetails.html - Jika mengklik karakter
<atau>, Anda dapat melihat bahwa karakter tersebut ditulis oleh library template Thymeleaf
- Jika mengklik nama tag HTML seperti
-
Asal Nilai Database
- Tabel pada halaman HTML berisi informasi yang berasal dari database
- Jika mengklik
Georgepada tabel, pelacakan tidak hanya menunjukkan bahwa nilai itu berasal dari database, tetapi juga sampai ke skrip SQL yang pertama kali menyisipkan nilai ke database - Alasan pelacakan dalam demo ini bisa sampai ke skrip SQL adalah karena database in-memory digunakan, sehingga isi database tidak keluar dari JVM
Demo MySQL dan Independensi Framework
- Jika demo PetClinic yang sama dijalankan dengan database MySQL, nilai dilacak hingga titik koneksi database
- Dalam kasus ini, Anda dapat melihat kueri SQL yang sebelumnya dikirim untuk membuat nilai tersebut serta detail bagaimana driver MySQL JDBC berkomunikasi dengan database
- Demo FlowTracker PetClinic mysql juga menunjukkan bahwa FlowTracker mencegat konten yang telah didekripsi dan dikirim melalui koneksi SSL database
- Spring PetClinic hanyalah contoh; FlowTracker tidak bergantung pada framework atau library tertentu
- Demo javac menunjukkan bagaimana FlowTracker membantu memahami format file class yang dihasilkan dan bytecode di dalamnya dengan mengamati compiler Java
Cara Menggunakan dan Hal yang Perlu Diperhatikan
- Saat ini FlowTracker lebih dekat ke proof-of-concept daripada siap produksi
- FlowTracker bekerja baik pada beberapa program contoh, tetapi tidak dijamin bekerja baik di semua program
- Karena menambahkan banyak overhead, eksekusi program menjadi jauh lebih lambat
- Langkah penggunaan:
- Unduh agent jar
flowtracker-*.jardari halaman rilis Github - Tambahkan
-javaagent:path/to/flowtracker.jarke command line Java - Tambahkan juga output
java -jar flowtracker.jar jvmoptske command line untuk mematikan beberapa optimasi JVM yang mengganggu FlowTracker - Secara default, FlowTracker memulai web server pada port 8011, jadi buka
http://localhost:8011/di browser
- Unduh agent jar
- Opsi konfigurasi yang lebih detail tersedia di
USAGE.md
Cara Kerja Internal: Instrumentasi Bytecode dan Model Tracker
- FlowTracker adalah agent instrumentasi yang menyuntikkan kode ke file class, yaitu bytecode, saat JVM memuat kelas
- Kode yang disuntikkan mempertahankan pemetaan antara data di memori dan asal-usulnya saat program membaca, meneruskan, dan menulis data
- Fokus target pelacakan adalah data teks dan biner seperti
String,char,byte[], bukan terutama angka, data terstruktur, atau data hasil perhitungan - Cara yang digunakan:
- Mengganti beberapa pemanggilan metode JDK dengan pemanggilan metode versi FlowTracker
- Menyuntikkan kode ke lokasi inti JDK untuk melacak input dan output
- Melakukan analisis aliran data dan instrumentasi yang lebih dalam untuk melacak variabel lokal dan nilai stack di dalam metode
- Menambahkan kode sebelum dan sesudah pemanggilan metode serta pada awal dan akhir metode yang dipanggil untuk melacak argumen dan nilai return dengan
ThreadLocal
-
Model Data Tracker
Tracker: menyimpan isi objek yang dilacak dan informasi asal-usulnyacontent: data seperti semua byte yang melewatiInputStreamatauOutputStreamsource: menghubungkan rentang tertentu dari suatu konten dengan rentang tertentu pada tracker lainTrackerRepository: menyimpanMap<Object, Tracker>global besar yang menghubungkan objek yang diminati denganTrackerterkaitTrackerPoint: menunjuk satu posisi di dalam tracker dan merepresentasikan satu nilai primitive, seperti asal satubyte
Instrumentasi dasar: hook JDK dan ASM
- FlowTracker menjaga
Trackertetap mutakhir dengan menyisipkan pemanggilan metode hook ketika metode JDK tertentu dipanggil - Contoh paling sederhana adalah
System.arraycopy- Pemanggilan
java.lang.System.arraycopydiganti dengan pemanggilancom.coekie.flowtracker.hook.SystemHook.arraycopy SystemHookmemanggilarraycopyyang sebenarnya, lalu mengambil tracker array sumber dan tujuan dariTrackerRepositorydan memperbaruinya agar tracker tujuan menunjuk ke sumber
- Pemanggilan
- Untuk instrumentasi seperti ini, digunakan library manipulasi bytecode ASM
- Sebagian besar hook ditambahkan di sisi yang dipanggil, di dalam metode JDK, bukan di sisi pemanggil
- Misalnya, pemanggilan
FileInputStreamHook.afterReadByteArrayditambahkan di akhirFileInputStream.read(byte[]) - Instrumentasi ini diimplementasikan sebagai mikro-framework internal berbasis annotation yang menggunakan
AdviceAdaptermilik ASM
- Misalnya, pemanggilan
- FlowTracker menambahkan hook ke kelas-kelas terkait I/O di JDK seperti
java.io.FileInputStream,java.io.FileOutputStream,sun.nio.ch.FileChannelImpl,sun.nio.ch.IOUtil, dansun.nio.ch.NioSocketImpl - Implementasi terkait:
Pelacakan nilai primitive dan analisis aliran data di dalam metode
- Nilai primitive seperti
bytetidak memiliki identity seperti objek, sehingga tidak dapat dilacak dengan aman sebagai keyMapdiTrackerRepository - FlowTracker menulis ulang kode agar asal-usul nilai primitive disimpan secara terpisah pada variabel lokal di dalam metode
- Misalnya, setelah
byte b = x[1], tracker untukbdiperoleh denganArrayHook.getElementTracker(x, 1), lalu saaty[2] = b, asal-usulnya dicatat pada array tujuan denganArrayHook.setElementTracker(y, 2, bTracker) - Untuk itu, FlowTracker melakukan interpretasi simbolik (symbolic interpretation) di atas fitur analisis ASM
- Pada setiap titik dalam metode, ia memodelkan dari mana nilai pada variabel lokal dan stack berasal serta ke mana nilai tersebut mengalir
- Implementasi terkait:
- FlowValue dan ArrayLoadValue
- MergedValue: menangani situasi ketika nilai dapat berasal dari beberapa lokasi karena alur kontrol seperti pernyataan if atau loop
- FlowInterpreter: sebagai perluasan
InterpreterASM, menginterpretasikan instruksi bytecode dan membuatFlowValueyang sesuai - Store dan ArrayStore
- FlowTransformer: menggerakkan keseluruhan proses analisis dan instrumentasi
- Tidak semua nilai primitive dilacak; fokusnya adalah
bytedanchar, sementaraintdanlongditangani secara lebih terbatas
Aliran data yang melintasi pemanggilan metode
- Analisis di dalam metode saja tidak dapat menangani situasi ketika nilai primitive mengalir sebagai argumen dan nilai kembalian metode lain
- FlowTracker menyimpan
PointTrackeruntuk argumen dan nilai kembalian diInvocation, lalu memasukkannya keThreadLocaltepat sebelum pemanggilan metode - Pada titik awal metode yang dipanggil, informasi dari
ThreadLocaldapat diambil denganInvocation.start(...)untuk menggunakan asal-usul argumen primitive - Dengan cara ini, bahkan ketika nilai primitive diteruskan ke metode seperti
out.write(b), tracker untukvaluedapat diwarisi di dalamwrite(byte value) - Implementasi terkait:
Memperlakukan kode itu sendiri sebagai sumber data
- Sumber utama yang dilacak FlowTracker adalah nilai yang berasal dari I/O dan dari kode itu sendiri
- Nilai yang berasal dari kode mencakup konstanta primitive dan
Stringseperti'a'dan"abc" - Untuk konstanta seperti ini, dibuat
ClassOriginTrackerper kelas, lalu disimpan representasi teks dari referensi kelas dan konstanta tersebut - Saat sebuah konstanta direferensikan, tracker untuk nilai tersebut akan menunjuk ke posisi di dalam representasi teks ini
- Karena model ini memperlakukan konstanta seolah-olah dibaca dari representasi teks kode, ia menjadi mirip dengan model pelacakan I/O
- Demi performa, digunakan ConstantDynamic (JEP 309) agar metode
constantPointtidak dipanggil setiap kali metode dijalankan - Implementasi terkait:
Penanganan string literal dan batasannya
- String literal membuat salinan
Stringbaru, lalu menghubungkanbyte[]di dalamString.valuedenganClassOriginTracker - Pernyataan seperti
String s = "abc";ditulis ulang menjadi bentuk sepertiString s = StringHook.constantString("abc", 1234, 81); - Cara ini merusak jaminan String interning yang biasanya disediakan JVM
- Semula, semua kemunculan konstanta String yang sama harus mereferensikan instance yang sama
- Setelah instrumentation, kode yang bergantung pada jaminan ini bisa rusak
- FlowTracker menyediakan beberapa mekanisme untuk mengurangi masalah ini
- Menggunakan ConstantDynamic agar string literal yang sama pada baris yang sama tetap mengembalikan instance yang sama meski dieksekusi berkali-kali
- Menulis ulang sebagian ekspresi
stringA == stringBmenjadiObjects.equals(stringA, stringB), sehingga dari sudut pandang tertentu terlihat seperti instance yang sama - Menonaktifkan pelacakan string literal di beberapa paket seperti
java.lang.* - Perilaku ini dapat dikonfigurasi dengan
breakStringInterningdiUSAGE.md
- Implementasi terkait:
Fallback untuk nilai yang tidak dilacak
- FlowTracker tidak melacak semua nilai dalam program
- Alasannya adalah kekhawatiran performa, bagian yang belum diimplementasikan, nilai yang kurang relevan, serta kebutuhan model data yang lebih kompleks untuk merepresentasikan nilai yang muncul dari kombinasi beberapa sumber
- Ketika nilai yang sebelumnya tidak dilacak mencapai lokasi yang harus mulai dilacak, nilai itu dihubungkan ke
ClassOriginTrackerseperti konstanta, dan posisinya direpresentasikan sebagai"<?>" - Misalnya, panjang array tidak dilacak, sehingga ketika
write(array.length)dipanggil,InvocationmenerimaPointTrackeryang menunjuk ke lokasi kode tempat pemanggilanwrite - Akibatnya, meskipun pada keluaran dalam format biner sumber aslinya tidak terlihat, dalam sebagian kasus makna nilai dapat ditafsirkan dengan cepat melalui string terlacak di sekitarnya dan lokasi kode
Topik implementasi yang bisa dibahas lebih lanjut
- MergedValue menangani pelacakan nilai yang melewati percabangan dan loop, dan dianggap sebagai salah satu bagian tersulit dalam analisis aliran data
- String concatenation ditangani melalui indification (JEP 280) dengan menambahkan hook pada
MethodHandleyang dikembalikan olehStringConcatFactory - Menemukan kode sumber, melakukan dekompilasi dengan Vineflower, serta menghubungkan bytecode dengan baris sumber juga termasuk dalam implementasi
- Konfigurasi ClassLoader berfokus pada menghindari dependensi bootclasspath dan konflik aplikasi, sekaligus mempertahankan siklus pengembangan cepat tanpa shading maupun nested jar
- Pelacakan nilai primitive yang disimpan di field juga termasuk dalam implementasi
- Frontend terdiri dari server web berbasis Jetty dan JAX-RS, serta UI web berbasis Svelte
1 komentar
Komentar Hacker News
Untuk instrumentasi, alih-alih agen instrumentasi, saya memakai fork dari compiler resmi Clojure, dan memanfaatkan karakteristik Clojure yang memungkinkan compiler diganti dengan mudah saat pengembangan untuk menyisipkan bytecode tambahan
Hal menarik dari rekaman eksekusi program Clojure adalah sebagian besar nilainya immutable, jadi snapshot bisa dibuat cukup dengan mempertahankan pointer
Karena demo di tulisan asli menelusuri aplikasi web, saya juga meninggalkan demo debugging aplikasi web dengan FlowStorm bagi yang tertarik https://www.youtube.com/watch?v=h8AFpZkAwPo
Senang melihat alat-alat di ekosistem Java/JVM yang bagus. Terakhir kali saya sekaget ini adalah saat dulu melihat jitwatch https://github.com/AdoptOpenJDK/jitwatch
FlowTracker sedikit mengingatkan saya pada analisis taint, yang melacak bagaimana input pengguna yang belum tervalidasi atau nilai rahasia mengalir di dalam program agar tidak bocor atau dipakai tanpa validasi
Kata kunci pencariannya adalah “dynamic taint tracking/analysis”
https://github.com/gmu-swe/phosphor
https://github.com/soot-oss/SootUp
https://github.com/feliam/klee-taint
Ke depannya, mudah membayangkan alat seperti ini menjadi garis pertahanan pertama saat melacak bug
Saat mengembangkan FlowTracker, banyak pekerjaan muncul dari upaya membuat pelacakan berfungsi pada program contoh tertentu
Saya tahu hasil yang ingin dicapai, tetapi sulit memprediksi mekanisme level rendah apa yang harus didukung agar contoh tertentu bisa berjalan, dan itu sering bergantung pada detail implementasi internal JDK atau library yang dilalui data
Namun keterhubungan elemen HTML ke skrip SQL yang memasukkan data itu ke DB tidak terjadi dengan cara seperti itu
Itu bukan sesuatu yang saya harapkan atau sengaja buat; memang terjadi begitu saja, jadi saya sendiri cukup terkejut dan jadi menantikan apa lagi yang bisa dilakukan dengan pendekatan ini
Akan bagus juga kalau ada cara melacak apakah data bersifat sementara atau harus ditulis kembali
Semakin banyak constraint seperti ini bisa dideskripsikan di depan, semakin baik
Di Smalltalk, semuanya adalah objek dan pesan, sehingga bisa ditelusuri balik dan diinteraksikan
Saya tidak sempat meluangkan waktu untuk mengembangkannya lebih jauh, tetapi alat pengembangan web sepertinya akan sangat diuntungkan oleh pelacakan atribusi full-stack semacam ini
Namun mengintegrasikan solusi seperti ini ke framework yang ada terasa sebagai tantangan besar
[1] HTML Source Maps - https://github.com/connorjclark/html-source-maps https://docs.google.com/document/d/19XYWiPL9h9vA6QcOrGV9Nfkr...
https://www.youtube.com/watch?v=TWAMr72VaaU&t=164s dan https://witheve.com/
Jika apa yang dilakukan FlowTracker diperluas, ia juga bisa menemukan SQL atau kerentanan injection lainnya. Jadi ada kemungkinan alat yang Anda ingat memakai pendekatan yang mirip
Atau pertanyaan seperti “apa saja yang dilihat string ini sejak saat dibuat hingga tiba di layar saya”
Ini terlihat seperti satu langkah ke arah itu
Untuk saat ini saya harus berhenti dulu, tetapi saya menantikan bisa membuatnya berjalan dan mengutak-atiknya