इस पेज पर, Sandbox2 की मदद से अपना सैंडबॉक्स एनवायरमेंट बनाने का तरीका बताया गया है. आपको सैंडबॉक्स नीति को तय करने का तरीका बताया जाएगा. साथ ही, कुछ ऐडवांस, लेकिन सामान्य बदलावों के बारे में भी बताया जाएगा. यहां दी गई जानकारी का इस्तेमाल गाइड के तौर पर करें. साथ ही, हेडर फ़ाइलों में दिए गए उदाहरणों और कोड के दस्तावेज़ों का इस्तेमाल करें.
1. सैंडबॉक्स एक्ज़ीक्यूटर का तरीका चुनना
सैंडबॉक्स की प्रोसेस, एक्ज़ीक्यूटर (सैंडबॉक्स एक्ज़ीक्यूटर देखें) से शुरू होती है. यह Sandboxee को चलाने के लिए ज़िम्मेदार होता है. executor.h हेडर फ़ाइल में, इस काम के लिए ज़रूरी एपीआई शामिल होता है. यह एपीआई बहुत लचीला है. इससे आपको अपने इस्तेमाल के हिसाब से सबसे सही विकल्प चुनने में मदद मिलती है. नीचे दिए गए सेक्शन में, तीन अलग-अलग तरीकों के बारे में बताया गया है. इनमें से कोई एक तरीका चुना जा सकता है.
पहला तरीका: स्टैंड-अलोन – सैंडबॉक्सिंग की सुविधा पहले से चालू होने पर, किसी बाइनरी को एक्ज़ीक्यूट करना
सैंडबॉक्सिंग का इस्तेमाल करने का यह सबसे आसान तरीका है. अगर आपको किसी ऐसे पूरे बाइनरी को सैंडबॉक्स करना है जिसका सोर्स कोड आपके पास नहीं है, तो हमारा सुझाव है कि आप इस तरीके का इस्तेमाल करें. यह सैंडबॉक्सिंग का इस्तेमाल करने का सबसे सुरक्षित तरीका भी है, क्योंकि इसमें सैंडबॉक्स न किए गए किसी भी इनिशियलाइज़ेशन का इस्तेमाल नहीं किया जाता. इससे कोई बुरा असर नहीं पड़ता.
यहां दिए गए कोड स्निपेट में, हम सैंडबॉक्स किए जाने वाले बाइनरी का पाथ और 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);
दूसरा तरीका: Sandbox2 Forkserver – एक्ज़ीक्यूटर को यह बताना कि उसे कब सैंडबॉक्स किया जाना है
इस तरीके से, शुरू होने के दौरान सैंडबॉक्स से बाहर रहने की सुविधा मिलती है. इसके बाद, ::sandbox2::Client::SandboxMeHere()
को कॉल करके, सैंडबॉक्स में शामिल होने का समय चुना जा सकता है. इसके लिए, आपको कोड में यह तय करना होगा कि सैंडबॉक्सिंग कब शुरू करनी है. साथ ही, यह सिंगल-थ्रेड होना चाहिए. इसके बारे में अक्सर पूछे जाने वाले सवालों में पढ़ें.
नीचे दिए गए कोड स्निपेट में, हमने उसी कोड का इस्तेमाल किया है जिसके बारे में ऊपर पहले तरीके में बताया गया है. हालांकि, प्रोग्राम को शुरू करने के दौरान, सैंडबॉक्स से बाहर चलाने की अनुमति देने के लिए, हम 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
) को सूचना देता है कि इसे सैंडबॉक्स में कब शामिल होना चाहिए.
तीसरा तरीका: कस्टम फ़ोर्कसर्वर – एक बाइनरी तैयार करें, फ़ोर्क के अनुरोधों का इंतज़ार करें, और सैंडबॉक्स को खुद मैनेज करें
इस मोड की मदद से, बाइनरी शुरू की जा सकती है, उसे सैंडबॉक्सिंग के लिए तैयार किया जा सकता है. साथ ही, बाइनरी के लाइफ़साइकल के किसी खास समय पर, उसे एक्ज़ीक्यूटर के लिए उपलब्ध कराया जा सकता है.
एक्ज़ीक्यूटर, आपके बाइनरी को फ़ोर्क करने का अनुरोध भेजेगा. इससे 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 नहीं है. इस्तेमाल का एक और सामान्य उदाहरण तब होता है, जब सैंडबॉक्स किए गए ऐप्लिकेशन में सीपीयू का ज़्यादा इस्तेमाल करने वाला लंबा इनिशलाइज़ेशन होता है. इसे ऐसे डेटा को प्रोसेस करने से पहले चलाया जा सकता है जिस पर भरोसा नहीं किया जा सकता.
इस एक्ज़ीक्यूटर तरीके के उदाहरण के लिए, custom_fork देखें.
2. सैंडबॉक्स की नीति बनाना
एक्ज़ीक्यूटर तय करने के बाद, आपको सैंडबॉक्स के लिए सैंडबॉक्स की नीति तय करनी होगी. ऐसा न होने पर, सैंडबॉक्सी को सिर्फ़ डिफ़ॉल्ट सिसकॉल नीति के तहत सुरक्षित किया जाता है.
सैंडबॉक्स नीति का मकसद, उन सिसकॉल और तर्कों पर पाबंदी लगाना है जिन्हें सैंडबॉक्सी बना सकता है. साथ ही, उन फ़ाइलों पर पाबंदी लगाना है जिन्हें वह ऐक्सेस कर सकता है. आपको उस कोड के लिए ज़रूरी सिस्टम कॉल के बारे में पूरी जानकारी होनी चाहिए जिसे आपको सैंडबॉक्स करना है. सिस्टम कॉल को मॉनिटर करने का एक तरीका यह है कि कोड को Linux के कमांड-लाइन टूल strace के साथ चलाया जाए.
सिस्टम कॉल की सूची मिलने के बाद, नीति तय करने के लिए PolicyBuilder का इस्तेमाल किया जा सकता है. PolicyBuilder में कई सुविधाजनक और हेल्पर फ़ंक्शन होते हैं. इनकी मदद से, कई सामान्य कार्रवाइयां की जा सकती हैं. यहां दी गई सूची में, उपलब्ध फ़ंक्शन का सिर्फ़ एक छोटा सा हिस्सा दिया गया है:
- प्रोसेस शुरू करने के लिए, किसी भी syscall को अनुमति वाली सूची में शामिल करें:
AllowStaticStartup();
AllowDynamicStartup();
- किसी भी ओपन/रीड/राइट* सिसकॉल को अनुमति वाली सूची में जोड़ें:
AllowOpen();
AllowRead();
AllowWrite();
- निकास/ऐक्सेस/स्टेट से जुड़े किसी भी syscall को अनुमति वाली सूची में शामिल करें:
AllowExit();
AllowStat();
AllowAccess();
- नींद/समय से जुड़े किसी भी syscall को अनुमति वाली सूची में शामिल करें:
AllowTime();
AllowSleep();
ये सुविधा फ़ंक्शन, काम के किसी भी syscall को अनुमति देते हैं. इसका फ़ायदा यह है कि एक ही नीति का इस्तेमाल अलग-अलग आर्किटेक्चर पर किया जा सकता है. हालांकि, कुछ सिस्टम कॉल उपलब्ध नहीं होते. उदाहरण के लिए, ARM64 में OPEN सिस्टम कॉल नहीं होता. हालांकि, इसमें सुरक्षा से जुड़ा थोड़ा जोखिम होता है. ऐसा इसलिए, क्योंकि इसमें ज़रूरत से ज़्यादा सिस्टम कॉल चालू किए जा सकते हैं. उदाहरण के लिए, AllowOpen() की मदद से, सैंडबॉक्सी किसी भी ओपन से जुड़े syscall को कॉल कर सकता है. अगर आपको सिर्फ़ एक खास सिसकॉल को अनुमति देनी है, तो AllowSyscall();
का इस्तेमाल करें. एक साथ कई सिसकॉल को अनुमति देने के लिए, AllowSyscalls()
का इस्तेमाल करें.
अब तक यह नीति, सिर्फ़ syscall आइडेंटिफ़ायर की जाँच करती है. अगर आपको नीति को और मज़बूत करना है और ऐसी नीति तय करनी है जिसमें सिर्फ़ खास आर्ग्युमेंट वाले सिसकॉल की अनुमति हो, तो आपको AddPolicyOnSyscall()
या AddPolicyOnSyscalls()
का इस्तेमाल करना होगा. ये फ़ंक्शन, सिर्फ़ syscall आईडी को आर्ग्युमेंट के तौर पर नहीं लेते. इसके अलावा, ये Linux कर्नल से bpf हेल्पर मैक्रो का इस्तेमाल करके, रॉ seccomp-bpf फ़िल्टर को भी आर्ग्युमेंट के तौर पर लेते हैं. BPF के बारे में ज़्यादा जानने के लिए, कर्नेल के दस्तावेज़ देखें. अगर आपको बार-बार BPF कोड लिखना पड़ रहा है और आपको लगता है कि इसमें यूज़ेबिलिटी-रैपर होना चाहिए, तो बेझिझक तौर पर सुविधा के लिए अनुरोध करें.
सिस्टम कॉल से जुड़े फ़ंक्शन के अलावा, PolicyBuilder, फ़ाइल सिस्टम से जुड़े कई फ़ंक्शन भी उपलब्ध कराता है. जैसे, सैंडबॉक्स में किसी फ़ाइल/डायरेक्ट्री को बाइंड-माउंट करने के लिए AddFile()
या AddDirectory()
. AddTmpfs()
हेल्पर का इस्तेमाल, सैंडबॉक्स में अस्थायी फ़ाइल स्टोरेज जोड़ने के लिए किया जा सकता है.
खास तौर पर, AddLibrariesForBinary()
फ़ंक्शन बहुत काम का है. यह किसी बाइनरी के लिए ज़रूरी लाइब्रेरी और लिंकर जोड़ता है.
जिन सिस्टम कॉल को अनुमति देनी है उन्हें तय करने के लिए, अब भी कुछ काम मैन्युअल तरीके से करना पड़ता है. उन सिस्टम कॉल के साथ एक नीति बनाएं जिनकी आपकी बाइनरी को ज़रूरत है. इसके बाद, इसे सामान्य वर्कलोड के साथ चलाएं. अगर उल्लंघन का पता चलता है, तो 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. सीमाओं में बदलाव करना
सैंडबॉक्स नीति, सैंडबॉक्सी को कुछ खास सिसकॉल करने से रोकती है. इससे अटैक सरफ़ेस कम हो जाता है. हालांकि, हमलावर अब भी किसी प्रोसेस को अनिश्चित काल तक चलाकर या रैम और अन्य संसाधनों का इस्तेमाल करके, अनचाहे असर डाल सकता है.
इस खतरे से निपटने के लिए, सैंडबॉक्सी को डिफ़ॉल्ट रूप से, एक्ज़ीक्यूशन की तय सीमाओं के तहत चलाया जाता है. अगर इन डिफ़ॉल्ट सीमाओं की वजह से, आपके प्रोग्राम को सही तरीके से लागू करने में समस्या आ रही है, तो 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
ऑब्जेक्ट को इंस्टैंटिएट करने और उसे सिंक्रोनस तरीके से लागू करने का तरीका दिखाया गया है. ज़्यादा जानकारी वाले उदाहरण के लिए, static देखें.
#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 हेडर फ़ाइल देखें) का इस्तेमाल करके, पूर्णांक, स्ट्रिंग, बाइट बफ़र, प्रोटोबफ़ या फ़ाइल डिस्क्रिप्टर भेजे जा सकते हैं.
फ़ाइल की जानकारी देने वाले शेयर करना
इंटर-प्रोसेस कम्यूनिकेशन एपीआई (ipc.h देखें) का इस्तेमाल करके, MapFd()
या ReceiveFd()
का इस्तेमाल किया जा सकता है:
एक्ज़ीक्यूटर से सैंडबॉक्सी में फ़ाइल डिस्क्रिप्टर को मैप करने के लिए,
MapFd()
का इस्तेमाल करें. इसका इस्तेमाल, एक्ज़ीक्यूटर से खोली गई फ़ाइल को Sandboxee में इस्तेमाल करने के लिए शेयर करने के लिए किया जा सकता है. इसके इस्तेमाल का उदाहरण static में देखा जा सकता है.// The executor opened /proc/version and passes it to the sandboxee as stdin executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
सॉकेटपेयर एंडपॉइंट बनाने के लिए,
ReceiveFd()
का इस्तेमाल करें. इसका इस्तेमाल, सैंडबॉक्स के स्टैंडर्ड आउटपुट या स्टैंडर्ड गड़बड़ियों को पढ़ने के लिए किया जा सकता है. इसके इस्तेमाल का उदाहरण टूल में देखा जा सकता है.// 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 का इस्तेमाल शुरू करने के लिए, आपको सबसे पहले Sandbox2 ऑब्जेक्ट से comms ऑब्जेक्ट पाना होगा:
sandbox2::Comms* comms = s2.comms();
comms ऑब्जेक्ट उपलब्ध होने के बाद, Send* फ़ंक्शन के परिवार के किसी एक फ़ंक्शन का इस्तेमाल करके, सैंडबॉक्स किए गए प्रोग्राम को डेटा भेजा जा सकता है. comms API के इस्तेमाल का उदाहरण, crc4 उदाहरण में देखा जा सकता है. नीचे दिया गया कोड स्निपेट, उस उदाहरण का एक हिस्सा दिखाता है. मैनेजर, SendBytes(buf, size)
के साथ unsigned char
buf[size]
भेजता है:
if (!(comms->SendBytes(static_cast<const uint8_t*>(buf), sz))) {
/* handle error */
}
Sandboxee से डेटा पाने के लिए, इनमें से किसी एक Recv*
फ़ंक्शन का इस्तेमाल करें. नीचे दिया गया कोड स्निपेट, crc4 के उदाहरण का एक हिस्सा है. एक्ज़ीक्यूटर को 32-बिट के बिना हस्ताक्षर वाले पूर्णांक में चेकसम मिलता है: uint32_t crc4;
if (!(comms->RecvUint32(&crc4))) {
/* handle error */
}
बफ़र के साथ डेटा शेयर करना
डेटा शेयर करने की एक और सुविधा यह है कि buffer 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. सैंडबॉक्स से बाहर निकलना
सैंडबॉक्स को चलाने के तरीके के आधार पर (यह चरण देखें), आपको सैंडबॉक्स को बंद करने के तरीके में बदलाव करना होगा. इसलिए, Sandboxee को भी बंद करने के तरीके में बदलाव करना होगा.
सिंक किए गए सैंडबॉक्स से बाहर निकलना
अगर सैंडबॉक्स को सिंक्रोनस तरीके से चलाया जा रहा है, तो Run फ़ंक्शन सिर्फ़ तब वैल्यू देगा, जब Sandboxee की प्रोसेस पूरी हो जाएगी. इसलिए, खाता बंद करने के लिए कोई अतिरिक्त चरण पूरा करने की ज़रूरत नहीं है. नीचे दिए गए कोड स्निपेट में इस स्थिति को दिखाया गया है:
Sandbox2::Result result = s2.Run();
LOG(INFO) << "Final execution status: " << result.ToString();
एसिंक्रोनस तरीके से चल रहे सैंडबॉक्स से बाहर निकलना
अगर सैंडबॉक्स एसिंक्रोनस तरीके से चल रहा है, तो उसे बंद करने के लिए दो विकल्प उपलब्ध हैं. सबसे पहले, Sandboxee के पूरा होने का इंतज़ार करें और एक्ज़ीक्यूशन की फ़ाइनल स्थिति पाएं:
sandbox2::Result result = s2.AwaitResult();
LOG(INFO) << "Final execution status: " << result.ToString();
इसके अलावा, आपके पास किसी भी समय Sandboxee को बंद करने का विकल्प होता है. हालांकि, हमारा सुझाव है कि आप AwaitResult()
को कॉल करें. ऐसा इसलिए, क्योंकि हो सकता है कि इस बीच किसी और वजह से Sandboxee बंद हो जाए:
s2.Kill();
sandbox2::Result result = s2.AwaitResult();
LOG(INFO) << "Final execution status: " << result.ToString();
7. टेस्ट
किसी अन्य कोड की तरह ही, सैंडबॉक्स लागू करने के लिए जांचें होनी चाहिए. सैंडबॉक्स टेस्ट का मकसद, प्रोग्राम के सही होने की जांच करना नहीं है. इसके बजाय, यह देखना है कि सैंडबॉक्स किया गया प्रोग्राम, सैंडबॉक्स के नियमों के उल्लंघन जैसी समस्याओं के बिना चल सकता है या नहीं. इससे यह भी पक्का होता है कि सैंडबॉक्स की नीति सही है.
सैंडबॉक्स किए गए प्रोग्राम की जांच उसी तरह की जाती है जिस तरह उसे प्रोडक्शन में चलाया जाता है. इसमें वे ही आर्ग्युमेंट और इनपुट फ़ाइलें इस्तेमाल की जाती हैं जिन्हें आम तौर पर प्रोसेस किया जाता है.
ये टेस्ट, शेल टेस्ट या सब प्रोसेस का इस्तेमाल करके C++ टेस्ट जितने आसान हो सकते हैं. प्रेरणा पाने के लिए, उदाहरण देखें.
नतीजा
यहां तक पढ़ने के लिए धन्यवाद. हमें उम्मीद है कि आपको हमारी गाइड पसंद आई होगी. साथ ही, अब आपको अपने उपयोगकर्ताओं को सुरक्षित रखने के लिए, सैंडबॉक्स बनाने में आसानी होगी.
सैंडबॉक्स और नीतियां बनाना एक मुश्किल काम है. इसमें छोटी-मोटी गड़बड़ियां होने की आशंका रहती है. हमारा सुझाव है कि आप किसी सुरक्षा विशेषज्ञ से अपनी नीति और कोड की समीक्षा कराएं, ताकि आपको सुरक्षा से जुड़ी कोई समस्या न हो.