Michael Howard dan Keith Brown
Artikel ini mengasumsikan Anda sudah familiar dengan C++, C#, dan SQL
Ringkasan: Ketika menyangkut masalah keamanan, ada banyak situasi yang dapat menimbulkan masalah. Anda mungkin memercayai semua kode yang berjalan di jaringan Anda, memberi semua pengguna akses ke file penting, dan tidak pernah repot-repot memeriksa apakah kode di mesin Anda telah berubah. Anda mungkin juga tidak menginstal perangkat lunak antivirus, gagal mengamankan kode Anda sendiri, dan memberikan terlalu banyak izin kepada banyak akun. Anda bahkan mungkin ceroboh dalam menggunakan sejumlah fungsi bawaan yang memungkinkan intrusi berbahaya, dan Anda mungkin membiarkan port server terbuka tanpa pengawasan apa pun. Tentunya masih banyak lagi contoh yang bisa kita berikan. Masalah apa yang benar-benar penting (yaitu, kesalahan paling berbahaya yang harus segera mendapat perhatian agar data dan sistem Anda tidak dikompromikan)? Pakar keamanan Michael Howard dan Keith Brown menawarkan sepuluh tips untuk membantu Anda.
--------------------------------------------------- -----------------------------------
Masalah keamanan melibatkan banyak aspek. Risiko keamanan bisa datang dari mana saja. Anda mungkin telah menulis kode penanganan kesalahan yang tidak efektif atau terlalu murah hati saat memberikan izin. Anda mungkin lupa layanan apa yang berjalan di server Anda. Anda dapat menerima semua masukan pengguna. Dan sebagainya. Untuk memberi Anda langkah awal dalam melindungi komputer, jaringan, dan kode Anda, berikut sepuluh tips yang dapat Anda ikuti untuk strategi jaringan yang lebih aman.
1. Mempercayai masukan pengguna menempatkan Anda pada risiko.
Meskipun Anda tidak membaca sisanya, ingatlah ini: “Jangan percaya masukan pengguna.” Masalah muncul jika Anda selalu berasumsi bahwa data tersebut valid dan tidak berbahaya. Sebagian besar kerentanan keamanan melibatkan penyerang yang memasukkan data tertulis yang berbahaya ke server.
Mempercayai kebenaran masukan dapat menyebabkan buffer overflows, serangan skrip lintas situs, serangan kode penyisipan SQL, dan banyak lagi.
Mari kita bahas potensi vektor serangan ini secara mendetail.
2. Mencegah buffer overflow
Ketika penyerang memberikan panjang data yang lebih besar dari yang diharapkan aplikasi, terjadi buffer overflow, dan data meluap ke ruang memori internal. Buffer overflow pada dasarnya adalah masalah C/C++. Ini merupakan ancaman, namun biasanya mudah untuk diperbaiki. Kami hanya melihat dua buffer overflows yang tidak terlihat jelas dan sulit diperbaiki. Pengembang tidak mengantisipasi bahwa data yang disediakan secara eksternal akan lebih besar daripada buffer internal. Overflow menyebabkan kerusakan pada struktur data lain di memori, yang sering dieksploitasi oleh penyerang untuk menjalankan kode berbahaya. Kesalahan indeks array juga dapat menyebabkan buffer underflow dan overruns, namun hal ini lebih jarang terjadi.
Lihatlah cuplikan kode C++ berikut:
void Melakukan Sesuatu(char *cBuffSrc, DWORD cbBuffSrc) {
char cBuffDest[32];
memcpy(cBuffDest,cBuffSrc,cbBuffSrc);
}
Apa masalahnya? Sebenarnya tidak ada yang salah dengan kode ini jika cBuffSrc dan cbBuffSrc berasal dari sumber yang dapat dipercaya (seperti kode yang tidak mempercayai data sehingga memverifikasi validitas dan ukurannya). Namun jika data berasal dari sumber yang tidak terpercaya dan belum terverifikasi, maka penyerang (sumber tidak terpercaya) dapat dengan mudah membuat cBuffSrc lebih besar dari cBuffDest dan juga mengatur cbBuffSrc menjadi lebih besar dari cBuffDest. Ketika memcpy menyalin data ke cBuffDest, alamat pengirim dari DoSomething diubah, dan karena cBuffDest berdekatan dengan alamat pengirim pada bingkai tumpukan fungsi, penyerang dapat melakukan beberapa operasi jahat melalui kode.
Cara untuk mengkompensasinya adalah dengan tidak mempercayai masukan pengguna, dan tidak mempercayai data apa pun yang dibawa dalam cBuffSrc dan cbBuffSrc:
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {
const DWORD cbBuffDest = 32;
char cBuffDest[cbBuffDest];
#ifdef _DEBUG
memset(cBuffDest, 0x33, cbBuffSrc);
#endif
memcpy(cBuffDest, cBuffSrc, min(cbBuffDest, cbBuffSrc));
}
Fungsi ini menunjukkan tiga properti dari fungsi yang ditulis dengan benar yang dapat mengurangi buffer overflow. Pertama, pemanggil harus menyediakan panjang buffer. Tentu saja, Anda tidak bisa begitu saja mempercayai nilai ini! Selanjutnya, dalam build debug, kode mendeteksi apakah buffer sebenarnya cukup besar untuk menampung buffer sumber. Jika tidak, pelanggaran akses dapat dipicu dan kode dapat dimuat ke dalam debugger. Saat melakukan debug, Anda akan terkejut betapa banyak bug yang Anda temukan. Terakhir dan yang paling penting, panggilan ke memcpy bersifat defensif karena panggilan tersebut tidak menyalin lebih banyak data daripada yang dapat ditampung oleh buffer target.
Sebagai bagian dari Windows® Security Push di Microsoft, kami membuat daftar fungsi penanganan string yang aman untuk pemrogram C. Anda dapat menemukannya di Strsafe.h: Penanganan String yang Lebih Aman dalam C (Bahasa Inggris).
3. Mencegah pembuatan skrip lintas situs
Serangan skrip lintas situs adalah masalah unik di Web. Serangan ini dapat membahayakan data klien melalui kerentanan tersembunyi dalam satu halaman Web. Bayangkan konsekuensi dari cuplikan kode ASP.NET berikut:
<script bahasa=c#>
Response.Write("Halo," + Permintaan.QueryString("nama"));
</script>
Berapa banyak orang yang pernah melihat kode serupa? Namun yang mengejutkan, ada masalah! Biasanya, pengguna akan mengakses kode ini menggunakan URL yang mirip dengan berikut ini:
http://explorationair.com/welcome.aspx?name=Michael
Kode C# mengasumsikan bahwa data selalu valid dan hanya berisi nama. Namun, penyerang dapat menyalahgunakan kode ini dengan memberikan skrip dan kode HTML sebagai nama. Jika Anda memasukkan URL berikut
http://northwindtraders.com/welcome.aspx?name=<script>alert(' Halo!');
</script>
Anda akan mendapatkan halaman web dengan kotak dialog bertuliskan "Halo!" Anda mungkin berkata, "Jadi apa?" Bayangkan penyerang dapat mengelabui pengguna agar mengeklik tautan seperti ini, namun string kueri berisi beberapa skrip dan HTML yang sangat berbahaya, sehingga memperoleh cookie pengguna dan mengirimkannya ke situs web milik pengguna. penyerang; sekarang penyerang memiliki akses ke informasi cookie pribadi Anda, atau lebih buruk lagi.
Untuk menghindari hal ini, ada dua cara. Yang pertama adalah tidak mempercayai masukan dan membatasi secara ketat isi nama pengguna. Misalnya, Anda dapat menggunakan ekspresi reguler untuk memeriksa apakah nama hanya berisi subkumpulan karakter umum dan tidak terlalu besar. Cuplikan kode C# berikut menunjukkan cara melakukan langkah ini:
Regex r = new Regex(@"^[w]{1,40}$")
;
// Bagus! Talinya oke
} kalau tidak {
// tidak bagus! String tidak valid
}
Kode ini menggunakan ekspresi reguler untuk memverifikasi bahwa string hanya berisi 1 hingga 40 huruf atau angka. Ini adalah satu-satunya cara aman untuk menentukan apakah suatu nilai benar.
Tidak mungkin HTML atau skrip dapat menipu ekspresi reguler ini! Jangan gunakan ekspresi reguler untuk mencari karakter yang tidak valid dan menolak permintaan jika ditemukan karakter yang tidak valid tersebut, karena mudah untuk melewatkan sesuatu.
Tindakan pencegahan kedua adalah mengkodekan HTML semua masukan sebagai keluaran. Hal ini mengurangi tag HTML berbahaya menjadi karakter escape yang lebih aman. Anda dapat menggunakan HttpServerUtility.HtmlEncode di ASP.NET, atau Server.HTMLEncode di ASP untuk menghindari string yang berpotensi bermasalah.
4. Jangan meminta izin sa
Serangan kepercayaan input terakhir yang akan kita bahas adalah penyisipan kode SQL. Banyak pengembang menulis kode yang mengambil masukan dan menggunakan masukan tersebut untuk membuat kueri SQL yang berkomunikasi dengan penyimpanan data backend seperti Microsoft® SQL Server™ atau Oracle.
Perhatikan cuplikan kode berikut:
void DoQuery(string Id) {
SqlConnection sql=SqlConnectionbaru(@"sumber data=localhost;" +
"id pengguna=sa;kata sandi=kata sandi;");
sql.Buka();
sqlstring= "PILIH telah dikirim" +
" DARI pengiriman DIMANA id='" + Id + "'";
SqlCommand cmd = baru SqlCommand(sqlstring,sql);
•••
Kode ini memiliki tiga kelemahan serius. Pertama, membuat sambungan dari layanan Web ke SQL Server sebagai akun administrator sistem sa. Anda akan segera melihat kelemahannya. Poin kedua, perhatikan praktik cerdas menggunakan "kata sandi" sebagai kata sandi akun sa!
Namun kekhawatiran sebenarnya adalah rangkaian string yang menyusun pernyataan SQL. Jika pengguna memasukkan 1001 untuk ID, Anda mendapatkan pernyataan SQL berikut, yang sepenuhnya valid.
SELECT hasshipped FROM pengiriman WHERE id = '1001'
Namun penyerang jauh lebih kreatif dari itu. Mereka akan memasukkan "pengiriman tabel DROP '1001' --" untuk ID, yang akan menjalankan kueri seperti ini:
PILIH telah dikirim DARI
pengiriman DIMANA id = '1001'
DROP table pengiriman -- ';
Ini mengubah cara kerja kueri. Kode ini tidak hanya mencoba menentukan apakah sesuatu telah dikirimkan, tetapi juga menghapus (menghapus) tabel pengiriman! Operator -- adalah operator komentar dalam SQL, yang memudahkan penyerang membuat serangkaian pernyataan SQL yang valid namun berbahaya!
Saat ini, Anda mungkin bertanya-tanya bagaimana setiap pengguna dapat menghapus tabel di database SQL Server. Tentu saja Anda benar, hanya administrator yang dapat melakukan pekerjaan seperti itu. Tapi di sini Anda terhubung ke database sebagai sa, dan sa dapat melakukan apapun yang dia inginkan pada database SQL Server. Jangan pernah menyambung ke SQL Server sebagai sa dari aplikasi apa pun; pendekatan yang benar adalah menggunakan Otentikasi Terintegrasi Windows, jika sesuai, atau menyambungkan sebagai akun yang telah ditentukan sebelumnya dengan izin yang sesuai.
Memperbaiki masalah kode penyisipan SQL itu mudah. Dengan menggunakan prosedur dan parameter tersimpan SQL, kode berikut menunjukkan cara membuat kueri seperti itu - dan cara menggunakan ekspresi reguler untuk mengonfirmasi bahwa masukan valid, karena transaksi kami menetapkan bahwa ID pengiriman hanya boleh terdiri dari 4 hingga 10 digit:
Regex r = Regex baru(@"^d{4,10}$");
if (!r.Match(Id).Sukses)
melempar Pengecualian baru("ID Tidak Valid");
SqlConnection sqlConn= new SqlConnection(strConn);
string str="sp_Telah Dikirim";
SqlCommand cmd = baru SqlCommand(str,sqlConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID",Id);
Buffer overflow, skrip lintas situs, dan serangan kode penyisipan SQL adalah contoh masalah input tepercaya. Semua serangan ini dimitigasi oleh mekanisme yang menganggap semua masukan berbahaya kecuali terbukti sebaliknya.
5. Perhatikan kode enkripsinya!
Mari kita lihat sesuatu yang mungkin mengejutkan kita. Saya menemukan bahwa lebih dari tiga puluh persen kode keamanan yang kami periksa memiliki kerentanan keamanan. Mungkin kerentanan paling umum adalah kode enkripsi Anda sendiri, yang mungkin rentan. Jangan pernah membuat kode enkripsi Anda sendiri, itu adalah tugas yang bodoh. Jangan berpikir bahwa hanya karena Anda memiliki algoritma enkripsi sendiri maka orang lain tidak dapat memecahkannya. Penyerang memiliki akses ke debugger, dan mereka juga memiliki waktu dan pengetahuan untuk menentukan cara kerja sistem - seringkali merusaknya dalam beberapa jam. Anda harus menggunakan Win32® CryptoAPI, namespace System.Security.Cryptography menyediakan sejumlah algoritma enkripsi yang sangat baik dan teruji.
6. Mengurangi kemungkinan diserang.
Jika lebih dari 90% pengguna tidak memintanya, suatu fitur tidak boleh diinstal secara default. Layanan Informasi Internet (IIS) 6.0 mengikuti rekomendasi penginstalan ini, yang dapat Anda baca di artikel Wayne Berry "Inovasi dalam Layanan Informasi Internet Memungkinkan Anda Menjaga Keamanan Data dan Proses Server," yang dirilis bulan ini. Ide di balik strategi instalasi ini adalah Anda tidak akan memperhatikan layanan yang tidak Anda gunakan, dan jika layanan tersebut berjalan, layanan tersebut dapat dieksploitasi oleh orang lain. Jika suatu fitur diinstal secara default, fitur tersebut harus dijalankan berdasarkan prinsip otorisasi paling sedikit. Artinya, jangan izinkan aplikasi berjalan dengan hak administrator kecuali diperlukan. Sebaiknya ikuti saran ini.
7. Gunakan prinsip otorisasi paling sedikit
Sistem operasi dan runtime bahasa umum memiliki kebijakan keamanan karena beberapa alasan. Banyak orang berasumsi bahwa alasan utama kebijakan keamanan ini ada adalah untuk mencegah pengguna melakukan tindakan yang merugikan secara sengaja: mengakses file yang tidak boleh mereka akses, mengkonfigurasi ulang jaringan agar sesuai dengan kebutuhan mereka, dan perilaku buruk lainnya. Ya, jenis serangan orang dalam ini umum terjadi dan perlu diwaspadai, namun ada alasan lain untuk tetap berpegang pada strategi keamanan ini. Artinya, membangun penghalang defensif di sekitar kode untuk mencegah pengguna membuat kekacauan di jaringan melalui tindakan yang disengaja atau (seperti yang sering terjadi) tidak disengaja. Misalnya, lampiran yang diunduh melalui email, ketika dijalankan pada mesin Alice, dibatasi pada sumber daya yang dapat diakses Alice. Jika lampiran berisi kuda Troya, strategi keamanan yang baik adalah membatasi kerusakan yang dapat ditimbulkannya.
Saat Anda merancang, membangun, dan menyebarkan aplikasi server, Anda tidak dapat berasumsi bahwa semua permintaan berasal dari pengguna yang sah. Jika orang jahat mengirimi Anda permintaan jahat (mudah-mudahan tidak) dan kode Anda berperilaku buruk, Anda ingin aplikasi Anda memiliki semua pertahanan yang memungkinkan untuk membatasi kerusakan. Oleh karena itu, kami yakin perusahaan Anda menerapkan kebijakan keamanan bukan hanya karena perusahaan tidak mempercayai Anda atau kode Anda, namun juga untuk melindungi dirinya dari kode luar yang berbahaya.
Prinsip otorisasi paling sedikit menyatakan bahwa izin minimum yang disyaratkan oleh kode harus diberikan dalam waktu sesingkat-singkatnya. Oleh karena itu, pasanglah sebanyak mungkin dinding pelindung di sekitar kode Anda setiap saat. Ketika sesuatu yang buruk terjadi - seperti yang dijamin oleh Hukum Murphy - Anda akan senang karena tembok pelindung tersebut ada. Oleh karena itu, berikut adalah beberapa metode khusus untuk menjalankan kode menggunakan prinsip otorisasi paling sedikit.
Pilih lingkungan aman untuk kode server Anda yang hanya mengizinkannya mengakses sumber daya yang diperlukan untuk melakukan tugasnya. Jika beberapa bagian kode Anda memerlukan izin tinggi, pertimbangkan untuk mengisolasi bagian kode tersebut dan menjalankannya secara terpisah dengan izin yang lebih tinggi. Untuk memisahkan kode ini dengan aman yang berjalan dengan informasi autentikasi sistem operasi berbeda, yang terbaik adalah menjalankan kode ini dalam proses terpisah (berjalan di lingkungan aman dengan hak istimewa lebih tinggi). Ini berarti Anda memerlukan komunikasi antar-proses (seperti COM atau Microsoft .NET remoting), dan Anda perlu merancang antarmuka ke kode tersebut untuk meminimalkan bolak-balik.
Jika Anda memisahkan kode ke dalam rakitan di lingkungan .NET Framework, pertimbangkan tingkat izin yang diperlukan untuk setiap bagian kode. Anda akan menemukan bahwa ini adalah proses yang mudah: Pisahkan kode yang memerlukan hak istimewa lebih tinggi ke dalam rakitan terpisah yang memberikan lebih banyak hak istimewa, sambil membiarkan sebagian besar rakitan lainnya berjalan dengan hak istimewa lebih rendah sehingga Anda dapat menambahkan lebih banyak penjaga di sekitar kode Anda. Saat melakukan ini, jangan lupa bahwa, karena tumpukan Keamanan Akses Kode (CAS), Anda membatasi izin tidak hanya pada rakitan Anda sendiri, tetapi juga pada rakitan mana pun yang Anda panggil.
Banyak orang membangun aplikasi mereka sendiri sehingga komponen baru dapat dipasang ke produk mereka setelah diuji dan tersedia bagi pelanggan. Mengamankan aplikasi jenis ini sangat sulit karena Anda tidak dapat menguji setiap jalur kode yang mungkin untuk menemukan bug dan lubang keamanan. Namun, jika aplikasi Anda dihosting, CLR menyediakan fitur luar biasa yang dapat digunakan untuk menutup titik ekstensibilitas ini. Dengan mendeklarasikan objek izin atau kumpulan izin dan memanggil PermitOnly atau Deny, Anda menambahkan tanda ke tumpukan Anda yang akan memblokir pemberian izin ke kode apa pun yang Anda panggil. Dengan melakukan ini sebelum memanggil plugin, Anda dapat membatasi tugas yang dapat dilakukan plugin tersebut. Misalnya, plugin untuk menghitung pembayaran cicilan tidak memerlukan akses apa pun ke sistem file. Ini hanyalah contoh lain dari hak istimewa yang paling rendah, di mana Anda melindungi diri Anda sendiri sebelumnya. Pastikan untuk mencatat pembatasan ini, dan ketahuilah bahwa plugin dengan hak istimewa lebih tinggi dapat menggunakan pernyataan Assert untuk menghindari pembatasan ini.
8. Waspadai pola kegagalan
dan terimalah. Yang lain benci menulis kode penanganan kesalahan seperti halnya Anda. Ada begitu banyak alasan mengapa kode bisa gagal, dan memikirkannya saja bisa membuat frustasi. Kebanyakan programmer, termasuk kami, lebih memilih untuk fokus pada jalur eksekusi normal. Di situlah pekerjaan benar-benar selesai. Mari selesaikan penanganan kesalahan ini secepat dan semudah mungkin, lalu lanjutkan ke baris kode sebenarnya berikutnya.
Sayangnya, emosi ini tidak aman. Sebaliknya, kita perlu lebih memperhatikan pola kegagalan dalam kode kita. Kode ini sering kali ditulis dengan sedikit perhatian mendalam dan sering kali tidak diuji sepenuhnya. Ingat kapan terakhir kali Anda benar-benar yakin telah men-debug setiap baris kode dalam suatu fungsi, termasuk setiap penangan kesalahan kecil di dalamnya?
Kode yang belum diuji sering kali menyebabkan kerentanan keamanan. Ada tiga hal yang dapat membantu Anda mengurangi masalah ini. Pertama, berikan perhatian yang sama kepada penangan kesalahan kecil itu seperti kode normal Anda. Pertimbangkan keadaan sistem saat kode penanganan kesalahan Anda dijalankan. Apakah sistem dalam keadaan efisien dan aman? Kedua, setelah Anda menulis sebuah fungsi, telusuri fungsi tersebut dan debug secara menyeluruh beberapa kali, pastikan untuk menguji setiap penangan kesalahan. Perhatikan bahwa bahkan dengan teknik seperti itu, kesalahan pengaturan waktu yang sangat halus mungkin tidak ditemukan. Anda mungkin perlu meneruskan parameter kesalahan ke fungsi Anda, atau menyesuaikan keadaan sistem dengan cara tertentu agar penangan kesalahan dapat dijalankan. Dengan meluangkan waktu untuk menelusuri kode Anda, Anda dapat memperlambat dan memiliki cukup waktu untuk melihat kode Anda dan status sistem Anda saat dijalankan. Dengan hati-hati menelusuri kode di debugger, kami menemukan banyak kelemahan dalam logika pemrograman kami. Ini adalah teknologi yang terbukti. Silakan gunakan teknik ini. Terakhir, pastikan rangkaian pengujian Anda menyebabkan fungsi Anda gagal. Cobalah untuk memiliki rangkaian pengujian yang memeriksa setiap baris kode dalam fungsi. Ini dapat membantu Anda mengenali pola, terutama saat mengotomatiskan pengujian dan menjalankannya setiap kali Anda membuat kode.
Ada satu hal yang sangat penting untuk dikatakan tentang mode kegagalan. Pastikan sistem Anda berada dalam kondisi seaman mungkin ketika kode Anda gagal. Beberapa kode yang bermasalah ditunjukkan di bawah ini:
bool accessGranted = true; // Terlalu optimis!
mencoba {
// Lihat apakah kita dapat mengakses c:test.txt
FileStream baru(@"c:test.txt",
FileMode.Buka,
FileAccess.Baca).Tutup();
}
tangkapan (SecurityException x) {
// Akses ditolak
accessGranted = salah;
}
menangkap (...) {
// Ada hal lain yang terjadi
}
Walaupun kita menggunakan CLR, kita tetap diperbolehkan mengakses file tersebut. Dalam hal ini, SecurityException tidak dilempar. Namun bagaimana jika, misalnya, Daftar Kontrol Akses Diskresioner (DACL) file tidak mengizinkan kita mengaksesnya? Pada saat ini, jenis pengecualian lain akan dilempar. Namun karena asumsi optimis pada baris kode pertama, kita tidak akan pernah mengetahui hal ini.
Cara yang lebih baik untuk menulis kode ini adalah dengan berhati-hati:
bool accessGranted = false;
mencoba {
// Lihat apakah kita dapat mengakses c:test.txt
FileStream baru(@"c:test.txt",
FileMode.Buka,
FileAccess.Baca).Tutup();
// Kalau kita masih di sini, bagus!
accessGranted = benar;
}
catch (...) {}
Ini akan lebih stabil karena betapa pun gagalnya kita, kita akan selalu kembali ke mode paling aman.
9. Peniruan Identitas sangat rentan
Saat menulis aplikasi server, Anda akan sering menggunakan, secara langsung atau tidak langsung, fitur praktis Windows yang disebut peniruan identitas. Peniruan identitas memungkinkan setiap thread dalam suatu proses berjalan di lingkungan keamanan yang berbeda, biasanya lingkungan klien. Misalnya, ketika pengalihan sistem file menerima permintaan file melalui jaringan, ia mengautentikasi klien jarak jauh, memeriksa untuk mengonfirmasi bahwa permintaan klien tidak melanggar DACL pada pembagian, dan kemudian melampirkan tanda klien ke thread yang menangani permintaan tersebut. . untuk mensimulasikan klien. Thread ini kemudian dapat mengakses sistem file lokal di server menggunakan lingkungan keamanan klien. Ini nyaman karena sistem file lokal sudah aman. Ia melakukan pemeriksaan akses dengan mempertimbangkan jenis akses yang diminta, DACL pada file, dan tanda peniruan identitas pada thread. Jika pemeriksaan akses gagal, sistem file lokal melaporkannya ke pengalihan sistem file, yang kemudian mengirimkan kesalahan ke klien jarak jauh. Hal ini tidak diragukan lagi nyaman untuk pengalihan sistem file, karena ia hanya meneruskan permintaan ke sistem file lokal dan membiarkannya melakukan pemeriksaan aksesnya sendiri, sama seperti jika kliennya adalah klien lokal.
Ini semua baik dan bagus untuk gateway sederhana seperti pengalihan file. Namun simulasi sering kali digunakan dalam aplikasi lain yang lebih kompleks. Ambil aplikasi web sebagai contoh. Jika Anda menulis program ASP klasik yang tidak dikelola, ekstensi ISAPI, atau aplikasi ASP.NET dan memiliki spesifikasi berikut dalam file Web.config-nya
<identity impersonate='true'>
maka lingkungan berjalan Anda akan memiliki dua lingkungan Keamanan yang berbeda: Anda akan memiliki tag proses dan tag thread. Secara umum, tag thread akan digunakan untuk pemeriksaan akses (lihat Gambar 3). Dengan asumsi Anda sedang menulis aplikasi ISAPI yang berjalan dalam proses server web, dan dengan asumsi bahwa sebagian besar permintaan tidak diautentikasi, tag thread Anda mungkin IUSR_MACHINE, namun tag proses Anda adalah SYSTEM! Misalkan kode Anda dapat dieksploitasi oleh pelaku kejahatan melalui buffer overflow. Apakah menurut Anda akan puas hanya dengan berjalan sebagai IUSR_MACHINE? Tentu saja tidak. Kode serangannya kemungkinan besar memanggil RevertToSelf untuk menghapus tanda peniruan identitas dengan harapan meningkatkan tingkat hak istimewanya. Dalam hal ini, dia akan berhasil dengan mudah. Dia juga dapat menghubungi CreateProcess. Itu tidak menyalin tag proses baru dari tag peniruan identitas, tetapi dari tag proses sehingga proses baru dapat berjalan sebagai SISTEM.
Lalu bagaimana cara mengatasi masalah kecil ini? Selain memastikan bahwa tidak ada buffer overflow yang terjadi, ingatlah prinsip otorisasi paling sedikit. Jika kode Anda tidak memerlukan hak istimewa sebesar SYSTEM, jangan konfigurasikan aplikasi web Anda untuk berjalan dalam proses server web. Jika Anda hanya mengonfigurasi aplikasi web Anda untuk berjalan di lingkungan isolasi menengah atau tinggi, tag proses Anda adalah IWAM_MACHINE. Anda sebenarnya tidak memiliki izin apa pun, jadi serangan ini hampir tidak berpengaruh. Perhatikan bahwa di IIS 6.0 (segera menjadi komponen Windows .NET Server), kode yang ditulis pengguna tidak akan dijalankan sebagai SISTEM secara default. Berdasarkan pemahaman bahwa pengembang memang melakukan kesalahan, bantuan apa pun yang dapat diberikan server web dalam mengurangi izin yang diberikan pada kode akan berguna jika ada masalah keamanan pada kode.
Inilah kendala lain yang mungkin dihadapi oleh pemrogram COM. COM memiliki kecenderungan buruk untuk mengabaikan thread. Jika Anda memanggil server COM yang sedang dalam proses dan model threadnya tidak cocok dengan model thread pemanggil, COM akan melakukan panggilan pada thread lain. COM tidak menyebarkan tanda peniruan identitas pada thread pemanggil, sehingga hasilnya adalah panggilan tersebut dieksekusi dalam konteks keamanan proses, bukan dalam konteks keamanan thread pemanggil. Sungguh mengejutkan!
Berikut contoh lain dari jebakan simulasi. Asumsikan bahwa server Anda menerima permintaan yang dikirim melalui pipa bernama, DCOM, atau RPC. Anda mengautentikasi klien dan meniru identitas mereka, membuka objek kernel atas nama mereka melalui peniruan identitas. Dan Anda lupa menutup salah satu objek (seperti file) ketika klien terputus. Saat klien berikutnya masuk, Anda mengautentikasi dan meniru identitasnya, dan coba tebak apa yang terjadi? Anda masih dapat mengakses file yang "dilewatkan" oleh klien sebelumnya, meskipun klien baru tidak mendapatkan akses ke file tersebut. Untuk alasan kinerja, kernel hanya melakukan pemeriksaan akses pada suatu objek saat pertama kali dibuka. Anda masih dapat mengakses file ini meskipun nanti Anda mengubah lingkungan keamanan karena Anda meniru identitas pengguna lain.
Situasi yang disebutkan di atas mengingatkan Anda bahwa simulasi memberikan kemudahan bagi pengembang server, namun kemudahan ini memiliki bahaya besar yang tersembunyi. Saat Anda menjalankan program dengan flag tiruan, pastikan untuk memperhatikan kode Anda dengan cermat.
10. Menulis aplikasi yang benar-benar dapat digunakan oleh pengguna non-admin
. Hal ini merupakan akibat wajar dari prinsip otorisasi paling sedikit. Jika pemrogram terus mengembangkan kode yang memerlukan administrator agar dapat berjalan dengan baik di Windows, kita tidak dapat berharap untuk meningkatkan keamanan sistem. Windows memiliki serangkaian fitur keamanan yang sangat solid, namun pengguna tidak dapat memanfaatkannya jika mereka harus menjadi administrator untuk mengoperasikannya.
Bagaimana Anda bisa meningkatkannya? Pertama, coba sendiri, tanpa menjalankannya sebagai administrator. Anda akan segera menyadari betapa sulitnya menggunakan program yang tidak dirancang dengan mempertimbangkan keamanan. Suatu hari, saya (Keith) menginstal perangkat lunak yang disediakan oleh produsen perangkat genggam saya yang menyinkronkan data antara komputer desktop dan perangkat genggam saya. Seperti biasa, saya keluar dari akun pengguna normal saya, masuk lagi menggunakan akun administrator bawaan, menginstal perangkat lunak, lalu masuk lagi ke akun normal saya, dan mencoba menjalankan perangkat lunak. Akibatnya, aplikasi memunculkan kotak dialog yang menyatakan bahwa file data yang diperlukan tidak dapat diakses, dan kemudian memberikan pesan pelanggaran akses. Teman, ini adalah produk perangkat lunak dari produsen perangkat genggam besar. Apakah ada alasan untuk kesalahan ini?
Setelah menjalankan FILEMON dari http://sysinternals.com (dalam bahasa Inggris) saya segera menemukan bahwa aplikasi tersebut mencoba membuka file data untuk akses tulis yang diinstal di direktori yang sama dengan bagian tengah aplikasi yang dapat dieksekusi. Ketika aplikasi diinstal di direktori Program Files seperti yang diharapkan, aplikasi tersebut tidak boleh mencoba menulis data ke direktori tersebut. Program Files memiliki kebijakan kontrol akses yang ketat karena suatu alasan. Kami tidak ingin pengguna menulis ke direktori ini, karena ini akan memudahkan satu pengguna meninggalkan Trojan untuk dieksekusi oleh pengguna lain. Faktanya, konvensi ini adalah salah satu persyaratan tanda tangan dasar Windos XP (lihat http://www.microsoft.com/winlogo [Bahasa Inggris]).
Kami mendengar terlalu banyak programmer memberikan alasan mengapa mereka memilih untuk menjalankan sebagai administrator ketika mengembangkan kode. Jika kita terus mengabaikan masalah ini, kita hanya akan memperburuk keadaan. Teman, Anda tidak memerlukan hak administrator untuk mengedit file teks. Hak administrator juga tidak diperlukan untuk mengedit atau men-debug suatu program. Bila Anda memerlukan hak istimewa administrator, gunakan fitur RunAs sistem operasi untuk menjalankan 玎com.asp?TARGET=/winlogo/">http://www.microsoft.com/winlogo [Bahasa Inggris]).
Kami terlalu sering mendengar hal ini. Pemrogram memberikan alasan mengapa mereka memilih untuk dijalankan sebagai administrator ketika mengembangkan kode. Jika kita terus mengabaikan masalah ini, itu hanya akan memperburuk keadaan. Mengedit file teks tidak memerlukan hak administrator hak istimewa, gunakan fitur RunAs sistem operasi untuk menjalankan program terpisah dengan hak istimewa yang lebih tinggi (lihat kolom Ringkasan Keamanan November 2001 [Bahasa Inggris].). Jika Anda menulis alat untuk pengembang, Anda memiliki tanggung jawab tambahan untuk grup ini hentikan lingkaran setan penulisan kode yang hanya dapat dijalankan sebagai administrator. Tujuannya harus berubah secara mendasar.
Untuk informasi lebih lanjut tentang bagaimana pengembang dapat dengan mudah menjalankan sebagai non-administrator, lihat situs Web Keith di http://www.develop. com/kbrown (dalam bahasa Inggris). Lihat buku Michael Writing Secure Code (Microsoft Press, 2001), yang memberikan tip tentang cara menulis aplikasi yang berjalan dengan baik di lingkungan non-administrator.