Topik temu duga representatif

Temu duga pengekodan Go: Bagaimanakah anda mengendalikan nama fail yang tidak dipercayai dengan selamat menggunakan os.Root?

PengekodanSederhana
Pasukan Editorial Offer.ccDiterbitkan Dikemas kini

Soalan

Sebuah pengekstrak arkib menerima nama fail yang tidak dipercayai dan mesti mengekalkan setiap output di bawah satu direktori. Menggunakan Go 1.24+, reka bentuk pelaksanaan yang selamat dan terangkan .., pautan simbolik, TOCTOU, pembersihan sumber, serta batasan rentas platform.

Kehendak soalan dan skop

Sebuah pengekstrak arkib menulis muat naik pengguna ke dalam /srv/uploads/job-42. Nama entri arkib dikawal sepenuhnya oleh pemanggil, yang mungkin menghantar ../../etc/passwd, laluan mutlak, atau pautan simbolik yang menghala ke luar direktori tugasan. Perkhidmatan tersebut mesti mencipta direktori dan fail sambil memastikan setiap operasi kekal berada di dalam direktori tugasan.

Gunakan Go 1.24 atau lebih baharu. Andaikan keistimewaan aplikasi biasa dan Linux dalam pengeluaran, dengan kemungkinan klien Windows atau WASI. Bezakan antara nama fail yang tidak dipercayai dan pemanggil yang dibenarkan secara eksplisit untuk memilih mana-mana direktori output.

Perkara yang diuji oleh penemu duga

  • Sama ada anda mentakrifkan punca (root) dan input kawalan penyerang sebelum memilih API yang dihadkan oleh direktori dan bukannya melakukan penggantian rentetan.
  • Sama ada anda tahu bahawa os.Root menolak .. dan pautan simbolik yang meloloskan diri daripada punca, serta perbezaan jaminan tersebut berbanding filepath.Clean.
  • Sama ada anda mengenali tetingkap TOCTOU semasa menyemak dengan EvalSymlinks dan membukanya kemudian.
  • Sama ada anda mengendalikan Root, deskriptor fail, fail sementara, pembersihan, dan dasar kebenaran.
  • Sama ada anda menyatakan batasan yang melibatkan bind mount, GOOS=js, pelaksanaan WASI, dan laluan yang sangat dalam.

Soalan untuk dijelaskan terlebih dahulu

  1. Adakah operasi ini membaca, menulis, atau kedua-duanya? Akses baca sahaja dan penciptaan memerlukan bendera dan kebenaran OpenFile yang berbeza.
  2. Adakah punca ditetapkan oleh perkhidmatan atau dipilih oleh pemanggil? Jika pemanggil boleh memilih mana-mana direktori, os.Root bukanlah sempadan kotak pasir (sandbox) tambahan.
  3. Bolehkah arkib mencipta pautan simbolik, pautan keras (hard link), fail peranti, atau penamaan semula? Jenis yang tidak dibenarkan mesti ditolak oleh dasar arkib dan bukannya diserahkan kepada Root.
  4. Adakah sasaran GOOS=js atau WASI termasuk dalam skop? Bahan rasmi menerangkan jaminan keselamatan laluan yang berbeza daripada pelaksanaan berasaskan deskriptor Unix.

Rangka jawapan 30 saat

“Saya menjadikan direktori tugasan sebagai satu-satunya punca. Setiap nama arkib dihantar sebagai nama relatif kepada os.Root; saya tidak menggabungkannya dengan rentetan asas dan memanggil os.Create biasa. Penciptaan, pembukaan, dan pemadaman melalui kaedah Root, jadi .. dan pautan simbolik yang cuba meloloskan diri akan gagal. Saya menulis melalui nama sementara dengan kebenaran terhad, kemudian melakukan komit mengikut dasar produk. Ujian merangkumi lintasan, perlumbaan pautan simbolik (symlink races), penciptaan serentak, nama peranti Windows, dan penutupan punca. Root mengekang laluan, tetapi ia tidak menggantikan semakan jenis arkib, pengasingan keistimewaan, atau kawalan bind-mount.”

Penyelaman mendalam langkah demi langkah

1. Tetapkan ketakberubahan keselamatan (security invariant)

“Nama tersebut tidak mengandungi ..” bukanlah takrifan keselamatan. Penyerang boleh menggunakan pautan simbolik atau menggantikan entri direktori antara waktu semakan dan pembukaan. Nyatakan ketakberubahan tersebut dengan tepat: setiap operasi sistem fail yang diterbitkan daripada nama arkib diselesaikan di dalam punca tugasan, dan operasi akan gagal jika perkara itu tidak dapat dibuktikan.

os.OpenRoot Go 1.24 membuka direktori punca. Kaedah seperti Root.Open, Root.Create, Root.OpenFile, Root.Mkdir, dan Root.Stat menerima nama relatif kepada punca tersebut. Pelaksanaan ini menolak .. dan lintasan pautan simbolik ke luar punca. Guna semula satu punca untuk tugasan tersebut, dan tutupnya selepas semua pemegang fail ditutup.

