Tambahkan Header Keamanan Dengan Lambda@Edge dan Terraform di AWS S3/CloudFront

Diterbitkan: 2021-04-24

Saat Anda masuk ke https://app.sendgrid.com untuk memeriksa detail akun Anda atau mengunjungi https://mc.sendgrid.com untuk Kampanye Pemasaran, Anda mengunjungi aplikasi web front-end kami yang dihosting di bucket AWS S3 dengan Distribusi CloudFront di atasnya.

Aplikasi web kami pada dasarnya terdiri dari file JavaScript dan CSS yang diperkecil dan dipisah kode, file HTML, dan file gambar yang diunggah ke bucket S3 sebagai cache CloudFront dan menyajikannya kepada pengguna kami. Setiap lingkungan aplikasi web kami mulai dari pengujian, staging, dan produksi memiliki bucket S3 dan distribusi CloudFront yang terpisah.

Infrastruktur AWS S3 dan CloudFront ini berfungsi dengan baik untuk aplikasi web kami dalam skala besar dalam menghosting file melalui jaringan pengiriman konten, tetapi konfigurasi awal kami tidak memiliki perlindungan yang lebih ketat dalam bentuk header keamanan.

Menambahkan header keamanan ini akan mencegah pengguna dari serangan, seperti skrip lintas situs, sniffing MIME, clickjacking, injeksi kode, dan serangan man-in-the-middle yang terkait dengan protokol tidak aman. Jika dibiarkan, ini akan memiliki konsekuensi serius bagi data pelanggan kami dan kepercayaan perusahaan kami untuk dapat memberikan pengalaman yang aman di web.

Sebelum kami meneliti cara menambahkan tajuk ini, pertama-tama kami mundur selangkah untuk melihat di mana kami berada. Setelah menjalankan URL aplikasi web kami melalui situs web pemindaian tajuk keamanan , kami secara mengejutkan menerima nilai yang gagal tetapi melihat daftar tajuk yang berguna untuk dilihat seperti yang ditunjukkan di bawah ini.

Seperti yang Anda lihat, ada banyak ruang untuk perbaikan. Kami meneliti cara mengonfigurasi sumber daya AWS S3 dan CloudFront kami untuk merespons kembali dengan header keamanan untuk mengurangi risiko dan kerentanan yang disebutkan.

Pada tingkat tinggi, kita dapat melakukannya dengan membuat fungsi Lambda@Edge yang mengubah header respons asal untuk menambahkan header keamanan yang diinginkan sebelum file aplikasi web kembali ke browser pengguna.

Strateginya adalah pertama-tama menguji menghubungkan berbagai hal secara manual melalui AWS Console. Kemudian, kami akan menempatkan konfigurasi ini di Terraform untuk menyimpan bagian infrastruktur ini dalam kode untuk referensi di masa mendatang dan kemampuan berbagi di seluruh tim dan aplikasi lain.

Jenis header keamanan apa yang ingin kami tambahkan?

Sebagai bagian dari rekomendasi dari tim Keamanan Produk kami, kami ditugaskan untuk menambahkan header keamanan seperti “Keamanan Transportasi Ketat” dan “Opsi Bingkai-X.” Kami menyarankan Anda juga memeriksa sumber daya seperti Cheatsheet Keamanan Web MDN untuk mendapatkan kecepatan. Berikut adalah ringkasan singkat dari header keamanan yang dapat Anda terapkan ke aplikasi web Anda.

Ketat-Transportasi-Keamanan (HSTS)

Ini untuk memberikan petunjuk kepada browser untuk mengakses aplikasi web Anda melalui HTTPS daripada HTTP.

Kebijakan-Keamanan-Konten (CSP)

Ini untuk menyetel daftar yang diizinkan secara eksplisit pada jenis sumber daya yang Anda muat atau sambungkan ke dalam aplikasi web Anda, seperti skrip, gambar, gaya, font, permintaan jaringan, dan iframe. Ini adalah yang paling sulit untuk kami siapkan karena kami memiliki skrip pihak ketiga, gambar, gaya, dan titik akhir API untuk direkam secara eksplisit dalam kebijakan ini.

