Katakanlah Anda mengode fungsi yang mengambil input dari API eksternal MyAPI
.
API eksternal itu MyAPI
memiliki kontrak yang menyatakan akan mengembalikan a string
atau a number
.
Apakah disarankan untuk menjaga terhadap hal-hal seperti null
, undefined
, boolean
, dll meskipun itu bukan bagian dari API dari MyAPI
? Secara khusus, karena Anda tidak memiliki kendali atas API tersebut, Anda tidak dapat membuat jaminan melalui sesuatu seperti analisis tipe statis sehingga lebih baik aman daripada menyesal?
Saya sedang berpikir sehubungan dengan Prinsip Robustness .
design
api
api-design
web-services
functions
Adam Thompson
sumber
sumber
<!doctype html><html><head><title>504 Gateway Timeout</title></head><body>The server was unable to process your request. Make sure you have typed the address correctly. If the problem persists, please try again later.</body></html>
Jawaban:
Anda tidak boleh mempercayai input ke perangkat lunak Anda, apa pun sumbernya. Tidak hanya memvalidasi jenis-jenis itu penting, tetapi juga rentang input dan logika bisnis juga. Per komentar, ini dijelaskan dengan baik oleh OWASP
Jika tidak melakukannya, paling tidak akan meninggalkan Anda dengan data sampah yang harus Anda bersihkan nanti, tetapi paling buruk Anda akan meninggalkan peluang untuk eksploitasi berbahaya jika layanan hulu itu dikompromikan dengan cara tertentu (qv Target hack). Berbagai masalah di antaranya termasuk membuat aplikasi Anda dalam kondisi yang tidak dapat dipulihkan.
Dari komentar saya dapat melihat bahwa mungkin jawaban saya bisa menggunakan sedikit ekspansi.
Dengan "jangan pernah mempercayai input", saya hanya bermaksud bahwa Anda tidak dapat berasumsi bahwa Anda akan selalu menerima informasi yang valid dan dapat dipercaya dari sistem hulu atau hilir, dan oleh karena itu Anda harus selalu membersihkan input tersebut dengan kemampuan terbaik Anda, atau menolak saya t.
Satu argumen muncul di komentar yang akan saya bahas melalui contoh. Meskipun ya, Anda harus mempercayai OS Anda sampai taraf tertentu, itu tidak masuk akal untuk, misalnya, menolak hasil generator angka acak jika Anda memintanya untuk angka antara 1 dan 10 dan merespons dengan "bob".
Demikian pula, dalam kasus OP, Anda harus memastikan bahwa aplikasi Anda hanya menerima input yang valid dari layanan hulu. Apa yang Anda lakukan saat tidak apa-apa terserah Anda, dan sangat tergantung pada fungsi bisnis aktual yang ingin Anda capai, tetapi minimal Anda akan mencatatnya untuk debugging nanti dan memastikan aplikasi Anda tidak berjalan dalam keadaan tidak dapat dipulihkan atau tidak aman.
Meskipun Anda tidak akan pernah tahu setiap input yang mungkin diberikan seseorang / sesuatu kepada Anda, Anda tentu dapat membatasi apa yang diperbolehkan berdasarkan persyaratan bisnis dan melakukan beberapa bentuk input whitelist berdasarkan itu.
sumber
Ya tentu saja Tetapi apa yang membuat Anda berpikir jawabannya bisa berbeda?
Anda tentu tidak ingin membiarkan program Anda berperilaku tidak terduga jika API tidak mengembalikan apa yang dikatakan kontrak, bukan? Jadi setidaknya Anda harus berurusan dengan perilaku seperti entah bagaimana . Bentuk minimal penanganan kesalahan selalu sepadan dengan usaha (sangat minimal!), Dan sama sekali tidak ada alasan untuk tidak menerapkan sesuatu seperti ini.
Namun, berapa banyak usaha yang harus Anda investasikan untuk menangani kasus seperti ini sangat tergantung pada kasus dan hanya dapat dijawab dalam konteks sistem Anda. Seringkali, entri log pendek dan membiarkan aplikasi berakhir dengan anggun sudah cukup. Terkadang, Anda akan lebih baik untuk menerapkan beberapa penanganan pengecualian terperinci, berurusan dengan berbagai bentuk nilai pengembalian "salah", dan mungkin harus menerapkan beberapa strategi mundur.
Tapi itu membuat perbedaan besar jika Anda menulis hanya beberapa aplikasi format spreadsheet in-house, untuk digunakan oleh kurang dari 10 orang dan di mana dampak finansial dari crash aplikasi cukup rendah, atau jika Anda membuat mengemudi mobil otonom baru sistem, di mana aplikasi crash mungkin menelan biaya.
Jadi tidak ada jalan pintas untuk merefleksikan apa yang Anda lakukan , menggunakan akal sehat Anda selalu wajib.
sumber
Prinsip Robustness - khususnya, "menjadi liberal dalam apa yang Anda terima" setengahnya - adalah ide yang sangat buruk dalam perangkat lunak. Awalnya dikembangkan dalam konteks perangkat keras, di mana kendala fisik membuat toleransi rekayasa sangat penting, tetapi dalam perangkat lunak, ketika seseorang mengirimi Anda masukan yang salah atau tidak benar, Anda memiliki dua pilihan. Anda dapat menolaknya, (lebih baik dengan penjelasan tentang apa yang salah), atau Anda dapat mencoba mencari tahu apa artinya itu.
Tidak pernah, tidak pernah, tidak pernah memilih opsi kedua kecuali Anda memiliki sumber daya yang setara dengan tim Pencarian Google untuk mengerjakan proyek Anda, karena itulah yang diperlukan untuk membuat program komputer yang melakukan apa pun yang dekat dengan pekerjaan yang layak di domain masalah tertentu. (Dan bahkan kemudian, saran Google terasa seperti mereka keluar langsung dari bidang kiri sekitar separuh waktu.) Jika Anda mencoba untuk melakukannya, apa yang Anda akan berakhir dengan adalah sakit kepala besar di mana program Anda akan sering mencoba untuk menafsirkan input buruk sebagai X, padahal yang dimaksud pengirim adalah Y.
Ini buruk karena dua alasan. Yang jelas adalah karena Anda memiliki data buruk di sistem Anda. Yang kurang jelas adalah bahwa dalam banyak kasus, baik Anda maupun pengirim tidak akan menyadari bahwa ada yang salah sampai jauh di kemudian hari ketika sesuatu meledak di wajah Anda, dan kemudian tiba-tiba Anda memiliki kekacauan besar yang mahal untuk diperbaiki dan tidak tahu apa yang salah karena efek yang terlihat sangat jauh dari akar penyebabnya.
Inilah sebabnya mengapa prinsip Fail Fast ada; simpan semua orang yang terlibat sakit kepala dengan menerapkannya ke API Anda.
sumber
Secara umum, kode harus dibangun untuk menjunjung tinggi setidaknya kendala-kendala berikut bilamana praktis:
Ketika diberi input yang benar, hasilkan output yang benar.
Ketika diberikan input yang valid (yang mungkin atau mungkin tidak benar), hasilkan output yang valid (juga).
Ketika diberi input yang tidak valid, proses tanpa efek samping apa pun di luar yang disebabkan oleh input normal atau yang didefinisikan sebagai menandakan kesalahan.
Dalam banyak situasi, program pada dasarnya akan melewati berbagai potongan data tanpa terlalu peduli apakah mereka valid. Jika potongan seperti itu mengandung data yang tidak valid, maka output program kemungkinan akan berisi data yang tidak valid sebagai konsekuensinya. Kecuali jika suatu program secara khusus dirancang untuk memvalidasi semua data, dan menjamin bahwa itu tidak akan menghasilkan output yang tidak valid bahkan ketika diberi input yang tidak valid , program yang memproses outputnya harus memungkinkan untuk kemungkinan data yang tidak valid di dalamnya.
Meskipun memvalidasi data sejak dini sering diinginkan, itu tidak selalu praktis. Antara lain, jika validitas satu bongkahan data tergantung pada isi bongkahan lain, dan jika mayoritas data dimasukkan ke dalam beberapa langkah langkah akan disaring sepanjang jalan, membatasi validasi ke data yang membuatnya melalui semua tahap dapat menghasilkan kinerja yang jauh lebih baik daripada mencoba memvalidasi semuanya.
Selanjutnya, bahkan jika program hanya diharapkan untuk diberikan data yang pra-divalidasi, sering baik untuk memiliki itu menegakkan kendala di atas pula setiap kali praktis. Mengulangi validasi penuh pada setiap langkah pemrosesan sering kali akan menguras kinerja utama, tetapi jumlah terbatas validasi yang diperlukan untuk menegakkan kendala di atas mungkin jauh lebih murah.
sumber
Mari kita bandingkan dua skenario dan coba sampai pada kesimpulan.
Skenario 1 Aplikasi kami mengasumsikan API eksternal akan berperilaku sesuai perjanjian.
Skenario 2 Aplikasi kami menganggap API eksternal dapat berperilaku tidak benar, maka tambahkan tindakan pencegahan.
Secara umum, ada peluang bagi API atau perangkat lunak apa pun untuk melanggar perjanjian; mungkin karena bug atau kondisi yang tidak terduga. Bahkan API mungkin mengalami masalah dalam sistem internal yang menghasilkan hasil yang tidak terduga.
Jika program kami ditulis dengan asumsi API eksternal akan mematuhi perjanjian dan menghindari menambahkan tindakan pencegahan; siapa yang akan menjadi pihak yang menghadapi masalah? Itu akan menjadi kita, orang-orang yang telah menulis kode integrasi.
Misalnya, nilai nol yang telah Anda pilih. Katakanlah, sesuai perjanjian API, respons harus memiliki nilai bukan-nol; tetapi jika tiba-tiba dilanggar program kami akan menghasilkan NPE.
Jadi, saya percaya akan lebih baik untuk memastikan aplikasi Anda memiliki beberapa kode tambahan untuk mengatasi skenario yang tidak terduga.
sumber
Anda harus selalu memvalidasi data yang masuk - yang dimasukkan pengguna atau lainnya - sehingga Anda harus memiliki proses untuk menangani ketika data yang diambil dari API eksternal ini tidak valid.
Secara umum, setiap lapisan di mana sistem ekstra-orgranizasional bertemu harus memerlukan otentikasi, otorisasi (jika tidak didefinisikan hanya dengan otentikasi), dan validasi.
sumber
Secara umum, ya, Anda harus selalu waspada terhadap input yang cacat, tetapi tergantung pada jenis API, "penjaga" artinya berbeda.
Untuk API eksternal ke server, Anda tidak ingin secara tidak sengaja membuat perintah yang membuat crash atau membahayakan kondisi server, jadi Anda harus berjaga-jaga terhadap hal itu.
Untuk API seperti misalnya kelas wadah (daftar, vektor, dll), melempar pengecualian adalah hasil yang sangat baik, mengkompromikan keadaan instance kelas mungkin dapat diterima sampai batas tertentu (misalnya wadah yang diurutkan disediakan dengan operator perbandingan yang rusak tidak akan disortir), bahkan menabrak aplikasi mungkin dapat diterima, tetapi kompromi keadaan aplikasi - misalnya menulis ke lokasi memori acak yang tidak terkait dengan instance kelas - kemungkinan besar tidak.
sumber
Untuk memberikan pendapat yang sedikit berbeda: Saya pikir dapat diterima untuk hanya bekerja dengan data yang Anda berikan, bahkan jika itu melanggar kontraknya. Ini tergantung pada penggunaan: Ini adalah sesuatu yang HARUS menjadi string untuk Anda, atau itu sesuatu yang Anda hanya tampilkan / tidak gunakan dll. Dalam kasus terakhir, cukup terima saja. Saya memiliki API yang hanya membutuhkan 1% dari data yang dikirimkan oleh api lain. Saya tidak peduli dengan data apa yang ada di 99%, jadi saya tidak akan pernah memeriksanya.
Harus ada keseimbangan antara "mengalami kesalahan karena saya tidak cukup memeriksa input saya" dan "Saya menolak data yang valid karena saya terlalu ketat".
sumber
Pandangan saya tentang ini adalah untuk selalu, selalu memeriksa setiap input ke sistem saya. Itu berarti setiap parameter yang dikembalikan dari API harus diperiksa, bahkan jika program saya tidak menggunakannya. Saya cenderung juga memeriksa setiap parameter yang saya kirim ke API untuk kebenaran. Hanya ada dua pengecualian untuk aturan ini, lihat di bawah.
Alasan pengujian adalah bahwa jika karena alasan tertentu API / input salah, program saya tidak dapat mengandalkan apa pun. Mungkin program saya ditautkan ke versi lama API yang melakukan sesuatu yang berbeda dari apa yang saya yakini? Mungkin program saya menemukan bug di program eksternal yang belum pernah terjadi sebelumnya. Atau bahkan lebih buruk, terjadi sepanjang waktu tetapi tidak ada yang peduli! Mungkin program eksternal ditipu oleh peretas untuk mengembalikan barang yang dapat merusak program atau sistem saya?
Dua pengecualian untuk menguji segala sesuatu di dunia saya adalah:
Kinerja setelah pengukuran kinerja yang cermat:
Ketika Anda tidak tahu apa yang harus dilakukan dengan kesalahan
Seberapa cermatnya memeriksa nilai input / pengembalian adalah pertanyaan penting. Sebagai contoh, jika API dikatakan mengembalikan string, saya akan memeriksa bahwa:
tipe data actully adalah string
dan panjang itu antara nilai min dan maks. Selalu periksa string untuk ukuran maksimal yang bisa ditangani oleh program saya (mengembalikan string yang terlalu besar adalah masalah keamanan klasik dalam sistem jaringan).
Beberapa string harus diperiksa untuk karakter atau konten "ilegal" ketika itu relevan. Jika program Anda mungkin mengirim string untuk mengatakan database nanti, sebaiknya periksa serangan database (cari injeksi SQL). Tes ini paling baik dilakukan di perbatasan sistem saya, di mana saya dapat menentukan dari mana serangan itu berasal dan saya bisa gagal lebih awal. Melakukan tes injeksi SQL penuh mungkin sulit ketika string kemudian digabungkan, sehingga tes harus dilakukan sebelum memanggil basis data, tetapi jika Anda dapat menemukan beberapa masalah lebih awal, ini bisa berguna.
Alasan untuk menguji parameter yang saya kirim ke API adalah untuk memastikan bahwa saya mendapatkan hasil yang benar kembali. Sekali lagi, melakukan tes-tes ini sebelum memanggil API mungkin tampak tidak perlu tetapi membutuhkan kinerja yang sangat sedikit dan mungkin menangkap kesalahan dalam program saya. Oleh karena itu tes paling berharga ketika mengembangkan suatu sistem (tetapi saat ini setiap sistem tampaknya dalam pengembangan berkelanjutan). Bergantung pada parameter, tes bisa lebih atau kurang menyeluruh, tetapi saya cenderung menemukan bahwa Anda sering dapat mengatur nilai min dan max yang diijinkan pada sebagian besar parameter yang bisa dibuat oleh program saya. Mungkin string harus selalu memiliki minimal 2 karakter dan panjang maksimum 2000 karakter? Nilai minimum dan maksimum harus berada di dalam apa yang dimungkinkan oleh API karena saya tahu bahwa program saya tidak akan pernah menggunakan rentang penuh dari beberapa parameter.
sumber