go
func writeEntry(rootDir, name string, data []byte) error {
    root, err := os.OpenRoot(rootDir)
    if err != nil {
        return err
    }
    defer root.Close()

    f, err := root.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)
    if err != nil {
        return err
    }
    defer f.Close()

    _, err = f.Write(data)
    return err
}

Ini menghalang penyelesaian nama daripada meloloskan diri, tetapi kod pengeluaran masih memerlukan had pada saiz fail, bilangan entri, dan kedalaman direktori, serta pemadaman output yang tidak lengkap selepas kegagalan penulisan. O_EXCL bermaksud “jangan tulis ganti fail sedia ada”; ia bukan penyahduplikasian arkib atau ketakberubahan perniagaan (business idempotency).

2. Terangkan mengapa pembersihan rentetan tidak mencukupi

filepath.Clean boleh menormalkan a/../b, tetapi ia tidak dapat membuktikan bahawa a bukan pautan simbolik di luar punca. Menyemak dengan EvalSymlinks dan membukanya kemudian masih mempunyai tetingkap TOCTOU: entri direktori boleh berubah selepas semakan dibuat. filepath.Join yang diikuti oleh os.Create tidak menyediakan sempadan direktori.

Pada Unix, Root menggunakan deskriptor direktori punca dan pembukaan relatif yang dikekang, jadi sempadan tersebut mengambil bahagian dalam operasi dan bukannya bergantung pada semakan berasingan. Laluan relatif dalaman punca dan pautan simbolik dibenarkan; ../ atau pautan simbolik mutlak yang meloloskan diri mesti gagal. Pemanggil masih harus menolak entri pautan simbolik dalam arkib melainkan produk menyokongnya secara eksplisit.

3. Kendalikan jenis arkib dan operasi penulisan

Baca jenis, saiz, kebenaran, dan nama setiap entri sebelum menulis. Tolak fail peranti, FIFO, pautan keras, dan pautan simbolik yang tidak disokong oleh produk. Cipta direktori induk dengan Root.Mkdir atau MkdirAll, kemudian cipta fail biasa dengan Root.OpenFile. Peruntukkan bajet untuk jumlah bait, bait per fail, komponen laluan, dan tugasan serentak.

Urutan penulisan yang lebih selamat ialah “nama sementara → selesai menulis → sahkan → komit atomik.” Nama sementara juga mesti dicipta melalui Root; menulis dalam direktori sementara sistem dan memindahkannya merentasi direktori akan mengubah andaian kebenaran, pelekapan (mount), dan keatoman (atomicity). Jika API Root yang tersedia tidak dapat menyediakan tingkah laku penamaan semula yang diperlukan, jadikan perkara itu sebagai kekangan platform atau produk yang didokumentasikan dan bukannya beralih secara senyap kepada operasi laluan tanpa sekatan.

4. Nyatakan batasan platform dan keistimewaan

Bahan rujukan Go menyatakan bahawa pelaksanaan Unix biasanya menjejaki deskriptor direktori punca, jadi punca yang dinamakan semula masih merujuk kepada direktori asal. Windows menggunakan pemegang (handle) dan menyekat beberapa nama peranti yang dikhaskan. GOOS=js kekurangan keluarga panggilan openat, menyebabkan had TOCTOU dalam pengesahan pautan simbolik; jaminan WASI bergantung pada pelaksanaannya. Root juga tidak menyekat bind mount Linux, fail khas /proc, atau akses fail peranti Unix.

Oleh itu, pengasingan kontena, dasar pelekapan, keistimewaan proses, dan senarai putih jenis arkib adalah kawalan yang berasingan. Jangan huraikan Root sebagai kotak pasir kontena yang lengkap atau menganggap direktori sewenang-wenangnya yang dipilih oleh pemanggil sebagai punca yang dikekang.

5. Bina pengesahan boleh laksana

Uji ../escape, laluan mutlak, pautan simbolik dalaman punca, pautan simbolik di luar punca, nama yang berakhir dengan ../, penciptaan serentak satu fail, entri bersaiz lebih, dan panggilan selepas menutup punca. Go 1.24.3 telah membetulkan keadaan di mana laluan Root yang berakhir dengan ../ boleh membuka direktori induk, jadi CI harus menetapkan (pin) rantaian alat yang mengandungi pembetulan tersebut dan mengekalkan ujian regresi itu.

Pada Linux, gunakan direktori sementara dan pautan simbolik sebenar untuk kes yang diterima dan ditolak. Gunakan -race untuk perlumbaan keadaan kongsi, tetapi jangan anggap ia sebagai bukti sempadan sistem fail. Binari yang dikompilasi silang tidak membuktikan jaminan kernel yang serupa; sertakan setiap GOOS dalam matriks ujian.

Contoh jawapan berkualiti tinggi