Tip: Gunakan header Content-Security-Policy-Report-Only untuk membantu Anda melakukan pengujian di lingkungan tertentu. Jika sumber daya tertentu melanggar kebijakan, kami mengamati keluaran konsol yang bermanfaat dari sumber daya yang perlu kami izinkan dalam kebijakan kami.

Jika Anda ingin menghindari kecelakaan lucu dengan layar kosong dan aplikasi web gagal dimuat, kami sangat menyarankan Anda bereksperimen dengan kebijakan Anda dalam mode hanya laporan terlebih dahulu dan melakukan pengujian menyeluruh sebelum merasa cukup percaya diri untuk menerapkan kebijakan keamanan ini dalam produksi.

X-Content-Type-Options

Ini untuk memelihara dan memuat aset dengan tipe MIME yang benar di halaman web Anda.

X-Frame-Options

Ini untuk memberikan aturan tentang bagaimana aplikasi web Anda berpotensi dimuat dalam iframe.

X-XSS-Perlindungan

Ini menghentikan pemuatan halaman jika serangan skrip lintas situs terdeteksi di browser tertentu.

Kebijakan-Perujuk

Ini mengatur bagaimana tajuk "Perujuk" dengan informasi tentang asal permintaan diteruskan saat mengikuti tautan ke situs atau sumber eksternal.

Dengan mempertimbangkan header keamanan ini, mari kembali ke cara kita menyiapkan distribusi CloudFront hari ini dan bagaimana fungsi Lambda@Edge akan membantu kita mencapai tujuan.

Menggunakan Lambda@Edge dengan distribusi CloudFront kami

Untuk distribusi CloudFront kami, kami mengatur hal-hal seperti:

    • Sertifikat SSL untuk domain yang akan dilampirkan di atas URL CloudFront seperti https://app.sendgrid.com
    • Asal ember S3
    • Grup asal dengan bucket utama dan replika untuk failover otomatis
    • Perilaku cache

Perilaku cache ini, khususnya, memungkinkan kami untuk mengontrol berapa lama kami ingin respons untuk jenis jalur dan file tertentu di-cache di server edge di seluruh dunia. Selain itu, perilaku cache juga memberi kami cara untuk memicu fungsi AWS Lambda sebagai respons terhadap berbagai peristiwa, seperti permintaan asal dan respons asal. Anda dapat menganggap fungsi AWS Lambda sebagai kode spesifik yang Anda tentukan yang akan berjalan sebagai respons terhadap peristiwa tertentu.

Dalam kasus kami, kami dapat mengubah header respons asal sebelum di-cache oleh server edge. Fungsi Lambda@Edge kami akan menambahkan header keamanan khusus ke respons asal sebelum akhirnya kembali ke server edge dan sebelum pengguna akhir menerima file JavaScript, CSS, dan HTML dengan header tersebut.

Kami memodelkan pendekatan kami setelah posting blog AWS ini dan memperluasnya agar lebih mudah membuat perubahan pada Kebijakan Keamanan Konten tertentu. Anda dapat memodelkan fungsi Lambda@Edge Anda setelah cara kami mengatur berbagai hal dalam membuat daftar untuk skrip, gaya, dan menghubungkan sumber. Fungsi ini secara efektif memodifikasi header respons asal CloudFront dan menambahkan setiap header keamanan dengan nilai tertentu ke respons sebelum kembali dengan memanggil fungsi panggilan balik yang disediakan seperti yang ditunjukkan di bawah ini.

Bagaimana kita menguji fungsi Lambda@Edge ini?

Sebelum Anda secara resmi mengubah cara pengembalian aset dengan header keamanan, Anda harus memverifikasi fungsi tersebut berfungsi setelah Anda mengonfigurasi semuanya secara manual melalui AWS Console. Sangat penting bahwa aplikasi web Anda harus dapat memuat dan berfungsi dengan baik dengan header keamanan yang ditambahkan ke respons jaringan Anda. Hal terakhir yang ingin Anda dengar adalah pemadaman tak terduga yang terjadi karena header keamanan, jadi ujilah secara menyeluruh di lingkungan pengembangan Anda.

