ในหน้านี้ คุณจะได้ทราบวิธีสร้างสภาพแวดล้อมแซนด์บ็อกซ์ของคุณเองด้วย Sandbox2 คุณจะได้เรียนรู้วิธีกำหนดนโยบายแซนด์บ็อกซ์ และการปรับเปลี่ยนขั้นสูง แต่มักปรับเปลี่ยนกันโดยทั่วไป ใช้ข้อมูลตรงนี้เป็นแนวทาง ร่วมกับตัวอย่างและเอกสารโค้ดในไฟล์ส่วนหัว
1. เลือกวิธีดำเนินการของแซนด์บ็อกซ์
แซนด์บ็อกซ์จะเริ่มต้นด้วยผู้ดำเนินการ (ดูผู้ดำเนินการแซนด์บ็อกซ์) ซึ่งมีหน้าที่ในการเรียกใช้แซนด์บ็อกซ์ ไฟล์ส่วนหัว executor.h มี API ที่จำเป็นสำหรับวัตถุประสงค์นี้ API มีความยืดหยุ่นสูงและให้คุณเลือกได้ที่เหมาะกับกรณีการใช้งานของคุณมากที่สุด ส่วนต่อไปนี้จะอธิบายถึง 3 วิธีการที่คุณสามารถเลือกได้
วิธีที่ 1: สแตนด์อโลน – เรียกใช้ไบนารีที่เปิดใช้แซนด์บ็อกซ์อยู่แล้ว
นี่เป็นวิธีที่ง่ายที่สุดในการใช้แซนด์บ็อกซ์และเป็นวิธีการที่แนะนำเมื่อคุณต้องการแซนด์บ็อกซ์ทั้งไบนารีซึ่งคุณไม่มีซอร์สโค้ด และยังเป็นวิธีที่ปลอดภัยที่สุดในการใช้แซนด์บ็อกซ์ เนื่องจากไม่มีการเริ่มต้นนอกแซนด์บ็อกซ์ที่อาจส่งผลเสีย
ในข้อมูลโค้ดต่อไปนี้ เรากำหนดเส้นทางของไบนารีที่จะทำการแซนด์บ็อกซ์และอาร์กิวเมนต์ที่เราต้องส่งผ่านไปยัง execve syscall ดังที่คุณเห็นในไฟล์ส่วนหัว executor.h เราจะไม่ระบุค่าสำหรับ envp
ดังนั้นจึงคัดลอกสภาพแวดล้อมจากกระบวนการหลัก โปรดทราบว่าอาร์กิวเมนต์แรกจะเป็นชื่อของโปรแกรมที่จะดำเนินการเสมอ และข้อมูลโค้ดของเราไม่ได้กำหนดอาร์กิวเมนต์อื่น
ตัวอย่างของเมธอดไฟล์ดำเนินการนี้ ได้แก่ static และ tool
#include "sandboxed_api/sandbox2/executor.h"
std::string path = "path/to/binary";
std::vector<std::string> args = {path}; // args[0] will become the sandboxed
// process' argv[0], typically the
// path to the binary.
auto executor = absl::make_unique<sandbox2::Executor>(path, args);
วิธีที่ 2: Sandbox2 Forkserver – แจ้งให้ผู้ดำเนินการทราบว่าต้องแซนด์บ็อกซ์เมื่อใด
วิธีนี้จะมอบความยืดหยุ่นในการไม่ได้อยู่ในแซนด์บ็อกซ์ในระหว่างการเริ่มต้น แล้วเลือกเวลาที่จะเข้าสู่การทำแซนด์บ็อกซ์ด้วยการเรียกใช้ ::sandbox2::Client::SandboxMeHere()
กำหนดให้คุณต้องระบุในโค้ดเมื่อต้องการเริ่มต้นแซนด์บ็อกซ์ และต้องเป็นชุดข้อความเดียว (อ่านเหตุผลในคำถามที่พบบ่อย)
ในข้อมูลโค้ดต่อไปนี้ เราใช้โค้ดเดียวกับที่ระบุไว้ในวิธีที่ 1 ด้านบน อย่างไรก็ตาม เราจะเรียกใช้ set_enable_sandbox_before_exec(false)
เพื่ออนุญาตให้โปรแกรมทำงานแบบไม่ใช้แซนด์บ็อกซ์ในระหว่างการเริ่มต้นทำงาน
#include "sandboxed_api/sandbox2/executor.h"
std::string path = "path/to/binary";
std::vector<std::string> args = {path};
auto executor = absl::make_unique<sandbox2::Executor>(path, args);
executor->set_enable_sandbox_before_exec(false);
เนื่องจากตอนนี้ผู้ดำเนินการมีแซนด์บ็อกซ์ที่ปิดใช้อยู่จนกว่าแซนด์บ็อกซ์จะได้รับการแจ้งเตือน เราจึงต้องสร้างอินสแตนซ์ ::sandbox2::Client
, ตั้งค่าการสื่อสารระหว่างผู้ดำเนินการและแซนด์บ็อกซ์ จากนั้นแจ้งให้ผู้ดำเนินการทราบว่าการเริ่มต้นของเราเสร็จสิ้นแล้ว และเราต้องการเริ่มแซนด์บ็อกซ์ทันทีโดยเรียกใช้ sandbox2_client.SandboxMeHere()
// main() of sandboxee
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, false);
// Set-up the sandbox2::Client object, using a file descriptor (1023).
sandbox2::Comms comms(sandbox2::Comms::kSandbox2ClientCommsFD);
sandbox2::Client sandbox2_client(&comms);
// Enable sandboxing from here.
sandbox2_client.SandboxMeHere();
…
ตัวอย่างของเมธอดไฟล์ดำเนินการนี้คือ crc4 โดยที่ crc4bin.cc
คือแซนด์บ็อกซ์และแจ้งผู้ดำเนินการ (crc4sandbox.cc
) เมื่อควรเข้าสู่แซนด์บ็อกซ์
วิธีที่ 3: Custom Forkserver – เตรียมไบนารี รอคำขอ Fork และใช้แซนด์บ็อกซ์ด้วยตนเอง
โหมดนี้ช่วยให้คุณเริ่มต้นไบนารี เตรียมไบนารีสำหรับแซนด์บ็อกซ์ และทำให้ไฟล์พร้อมใช้งานสำหรับผู้ดำเนินการเมื่อถึงช่วงเวลาหนึ่งในวงจรของไบนารี
ตัวดำเนินการจะส่งคำขอ Fork ไปยังไบนารีของคุณ ซึ่งจะ fork()
(ผ่าน ::sandbox2::ForkingClient::WaitAndFork()
) กระบวนการที่สร้างขึ้นใหม่จะพร้อมสำหรับการแซนด์บ็อกซ์ด้วย ::sandbox2::Client::SandboxMeHere()
#include "sandboxed_api/sandbox2/executor.h"
// Start the custom ForkServer
std::string path = "path/to/binary";
std::vector<std::string> args = {path};
auto fork_executor = absl::make_unique<sandbox2::Executor>(path, args);
fork_executor->StartForkServer();
// Initialize Executor with Comms channel to the ForkServer
auto executor = absl::make_unique<sandbox2::Executor>(
fork_executor->ipc()->GetComms());
โปรดทราบว่าโหมดนี้ค่อนข้างซับซ้อนและใช้ได้เฉพาะในบางกรณีเท่านั้น เช่น เมื่อคุณมีความต้องการหน่วยความจำเต็ม คุณจะได้รับประโยชน์จาก COW แต่ข้อเสียก็คือไม่มี ASLR จริงๆ ตัวอย่างการใช้งานทั่วไปอีกตัวอย่างหนึ่งคือเมื่อแซนด์บ็อกซ์มีการเริ่มการทำงานของ CPU ที่ใช้เวลานานซึ่งเรียกใช้ได้ก่อนที่จะมีการประมวลผลข้อมูลที่ไม่น่าเชื่อถือ
ดูตัวอย่างเมธอดของผู้ดำเนินการนี้ได้ที่ custom_fork
2. สร้างนโยบายแซนด์บ็อกซ์
เมื่อมีผู้ดำเนินการแล้ว คุณอาจต้องการกำหนดนโยบายแซนด์บ็อกซ์สำหรับแซนด์บ็อกซ์ ไม่เช่นนั้น แซนด์บ็อกซ์จะได้รับการปกป้องโดยนโยบายเริ่มต้นของ Syscall เท่านั้น
วัตถุประสงค์ของนโยบายแซนด์บ็อกซ์คือการจำกัดการเรียกใช้ syscall และอาร์กิวเมนต์ที่แซนด์บ็อกซ์สามารถสร้างได้ รวมถึงไฟล์ที่แซนด์บ็อกซ์สามารถเข้าถึงได้ คุณจะต้องเข้าใจ Syscall อย่างละเอียดที่จำเป็นสำหรับโค้ดที่วางแผนจะใช้แซนด์บ็อกซ์ วิธีหนึ่งในการสังเกตการณ์ Syscall คือการเรียกใช้โค้ดด้วยสแต็กของเครื่องมือบรรทัดคำสั่งของ Linux
เมื่อคุณมีรายการ Syscall แล้ว คุณสามารถใช้ PolicyBuilder เพื่อกำหนดนโยบาย PolicyBuilder มาพร้อมกับฟังก์ชันที่ช่วยอำนวยความสะดวกและความช่วยเหลือมากมายที่อนุญาตการดำเนินการทั่วไปหลายอย่าง รายการต่อไปนี้เป็นเพียงตัวอย่างบางส่วนของฟังก์ชันที่ใช้ได้
- เพิ่ม syscall ไว้ในรายการที่อนุญาตสำหรับการเริ่มต้นกระบวนการ:
AllowStaticStartup();
AllowDynamicStartup();
- เพิ่ม syscalls ที่เปิด/read/write* ไว้ในรายการที่อนุญาต ดังนี้
AllowOpen();
AllowRead();
AllowWrite();
- เพิ่ม syscalls ที่เกี่ยวข้องกับการออก/การเข้าถึง/สถานะไปยังรายการที่อนุญาต
AllowExit();
AllowStat();
AllowAccess();
- เพิ่ม syscalls ที่เกี่ยวข้องกับการนอนหลับ/เวลาลงในรายการที่อนุญาต
AllowTime();
AllowSleep();
ฟังก์ชันอำนวยความสะดวกเหล่านี้จะเพิ่มการเรียกใช้ Syscall ที่เกี่ยวข้องลงในรายการได้ทั้งหมด ซึ่งมีข้อดีตรงที่นโยบายเดียวกันสามารถใช้กับสถาปัตยกรรมต่างๆ ที่ syscall ไม่พร้อมใช้งาน (เช่น ARM64 ไม่มี OPEN syscall) แต่มีความเสี่ยงด้านความปลอดภัยเล็กน้อยที่จะเปิดใช้ Sycsall มากเกินกว่าที่จำเป็น ตัวอย่างเช่น AllowOpen() ช่วยให้แซนด์บ็อกซ์สามารถเรียก syscall ที่เกี่ยวข้องแบบเปิด หากต้องการอนุญาต Sycall ที่ระบุอยู่ในรายการที่อนุญาตเพียง 1 รายการ คุณใช้ AllowSyscall();
เพื่ออนุญาต syscall ได้หลายรายการพร้อมกัน คุณสามารถใช้ AllowSyscalls()
ได้
จนถึงขณะนี้ นโยบายจะตรวจสอบเฉพาะตัวระบุ Syscall เท่านั้น หากคุณต้องการปรับปรุงนโยบายเพิ่มเติมและต้องการกำหนดนโยบายที่คุณอนุญาตให้ใช้เฉพาะ syscall ที่มีอาร์กิวเมนต์ที่ระบุ คุณจะต้องใช้ AddPolicyOnSyscall()
หรือ AddPolicyOnSyscalls()
ฟังก์ชันเหล่านี้ไม่เพียงนำรหัส syscall เป็นอาร์กิวเมนต์เท่านั้น แต่ยังใช้ตัวกรอง seccomp-bpf ดิบโดยใช้มาโครตัวช่วย bpf จากเคอร์เนลของ Linux ด้วย โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับ BPF ในเอกสารประกอบของเคอร์เนล หากคุณพบว่าตนเองกำลังเขียนโค้ด BPF ซ้ำๆ และคิดว่าควรมีข้อมูลสรุปความสามารถในการใช้งาน โปรดยื่นคำขอฟีเจอร์
นอกเหนือจากฟังก์ชันที่เกี่ยวข้องกับ syscall แล้ว PolicyBuilder ยังมีฟังก์ชันเกี่ยวกับระบบไฟล์จำนวนมาก เช่น AddFile()
หรือ AddDirectory()
เพื่อเชื่อมโยงไฟล์/ไดเรกทอรีเข้ากับแซนด์บ็อกซ์ คุณจะใช้ตัวช่วยของ AddTmpfs()
เพื่อเพิ่มพื้นที่เก็บไฟล์ชั่วคราวในแซนด์บ็อกซ์ได้
ฟังก์ชันที่มีประโยชน์เป็นพิเศษคือ AddLibrariesForBinary()
ซึ่งจะเพิ่มไลบรารีและ Linker ที่ไบนารีจำเป็นต้องใช้
น่าเสียดายที่การเพิ่ม syscalls ไปยังรายการที่อนุญาตยังคงเป็นงานที่ต้องดำเนินการด้วยตนเอง สร้างนโยบายด้วย syscalls ที่คุณทราบว่าจำเป็นต้องใช้ไบนารีของคุณ และเรียกใช้ด้วยภาระงานทั่วไป หากมีการละเมิดเกิดขึ้น ให้เพิ่มการเรียกใช้ Syscall ให้อนุญาตและดำเนินการซ้ำ หากคุณพบเห็นการละเมิดที่คิดว่าอาจมีความเสี่ยงที่จะเพิ่มลงในรายการที่อนุญาตและโปรแกรมจัดการกับข้อผิดพลาดอย่างเหมาะสม คุณอาจลองทำให้รายการส่งคืนเป็นข้อผิดพลาดโดยใช้ BlockSyscallWithErrno()
แทน
#include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/sandbox2/policybuilder.h"
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
std::unique_ptr<sandbox2::Policy> CreatePolicy() {
return sandbox2::PolicyBuilder()
.AllowSyscall(__NR_read) // See also AllowRead()
.AllowTime() // Allow time, gettimeofday and clock_gettime
.AddPolicyOnSyscall(__NR_write, {
ARG(0), // fd is the first argument of write (argument #0)
JEQ(1, ALLOW), // allow write only on fd 1
KILL, // kill if not fd 1
})
.AddPolicyOnSyscall(__NR_mprotect, {
ARG_32(2), // prot is a 32-bit wide argument, so it's OK to use *_32
// macro here
JNE32(PROT_READ | PROT_WRITE, KILL), // prot must be the RW, otherwise
// kill the process
ARG(1), // len is a 64-bit argument
JNE(0x1000, KILL), // Allow single page syscalls only, otherwise kill
// the process
ALLOW, // Allow for the syscall to proceed, if prot and
// size match
})
// Allow the openat() syscall but always return "not found".
.BlockSyscallWithErrno(__NR_openat, ENOENT)
.BuildOrDie();
}
3. ปรับขีดจำกัด
นโยบายแซนด์บ็อกซ์ป้องกันไม่ให้แซนด์บ็อกซ์เรียกใช้ syscalls ที่ระบุ จึงช่วยลดพื้นที่ในการโจมตี อย่างไรก็ตาม ผู้โจมตีอาจอาจทำให้เกิดผลกระทบที่ไม่พึงประสงค์ได้ ด้วยการเรียกใช้กระบวนการอย่างไม่มีกำหนดหรือใช้ RAM และทรัพยากรอื่นๆ จนหมด
แซนด์บ็อกซ์ทำงานภายใต้ขีดจำกัดการดำเนินการที่เข้มงวดโดยค่าเริ่มต้นเพื่อจัดการกับภัยคุกคามนี้ หากขีดจำกัดเริ่มต้นเหล่านี้ทำให้เกิดปัญหาในการดำเนินการที่ถูกต้องของโปรแกรม คุณปรับขีดจำกัดได้โดยใช้คลาส sandbox2::Limits
โดยการเรียกใช้ limits()
ในออบเจ็กต์ผู้ดำเนินการ
ข้อมูลโค้ดด้านล่างแสดงตัวอย่างการปรับขีดจำกัด ตัวเลือกที่ใช้ได้ทั้งหมดจะบันทึกอยู่ในไฟล์ส่วนหัว limits.h
// Restrict the address space size of the sandboxee to 4 GiB.
executor->limits()->set_rlimit_as(4ULL << 30);
// Kill sandboxee with SIGXFSZ if it writes more than 1 GiB to the filesystem.
executor->limits()->set_rlimit_fsize(1ULL << 30);
// Number of file descriptors which can be used by the sandboxee.
executor->limits()->set_rlimit_nofile(1ULL << 10);
// The sandboxee is not allowed to create core files.
executor->limits()->set_rlimit_core(0);
// Maximum 300s of real CPU time.
executor->limits()->set_rlimit_cpu(300);
// Maximum 120s of wall time.
executor->limits()->set_walltime_limit(absl::Seconds(120));
ดูตัวอย่างการใช้คลาส sandbox2::Limits
ได้ที่เครื่องมือตัวอย่าง
4. เรียกใช้แซนด์บ็อกซ์
ในส่วนก่อนหน้านี้ คุณได้เตรียมสภาพแวดล้อม นโยบาย รวมถึงผู้ดำเนินการและแซนด์บ็อกซ์ ขั้นตอนถัดไปคือการสร้างออบเจ็กต์ Sandbox2
และเรียกใช้
เรียกใช้พร้อมกัน
แซนด์บ็อกซ์จะทำงานแบบซิงโครนัสได้ ซึ่งจะบล็อกจนกว่าจะมีผลลัพธ์ ข้อมูลโค้ดด้านล่างแสดงการสร้างอินสแตนซ์ของออบเจ็กต์ Sandbox2
และการดำเนินการพร้อมกันของออบเจ็กต์ ดูตัวอย่างโดยละเอียดเพิ่มเติมได้ที่แบบคงที่
#include "sandboxed_api/sandbox2/sandbox2.h"
sandbox2::Sandbox2 s2(std::move(executor), std::move(policy));
sandbox2::Result result = s2.Run(); // Synchronous
LOG(INFO) << "Result of sandbox execution: " << result.ToString();
เรียกใช้แบบไม่พร้อมกัน
คุณยังสามารถเรียกใช้แซนด์บ็อกซ์แบบไม่พร้อมกันเพื่อไม่ให้บล็อกจนกว่าจะได้ผลลัพธ์ ซึ่งจะเป็นประโยชน์ เช่น เมื่อสื่อสารกับแซนด์บ็อกซ์ ข้อมูลโค้ดด้านล่างแสดงกรณีการใช้งานนี้ ดูตัวอย่างโดยละเอียดเพิ่มเติมได้ที่ crc4 และ tool
#include "sandboxed_api/sandbox2/sandbox2.h"
sandbox2::Sandbox2 s2(std::move(executor), std::move(policy));
if (s2.RunAsync()) {
// Communicate with sandboxee, use s2.Kill() to kill it if needed
// ...
}
Sandbox2::Result result = s2.AwaitResult();
LOG(INFO) << "Final execution status: " << result.ToString();
5. การสื่อสารกับแซนด์บ็อกซ์
โดยค่าเริ่มต้น ผู้ดำเนินการจะสื่อสารกับแซนด์บ็อกซ์ผ่านคำอธิบายไฟล์ได้ คุณอาจใช้เพียงเท่านี้ก็ได้ หากต้องการแชร์ไฟล์กับแซนด์บ็อกซ์หรืออ่านเอาต์พุตมาตรฐานของแซนด์บ็อกซ์
แต่ส่วนใหญ่แล้วคุณย่อมต้องการตรรกะการสื่อสารที่ซับซ้อนมากขึ้นระหว่างผู้ดำเนินการและแซนด์บ็อกซ์ comms API (ดูไฟล์ส่วนหัว comms.h) สามารถใช้เพื่อส่งจำนวนเต็ม สตริง บัฟเฟอร์ไบต์ โปรโตคอล หรือตัวอธิบายไฟล์
การแชร์ตัวอธิบายไฟล์
การใช้ Inter-Process Communication API (ดูที่ ipc.h) คุณจะใช้ MapFd()
หรือ ReceiveFd()
ได้ดังนี้
ใช้
MapFd()
เพื่อจับคู่ข้อบ่งชี้ไฟล์จากผู้ดำเนินการไปยังแซนด์บ็อกซ์ ซึ่งอาจใช้ในการแชร์ไฟล์ที่เปิดจากผู้ดำเนินการเพื่อใช้ในแซนด์บ็อกซ์ ตัวอย่างการใช้งานสามารถดูได้ในแบบคงที่// The executor opened /proc/version and passes it to the sandboxee as stdin executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
ใช้
ReceiveFd()
เพื่อสร้างปลายทาง Socketpair สามารถใช้เพื่ออ่านเอาต์พุตมาตรฐานหรือข้อผิดพลาดมาตรฐานของแซนด์บ็อกซ์ ดูตัวอย่างการใช้งานได้ในเครื่องมือ// The executor receives a file descriptor of the sandboxee stdout int recv_fd1 = executor->ipc())->ReceiveFd(STDOUT_FILENO);
การใช้ Comms API
Sandbox2 มี Comms API ที่ใช้งานสะดวก นี่เป็นวิธีง่ายๆ ในการแชร์จำนวนเต็ม สตริง หรือบัฟเฟอร์ไบต์ระหว่างตัวดำเนินการและแซนด์บ็อกซ์ ด้านล่างคือข้อมูลโค้ดบางส่วนที่คุณจะพบในตัวอย่าง crc4
หากต้องการเริ่มต้นใช้งาน comms API ก่อนอื่นคุณต้องรับออบเจ็กต์ comms จากออบเจ็กต์ Sandbox2
sandbox2::Comms* comms = s2.comms();
เมื่อออบเจ็กต์การสื่อสารพร้อมใช้งาน ระบบจะส่งข้อมูลไปยังแซนด์บ็อกซ์โดยใช้ฟังก์ชันในตระกูล Send* ได้ ดูตัวอย่างการใช้ comms API ได้ในตัวอย่าง crc4 ข้อมูลโค้ดด้านล่างแสดงตัวอย่างบางส่วน ผู้ดำเนินการส่ง unsigned char buf[size]
พร้อม SendBytes(buf, size)
:
if (!(comms->SendBytes(static_cast<const uint8_t*>(buf), sz))) {
/* handle error */
}
หากต้องการรับข้อมูลจากแซนด์บ็อกซ์ ให้ใช้ฟังก์ชัน Recv*
อย่างใดอย่างหนึ่ง ข้อมูลโค้ดด้านล่างเป็นข้อความที่ตัดตอนมาจากตัวอย่าง crc4 ผู้ดำเนินการจะได้รับการตรวจสอบข้อผิดพลาดในจำนวนเต็มที่ไม่มีเครื่องหมาย 32 บิต:
uint32_t crc4;
if (!(comms->RecvUint32(&crc4))) {
/* handle error */
}
การแชร์ข้อมูลกับบัฟเฟอร์
ฟังก์ชันการแชร์ข้อมูลอีกอย่างคือการใช้ API บัฟเฟอร์เพื่อแชร์ข้อมูลจำนวนมากและเพื่อหลีกเลี่ยงสำเนาที่มีค่าใช้จ่ายสูงที่ส่งกลับไปกลับมาระหว่างผู้ดำเนินการกับแซนด์บ็อกซ์
ผู้ดำเนินการสร้างบัฟเฟอร์ ไม่ว่าจะตามขนาดและข้อมูลที่จะส่ง หรือโดยตรงจากคำอธิบายไฟล์ และส่งไปยังแซนด์บ็อกซ์โดยใช้ comms->SendFD()
ในตัวดำเนินการและ comms->RecvFD()
ในแซนด์บ็อกซ์
คุณจะเห็นด้านของผู้ดำเนินการในข้อมูลโค้ดด้านล่าง แซนด์บ็อกซ์ทำงานแบบไม่พร้อมกันและแชร์ข้อมูลผ่านบัฟเฟอร์กับแซนด์บ็อกซ์:
// start the sandbox asynchronously
s2.RunAsync();
// instantiate the comms object
sandbox2::Comms* comms = s2.comms();
// random buffer data we want to send
constexpr unsigned char buffer_data[] = /* random data */;
constexpr unsigned int buffer_dataLen = 34;
// create sandbox2 buffer
absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
sandbox2::Buffer::CreateWithSize(1ULL << 20 /* 1Mib */);
std::unique_ptr<sandbox2::Buffer> buffer_ptr = std::move(buffer).value();
// point to the sandbox2 buffer and fill with data
uint8_t* buf = buffer_ptr‑>data();
memcpy(buf, buffer_data, buffer_data_len);
// send the data to the sandboxee
comms‑>SendFd(buffer_ptr‑>fd());
ในฝั่งของแซนด์บ็อกซ์ คุณจะต้องสร้างออบเจ็กต์บัฟเฟอร์และอ่านข้อมูลจากตัวบอกไฟล์จากผู้ปฏิบัติการด้วย โดยทำดังนี้
// establish the communication with the executor
int fd;
comms.RecvFD(&fd);
// create the buffer
absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
sandbox2::Buffer::createFromFd(fd);
// get the data
auto buffer_ptr = std::move(buffer).value();
uint8_t* buf = buffer_ptr‑>data();
/* work with the buf object */
6. กำลังออกจากแซนด์บ็อกซ์
คุณต้องปรับวิธีการสิ้นสุดแซนด์บ็อกซ์และแซนด์บ็อกซ์ด้วย ทั้งนี้ขึ้นอยู่กับวิธีที่คุณเรียกใช้แซนด์บ็อกซ์ (ดูขั้นตอนนี้)
การออกจากแซนด์บ็อกซ์ที่ทำงานพร้อมกัน
หากแซนด์บ็อกซ์ทำงานแบบพร้อมกัน การเรียกใช้จะแสดงเมื่อแซนด์บ็อกซ์ทำงานเสร็จแล้วเท่านั้น คุณจึงไม่ต้องดำเนินการใดๆ เพิ่มเติมอีก ข้อมูลโค้ดด้านล่างแสดงสถานการณ์นี้
Sandbox2::Result result = s2.Run();
LOG(INFO) << "Final execution status: " << result.ToString();
การออกจากแซนด์บ็อกซ์ที่ทำงานแบบไม่พร้อมกัน
หากแซนด์บ็อกซ์ทำงานไม่พร้อมกัน มี 2 ตัวเลือกที่สามารถยุติได้ ขั้นแรก คุณสามารถรอให้แซนด์บ็อกซ์เสร็จสมบูรณ์และรับสถานะการดำเนินการขั้นสุดท้าย โดยทำดังนี้
sandbox2::Result result = s2.AwaitResult();
LOG(INFO) << "Final execution status: " << result.ToString();
หรือคุณอาจปิดแซนด์บ็อกซ์ได้ทุกเมื่อ แต่เราขอแนะนำให้เรียกใช้ AwaitResult()
เนื่องจากแซนด์บ็อกซ์อาจสิ้นสุดการทำงานเนื่องจากสาเหตุอื่นในระหว่างนี้
s2.Kill();
sandbox2::Result result = s2.AwaitResult();
LOG(INFO) << "Final execution status: " << result.ToString();
7. ทดสอบ
การใช้งานแซนด์บ็อกซ์ควรมีการทดสอบเช่นเดียวกับโค้ดอื่นๆ การทดสอบแซนด์บ็อกซ์ไม่ได้มีวัตถุประสงค์เพื่อทดสอบความถูกต้องของโปรแกรม แต่เพื่อตรวจสอบว่าโปรแกรมที่ทำแซนด์บ็อกซ์สามารถทำงานโดยไม่มีปัญหาอย่างการละเมิดแซนด์บ็อกซ์หรือไม่ การทำเช่นนี้จะช่วยให้นโยบายแซนด์บ็อกซ์นั้นถูกต้องด้วย
โปรแกรมที่ทำแซนด์บ็อกซ์จะได้รับการทดสอบเช่นเดียวกับที่คุณเรียกใช้ในเวอร์ชันที่ใช้งานจริง ด้วยอาร์กิวเมนต์และไฟล์อินพุตที่โปรแกรมจะประมวลผลตามปกติ
การทดสอบเหล่านี้อาจเป็นการทดสอบง่ายๆ ด้วยการทดสอบ Shell หรือการทดสอบ C++ โดยใช้กระบวนการย่อย ดูตัวอย่างเพื่อหาแรงบันดาลใจ
บทสรุป
ขอขอบคุณที่อ่านข้อมูลจนถึงตอนนี้ เราหวังว่าคุณจะชอบคู่มือของเรา และตอนนี้รู้สึกมั่นใจในการสร้างแซนด์บ็อกซ์ของคุณเองเพื่อช่วยให้ผู้ใช้ของคุณปลอดภัย
การสร้างแซนด์บ็อกซ์และนโยบายเป็นงานที่ยากและมีแนวโน้มที่จะเกิดข้อผิดพลาดเล็กๆ น้อยๆ เพื่อความปลอดภัย เราขอแนะนำให้คุณให้ผู้เชี่ยวชาญด้านความปลอดภัยตรวจสอบนโยบายและโค้ดของคุณ