“Saya menjadikan direktori tugasan sebagai punca tetap dan membenarkan nama arkib yang tidak dipercayai hanya sebagai laluan relatif yang dihantar kepada os.Root. Pengekstrak menolak fail peranti, pautan keras, dan pautan simbolik yang tidak disokong, serta mengehadkan entri, bait, dan kedalaman direktori. Ia mencipta direktori induk dan fail biasa melalui Root, menulis ke nama sementara di bawah punca yang sama, dan memadamkan output yang tidak lengkap jika gagal. .. dan pautan simbolik yang meloloskan diri ditolak oleh pembukaan yang dikekang itu sendiri, mengelakkan tetingkap TOCTOU semak-kemudian-buka.

Saya tidak akan menganggap ini sebagai kotak pasir yang lengkap: bind mount, keistimewaan, pelekapan kontena, dan dasar arkib kekal berasingan. Ujian Linux menggunakan pautan simbolik sebenar dan penulisan serentak untuk lintasan, penciptaan pendua, pembersihan, dan penamat ../; Windows, WASI, dan GOOS=js mendapat ujian sempadan eksplisit. Jika produk membenarkan direktori sewenang-wenangnya yang dipilih oleh pemanggil, saya membuang andaian Root yang palsu dan menggunakan dasar keistimewaan serta audit yang eksplisit.”

Kesilapan lazim

  • Gejala: filepath.Join(base, name) diikuti oleh os.Create biasa → Mengapa ia gagal: .. dan pautan simbolik boleh diselesaikan di luar asas → Pembetulan: tetapkan punca dan laksanakan setiap operasi melalui Root.
  • Gejala: semak dengan EvalSymlinks, kemudian buka seperti biasa → Mengapa ia gagal: tetingkap TOCTOU masih wujud → Pembetulan: jadikan kekangan tersebut sebahagian daripada operasi buka dan uji sempadan perlumbaan.
  • Gejala: mendakwa bahawa os.Root menyekat setiap kelolosan sistem fail → Mengapa ia gagal: bind mount, fail peranti, dan keistimewaan berada di luar jaminan API tersebut → Pembetulan: tambah kawalan pelekapan, keistimewaan, dan jenis arkib.
  • Gejala: mengabaikan GOOS=js, WASI, dan versi tampalan → Mengapa ia gagal: pelaksanaan platform dan pembetulan keselamatan berbeza-beza → Pembetulan: tetapkan rantaian alat dan bina matriks regresi rentas platform.
  • Gejala: hanya menguji pengekstrakan yang berjaya → Mengapa ia gagal: keselamatan laluan dibuktikan melalui penolakan cubaan meloloskan diri dan pembersihan → Pembetulan: uji lintasan, nama mutlak, pautan simbolik, konflik, had, dan panggilan pada punca yang ditutup.

Soalan susulan dan respons

Soalan susulan 1: Patutkah pautan simbolik dalaman punca dalam arkib dibenarkan?

Tentukan berdasarkan keperluan produk. Jika pautan mesti dikekalkan, sahkan melalui Root bahawa sasaran kekal di dalam punca dan hadkan bilangan serta kedalaman pautan. Jika tugasan hanya mengekstrak fail biasa, menolak pautan simbolik adalah lebih mudah untuk diaudit. Walau apa pun, Root tidak menggantikan senarai putih jenis arkib.

Soalan susulan 2: Bagaimanakah anda mengendalikan bind mount Linux?

Root tidak menyediakan pengasingan bind-mount. Letakkan direktori tugasan pada sistem fail khusus melalui dasar pelekapan kontena atau hos, hadkan keistimewaan aplikasi, dan tolak pelekapan yang tidak dipercayai dalam semakan penggunaan (deployment). Jika model ancaman merangkumi penyerang dengan keistimewaan, gunakan kotak pasir yang lebih kukuh atau pekerja terasing dan bukannya menambah lebih banyak semakan rentetan.

Soalan susulan 3: Mengapa tidak menulis fail sementara dalam direktori temp sistem dan memindahkannya ke dalam punca?

Pemindahan merentasi direktori mengubah andaian kebenaran, pelekapan, dan keatoman, dan fail sementara tersebut mungkin bocor. Cipta nama sementara melalui Root dan selesaikan penulisan serta komit di bawah punca yang sama. Jika platform tidak dapat menyediakan operasi atomik yang diperlukan, dokumentasikan ciri yang dikurangkan atau pelaksanaan keserasian yang diaudit dan bukannya kembali secara senyap kepada penggabungan laluan biasa.

Soalan susulan 4: Bolehkah GOOS=js didakwa mempunyai tahap keselamatan yang sama?

Tidak. Penjelasan rasmi menyatakan bahawa sasaran tersebut kekurangan keluarga openat dan mempunyai batasan TOCTOU dalam pengesahan pautan simbolik. Hadkan ciri untuk sasaran tersebut, bergantung pada kotak pasir masa jalanan (runtime), atau alihkan pemprosesan fail yang tidak dipercayai ke pelayan dengan kekangan direktori yang lebih kukuh. API dan dokumentasi mesti mendedahkan perbezaan ini.

Sumber awam

Soalan berkaitan

Alat temu duga berkaitan

Gunakan Tangkapan Skrin untuk gesaan pengekodan

Tangkap soalan, kemudian selesaikan kekangan, penyelesaian, kod, kes pinggir dan kerumitan mengikut urutan.

Lihat alat