Penting juga untuk mengetahui apa sebenarnya yang akan Anda tulis dalam kode Terraform nanti untuk menyimpan konfigurasi ini di basis kode Anda. Jika Anda tidak tahu tentang Terraform, ini memberi Anda cara untuk menulis dan mengelola infrastruktur cloud Anda melalui kode.

Tip: Lihat dokumen Terraform untuk melihat apakah itu dapat membantu Anda mempertahankan konfigurasi kompleks Anda tanpa perlu mengingat semua langkah yang Anda lakukan di konsol cloud.

Cara memulai di AWS Console

Mari kita mulai dengan cara mengatur semuanya secara manual melalui AWS Console.

  1. Pertama, Anda perlu membuat fungsi Lambda@Edge di wilayah "us-east-1". Pergi ke halaman layanan Lambda, kami akan mengklik "Buat Fungsi" dan beri nama seperti "testSecurityHeaders1."

2. Anda dapat menggunakan peran yang ada dengan izin untuk menjalankan fungsi di server edge atau Anda dapat menggunakan salah satu templat kebijakan peran mereka, seperti “Izin Lambda@Edge Dasar…,” dan beri nama “lambdaedgeroletest.”

3. Setelah membuat fungsi dan peran Lambda pengujian Anda, Anda akan melihat sesuatu seperti ini di mana Anda akan melihat tombol "Tambah Pemicu" untuk fungsi tersebut. Di sinilah Anda akhirnya akan mengaitkan Lambda dengan perilaku cache distribusi CloudFront yang dipicu pada peristiwa respons asal.

4. Selanjutnya, Anda perlu mengedit kode fungsi dengan kode header keamanan yang kami buat sebelumnya dan tekan "Simpan."

5. Setelah menyimpan kode fungsi, mari kita uji apakah fungsi Lambda Anda berfungsi dengan menggulir ke atas dan menekan tombol "Uji". Anda akan membuat peristiwa uji bernama "samplecloudfrontresponse" menggunakan templat peristiwa "cloudfront-modify-response-header" untuk mengejek peristiwa respons asal CloudFront yang sebenarnya dan untuk melihat bagaimana fungsi Anda berjalan melawannya.

Anda akan melihat hal-hal seperti objek header "cf.response" yang akan diubah oleh kode fungsi Lambda Anda.

6. Setelah membuat test event, Anda akan mengklik tombol “Test” lagi dan akan melihat bagaimana fungsi Lambda bekerja melawannya. Ini harus berjalan dengan sukses dengan log yang menampilkan respons yang dihasilkan dengan header keamanan tambahan seperti ini.

Bagus, fungsi Lambda sepertinya menambahkan header keamanan ke respons dengan benar!

7. Mari kembali ke area "Desainer" dan klik tombol "Tambah Pemicu" sehingga Anda dapat mengaitkan fungsi Lambda dengan perilaku cache distribusi CloudFront Anda pada peristiwa respons asal. Pastikan untuk memilih pemicu “CloudFront” dan klik tombol “Deploy to Lambda@Edge”.

8. Selanjutnya, pilih distribusi CloudFront (dalam contoh kami, kami menghapus input di sini untuk alasan keamanan) dan perilaku cache untuk dikaitkan dengannya.

Anda kemudian memilih perilaku cache "*" dan memilih peristiwa "Origin response" untuk mencocokkan semua jalur permintaan ke distribusi CloudFront Anda dan untuk memastikan fungsi Lambda selalu berjalan untuk semua respons asal.

Anda kemudian mencentang pengakuan sebelum mengklik "Sebarkan" untuk secara resmi menerapkan fungsi Lambda Anda.

9. Setelah berhasil mengaitkan fungsi Lambda Anda dengan semua perilaku cache distribusi CloudFront yang relevan, Anda akan melihat sesuatu yang mirip dengan ini di area "Desainer" dasbor Lambda di mana Anda dapat melihat pemicu CloudFront dan memiliki opsi untuk melihat atau menghapusnya.

Membuat perubahan pada kode Lambda Anda

Kapan pun Anda mungkin perlu membuat perubahan pada kode Lambda Anda, kami menyarankan:

    1. Publikasikan versi baru melalui tarik-turun tombol "Tindakan"
    2. Hapus pemicu pada versi yang lebih lama (Anda dapat mengklik menu tarik-turun "Kualifikasi" untuk melihat semua versi Lambda Anda)
    3. Kaitkan pemicu dengan nomor versi terbaru yang baru saja Anda terbitkan

Saat menerapkan Lambda Anda untuk pertama kalinya atau setelah memublikasikan versi baru Lambda Anda dan mengaitkan pemicu dengan versi Lambda yang lebih baru, Anda mungkin tidak langsung melihat header keamanan di respons untuk aplikasi web Anda. Ini karena cara server tepi di CloudFront menyimpan respons. Bergantung pada berapa lama Anda mengatur time-to-live dalam perilaku cache Anda, Anda mungkin harus menunggu beberapa saat untuk melihat header keamanan baru kecuali Anda melakukan pembatalan cache di distribusi CloudFront Anda yang terpengaruh.

Setelah menerapkan kembali perubahan Anda ke fungsi Lambda Anda, seringkali perlu waktu untuk membersihkan cache (tergantung pada pengaturan cache CloudFront Anda) sebelum tanggapan Anda memiliki tweak terbaru untuk header keamanan Anda.

Tip: Untuk menghindari banyak menyegarkan halaman atau tidak yakin apakah perubahan Anda berhasil, mulai pembatalan cache CloudFront untuk mempercepat proses mengosongkan cache sehingga Anda dapat melihat header keamanan yang diperbarui.

Buka halaman Layanan CloudFront Anda, tunggu hingga status distribusi CloudFront Anda di-deploy, artinya semua asosiasi lambda telah selesai dan diterapkan, dan buka tab “Invalidations”. Klik "Buat Pembatalan" dan masukkan "/*" sebagai jalur objek untuk membatalkan semua hal dalam cache dan tekan "Batalkan." Ini tidak akan memakan waktu terlalu lama, dan setelah selesai, menyegarkan aplikasi web Anda akan melihat perubahan header keamanan terbaru.

Saat Anda mengulangi header keamanan Anda berdasarkan apa yang Anda temukan sebagai pelanggaran atau kesalahan dalam aplikasi web Anda, Anda dapat mengulangi proses ini:

    • Menerbitkan versi fungsi Lambda baru
    • Menghapus pemicu pada versi Lambda lama
    • Mengaitkan pemicu pada versi baru
    • Cache membatalkan distribusi CloudFront Anda
    • Menguji aplikasi web Anda
    • Mengulangi sampai Anda merasa percaya diri dan hal-hal aman berfungsi seperti yang diharapkan tanpa halaman kosong, permintaan API yang gagal, atau kesalahan keamanan konsol

Setelah semuanya stabil, Anda dapat secara opsional beralih ke Terraforming yang baru saja Anda lakukan secara manual ke dalam konfigurasi kode, dengan asumsi Anda memiliki Terraform yang terintegrasi dengan akun AWS Anda. Kami tidak akan membahas cara menyiapkan Terraform dari awal, tetapi kami akan menunjukkan kepada Anda cuplikan seperti apa tampilan kode Terraform.

Terraforming Lambda@Edge yang dipicu oleh distribusi CloudFront kami

Setelah mengulangi fungsi Lambda@Edge untuk header keamanan di wilayah "us-east-1", kami ingin menambahkan ini ke basis kode Terraform kami untuk pemeliharaan kode dan kontrol versi di masa mendatang.

Untuk semua perilaku cache yang sudah kami terapkan, kami harus mengaitkan perilaku cache dengan fungsi Lambda@Edge, yang dipicu oleh peristiwa respons asal.

Langkah-langkah berikut mengasumsikan Anda sudah memiliki sebagian besar distribusi CloudFront dan bucket S3 yang dikonfigurasi melalui Terraform. Kami akan fokus pada modul dan properti utama yang terkait dengan Lambda@Edge dan menambahkan pemicu ke perilaku cache distribusi CloudFront. Kami tidak akan menjelaskan cara mengatur bucket S3 Anda dan pengaturan distribusi CloudFront lainnya dari awal melalui Terraform, tetapi kami harap Anda dapat melihat tingkat upaya untuk mencapainya sendiri.

Saat ini kami memecah sumber daya AWS kami ke dalam folder modul terpisah dan meneruskan variabel ke dalam modul tersebut untuk fleksibilitas dalam konfigurasi kami. Kami memiliki folder apply dengan subfolder development dan production dan masing-masing memiliki file main.tf sendiri di mana kami memanggil modul ini dengan variabel input tertentu untuk membuat instance atau memodifikasi sumber daya AWS kami.

Subfolder tersebut juga masing-masing memiliki folder lambdas tempat kami menyimpan kode Lambda kami seperti file security_headers_lambda.js . security_headers_lambda.js memiliki kode yang sama dengan yang kami gunakan dalam fungsi Lambda kami ketika kami menguji secara manual, kecuali kami juga menyimpannya di basis kode kami untuk kami zip dan unggah melalui Terraform.

1. Pertama, kita memerlukan modul yang dapat digunakan kembali untuk meng-zip file Lambda kita sebelum diunggah dan diterbitkan sebagai versi lain dari fungsi Lambda@Edge kita. Ini mengambil jalur ke folder Lambda kami yang menyimpan fungsi Lambda Node.js akhirnya.

2. Selanjutnya, kami menambahkan ke modul CloudFront kami yang sudah ada, yang membungkus bucket S3, kebijakan, dan sumber daya distribusi CloudFront dengan juga membuat sumber daya Lambda yang dibuat dari file Lambda yang di-zip. Karena keluaran modul zip Lambda diteruskan sebagai variabel ke dalam modul CloudFront untuk menyiapkan sumber daya Lambda, kita perlu menentukan wilayah penyedia AWS sebagai “us-east-1” dan dengan kebijakan peran yang berfungsi seperti ini.

3. Di dalam modul CloudFront, kami kemudian mengaitkan fungsi Lambda@Edge ini dengan perilaku cache distribusi CloudFront seperti yang ditunjukkan di bawah ini.

4. Terakhir, menggabungkan semuanya dalam file main.tf folder apply/development atau apply/production , kita memanggil semua modul ini dan memasukkan output yang tepat sebagai variabel ke dalam modul CloudFront kita seperti yang ditunjukkan di sini.

Tweak konfigurasi ini pada dasarnya menangani langkah-langkah manual yang kami lakukan di bagian sebelumnya untuk memperbarui kode Lambda dan mengaitkan versi yang lebih baru dengan perilaku cache CloudFront dan pemicu untuk peristiwa respons asal. Woo hoo! Tidak perlu melalui atau mengingat langkah-langkah Konsol AWS selama kami menerapkan perubahan ini pada sumber daya kami.

Bagaimana kita meluncurkan ini dengan aman di lingkungan yang berbeda?

Saat kami pertama kali mengaitkan fungsi Lambda@Edge dengan distribusi CloudFront pengujian kami, kami segera menyadari bagaimana aplikasi web kami tidak lagi dimuat dengan benar. Hal ini terutama disebabkan oleh bagaimana kami mengimplementasikan header Content-Security-Policy kami dan bagaimana header tersebut tidak mencakup semua sumber daya yang kami muat di aplikasi kami. Header keamanan lainnya memiliki risiko yang lebih kecil dalam hal memblokir aplikasi kita agar tidak dimuat. Di masa mendatang, kami akan fokus untuk meluncurkan header keamanan dengan iterasi lebih lanjut untuk menyempurnakan header Kebijakan-Keamanan-Konten.

Seperti yang disebutkan sebelumnya, kami menemukan bagaimana kami dapat memanfaatkan header Content-Security-Policy-Report-Only sebagai gantinya untuk meminimalkan risiko saat kami mengumpulkan lebih banyak domain sumber daya untuk ditambahkan di setiap kebijakan kami.

Dalam mode hanya laporan ini, kebijakan akan tetap berjalan di browser dan mengeluarkan pesan kesalahan konsol dari setiap pelanggaran terhadap kebijakan. Namun, itu tidak akan langsung memblokir skrip dan sumber tersebut, sehingga aplikasi web kami tetap dapat berjalan seperti biasa. Terserah kami untuk terus menelusuri seluruh aplikasi web untuk memastikan kami tidak melewatkan sumber penting apa pun dalam kebijakan kami atau jika tidak, hal itu akan berdampak negatif pada pelanggan dan tim dukungan kami.

Untuk setiap lingkungan, Anda dapat meluncurkan header keamanan Lambda seperti berikut:

    • Publikasikan perubahan pada Lambda Anda baik secara manual atau melalui paket Terraform dan terapkan perubahan ke lingkungan dengan header keamanan lain dan header Content-Security-Policy-Report-Only terlebih dahulu.
    • Tunggu hingga status distribusi CloudFront Anda diterapkan sepenuhnya dengan Lambda yang terkait dengan perilaku cache.
    • Lakukan pembatalan cache pada distribusi CloudFront Anda jika header keamanan sebelumnya masih muncul, atau perlu waktu terlalu lama agar perubahan Anda saat ini muncul di browser Anda.
    • Kunjungi dan jelajahi halaman aplikasi web Anda dengan alat pengembang terbuka, pindai konsol untuk setiap pesan kesalahan konsol "Laporkan Saja..." untuk meningkatkan header Kebijakan-Keamanan-Konten Anda.
    • Buat perubahan pada kode Lambda Anda untuk memperhitungkan pelanggaran yang dilaporkan tersebut.
    • Ulangi dari langkah pertama sampai Anda merasa cukup percaya diri untuk mengubah header Anda dari Content-Security-Policy-Report-Only menjadi Content-Security-Policy, yang berarti lingkungan akan menerapkannya.

Meningkatkan skor header keamanan kami

Setelah berhasil menerapkan perubahan Terraform ke lingkungan kami dan membatalkan cache CloudFront, kami menyegarkan halaman di aplikasi web kami. Kami membiarkan alat pengembang terbuka untuk melihat header keamanan, seperti HSTS, CSP, dan yang lainnya dalam respons jaringan kami, seperti header keamanan yang ditunjukkan di bawah ini.

Kami juga menjalankan aplikasi web kami melalui laporan pemindaian header keamanan seperti yang ada di situs ini . Hasilnya, kami menyaksikan peningkatan yang luar biasa (peringkat A!) dari nilai yang sebelumnya gagal, dan Anda dapat mencapai peningkatan serupa setelah mengubah pengaturan S3/CloudFront Anda untuk memiliki header keamanan.

Maju dengan tajuk keamanan

Setelah secara manual mengatur header keamanan melalui AWS Console atau berhasil melakukan Terraforming solusi dan menerapkan perubahan ke setiap lingkungan Anda, Anda sekarang memiliki dasar yang bagus untuk iterasi lebih lanjut dan meningkatkan header keamanan yang ada di masa mendatang.

Bergantung pada evolusi aplikasi web Anda, Anda mungkin harus membuat header Content-Security-Policy Anda lebih spesifik dalam hal sumber daya yang diizinkan untuk keamanan yang lebih ketat. Atau, Anda mungkin perlu menambahkan tajuk baru seluruhnya untuk tujuan terpisah atau untuk mengisi lubang keamanan lain.

Dengan perubahan mendatang pada header keamanan Anda di fungsi Lambda@Edge, Anda dapat mengikuti strategi rilis serupa per lingkungan untuk memastikan aplikasi web Anda aman dari serangan berbahaya di web dan tetap berfungsi tanpa pengguna Anda menyadari perbedaannya.

Untuk lebih banyak artikel yang ditulis oleh Alfred Lucero, kunjungi halaman penulis blognya: https://sendgrid.com/blog/author/alfred/