इस पेज पर, आपको Sandbox2 की मदद से अपना सैंडबॉक्स किया गया एनवायरमेंट बनाने का तरीका बताया गया है. आपको सैंडबॉक्स नीति तय करने का तरीका बताया जाएगा. साथ ही, कुछ ऐडवांस, लेकिन सामान्य बदलावों के बारे में भी बताया जाएगा. यहां दी गई जानकारी को गाइड के तौर पर इस्तेमाल करें. साथ ही, हेडर फ़ाइलों में दिए गए उदाहरण और कोड के दस्तावेज़ भी देखें.
1. सैंडबॉक्स एक्ज़ीक्यूटर का तरीका चुनें
सैंडबॉक्सिंग की शुरुआत, एक्ज़ीक्यूटर से होती है (सैंडबॉक्स एक्ज़ीक्यूटर देखें). यह सैंडबॉक्सी को चलाने के लिए ज़िम्मेदार होता है. 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) को सूचना देता है कि इसे सैंडबॉक्स में कब शामिल होना चाहिए.
तीसरा तरीका: कस्टम फ़ोर्कसर्वर – बाइनरी तैयार करें, फ़ोर्क के अनुरोधों का इंतज़ार करें, और खुद सैंडबॉक्स करें
इस मोड की मदद से, किसी बाइनरी को शुरू किया जा सकता है, उसे सैंडबॉक्सिंग के लिए तैयार किया जा सकता है, और बाइनरी के लाइफ़साइकल के किसी खास समय पर, उसे एक्ज़ीक्यूटर के लिए उपलब्ध कराया जा सकता है.
एग्ज़ीक्यूटर, आपकी बाइनरी को फ़ोर्क करने का अनुरोध भेजेगा. यह अनुरोध, ::sandbox2::ForkingClient::EnterForkLoop() के ज़रिए fork() करेगा. नई प्रोसेस, ::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());
ध्यान रखें कि यह मोड काफ़ी जटिल है और सिर्फ़ कुछ खास मामलों में लागू होता है. उदाहरण के लिए, जब आपके पास मेमोरी की ज़रूरतें सीमित हों. आपको सीओडब्ल्यू से फ़ायदा मिलेगा, लेकिन नुकसान यह है कि इसमें कोई असली एएसएलआर नहीं है. सैंडबॉक्स का इस्तेमाल करने का एक और उदाहरण तब होता है, जब सैंडबॉक्स में मौजूद प्रोग्राम को शुरू करने में ज़्यादा समय लगता है और सीपीयू का ज़्यादा इस्तेमाल होता है. इसे अविश्वसनीय डेटा को प्रोसेस करने से पहले चलाया जा सकता है.
एग्ज़ीक्यूटर के इस तरीके का उदाहरण देखने के लिए, custom_fork देखें.
2. सैंडबॉक्स की नीति बनाना
एक्ज़ीक्यूटर मिलने के बाद, आपको सैंडबॉक्स के लिए सैंडबॉक्स पॉलिसी तय करनी होगी. ऐसा न होने पर, सैंडबॉक्सी को सिर्फ़ डिफ़ॉल्ट सिस्कॉल नीति से सुरक्षित किया जाता है.
सैंडबॉक्स नीति का मकसद, उन syscalls और तर्कों को सीमित करना है जिन्हें Sandboxee बना सकता है. साथ ही, उन फ़ाइलों को भी सीमित करना है जिन्हें वह ऐक्सेस कर सकता है. आपको उन सिसकॉल के बारे में पूरी जानकारी होनी चाहिए जिनकी ज़रूरत उस कोड को होती है जिसे आपको सैंडबॉक्स करना है. सिस्टम कॉल को मॉनिटर करने का एक तरीका यह है कि कोड को Linux के कमांड-लाइन टूल strace के साथ चलाया जाए.
सिस्टम कॉल की सूची मिलने के बाद, नीति तय करने के लिए PolicyBuilder का इस्तेमाल किया जा सकता है.
PolicyBuilder में कई सुविधाजनक और हेल्पर फ़ंक्शन होते हैं. इनकी मदद से, कई सामान्य कार्रवाइयां की जा सकती हैं. इनमें से कई कार्रवाइयां, DefaultPolicyBuilder() में पहले से शामिल हैं. यहां दी गई सूची में, उपलब्ध फ़ंक्शन का सिर्फ़ एक छोटा सा हिस्सा शामिल है:
- प्रोसेस स्टार्टअप के लिए, किसी भी syscall को अनुमति वाली सूची में शामिल करें:
AllowStaticStartup();AllowDynamicStartup();
- किसी भी open/read/write* syscall को अनुमति वाली सूची में जोड़ें:
AllowOpen();AllowRead();AllowWrite();
- एग्ज़िट/ऐक्सेस/स्टेट से जुड़े किसी भी syscall को अनुमति वाली सूची में शामिल करें:
AllowExit();AllowStat();AllowAccess();
- नींद/समय से जुड़े किसी भी सिस्टम कॉल को अनुमति दें:
AllowTime();AllowSleep();
ये सुविधा फ़ंक्शन, काम के किसी भी syscall को अनुमति वाली सूची में शामिल करने की अनुमति देते हैं. इसका फ़ायदा यह है कि एक ही नीति का इस्तेमाल अलग-अलग आर्किटेक्चर पर किया जा सकता है, जहां कुछ syscalls उपलब्ध नहीं होते हैं.उदाहरण के लिए, ARM64 में कोई OPEN syscall नहीं होता. हालांकि, इसमें सुरक्षा से जुड़ा थोड़ा जोखिम होता है, क्योंकि इसमें ज़रूरत से ज़्यादा sycsalls चालू हो सकते हैं. उदाहरण के लिए, AllowOpen() की मदद से, सैंडबॉक्स किए गए ऐप्लिकेशन को ओपन से जुड़े किसी भी syscall को कॉल करने की अनुमति मिलती है. अगर आपको सिर्फ़ एक खास सिसकॉल को अनुमति देनी है, तो AllowSyscall(); का इस्तेमाल करें. एक साथ कई सिसकॉल को अनुमति देने के लिए, AllowSyscalls() का इस्तेमाल करें.
अब तक यह नीति सिर्फ़ syscall आइडेंटिफ़ायर की जांच करती है. अगर आपको नीति को और मज़बूत करना है और ऐसी नीति तय करनी है जिसमें सिर्फ़ खास आर्ग्युमेंट वाले syscall की अनुमति हो, तो आपको AddPolicyOnSyscall() या AddPolicyOnSyscalls() का इस्तेमाल करना होगा. ये फ़ंक्शन, सिर्फ़ सिसकॉल आईडी को आर्ग्युमेंट के तौर पर नहीं लेते हैं. ये Linux कर्नल से bpf हेल्पर मैक्रो का इस्तेमाल करके, रॉ सेस्कॉम्प-बीपीएफ़ फ़िल्टर भी लेते हैं. BPF के बारे में ज़्यादा जानने के लिए, कर्नेल के दस्तावेज़ देखें. अगर आपको बार-बार बीपीएफ़ कोड लिखना पड़ रहा है और आपको लगता है कि इसके लिए, उपयोगिता-रैपर होना चाहिए, तो बेझिझक सुविधा का अनुरोध करें.
सिस्टम कॉल से जुड़े फ़ंक्शन के अलावा, PolicyBuilder, फ़ाइल सिस्टम से जुड़े कई फ़ंक्शन भी उपलब्ध कराता है. जैसे, सैंडबॉक्स में किसी फ़ाइल/डायरेक्ट्री को बाइंड-माउंट करने के लिए AddFile() या AddDirectory(). AddTmpfs() हेल्पर का इस्तेमाल, सैंडबॉक्स में कुछ समय के लिए फ़ाइल स्टोरेज जोड़ने के लिए किया जा सकता है.
AddLibrariesForBinary() एक खास तौर पर उपयोगी फ़ंक्शन है, जो बाइनरी के लिए ज़रूरी लाइब्रेरी और लिंकर जोड़ता है.
हालांकि, अनुमति वाली सूची में शामिल करने के लिए, सिस्टम कॉल की पहचान करने का काम अब भी मैन्युअल तरीके से करना पड़ता है. उन syscalls के साथ एक नीति बनाएं जिनकी ज़रूरत आपके बाइनरी को है. इसके बाद, इसे सामान्य वर्कलोड के साथ चलाएं. अगर उल्लंघन ट्रिगर होता है, तो syscall को अनुमति वाली सूची में शामिल करें और प्रोसेस को दोहराएं. अगर आपको कोई ऐसी गड़बड़ी मिलती है जिसे अनुमति वाली सूची में शामिल करना जोखिम भरा हो सकता है और प्रोग्राम गड़बड़ियों को आसानी से ठीक कर लेता है, तो BlockSyscallWithErrno() का इस्तेमाल करके, गड़बड़ी को ठीक करने के बजाय उसे वापस लाने की कोशिश की जा सकती है.
यहां दिए गए उदाहरण में, sandbox2::PolicyBuilder को सीधे तौर पर बनाया गया है, ताकि write और mprotect आर्ग्युमेंट को सीमित करने और openat को ब्लॉक करने का तरीका दिखाया जा सके. हालांकि, DefaultPolicyBuilder() इसकी अनुमति देता है:
#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()का इस्तेमाल करें. इसका इस्तेमाल, एक्ज़ीक्यूटर से खोली गई फ़ाइल को शेयर करने के लिए किया जा सकता है, ताकि उसे सैंडबॉक्स में इस्तेमाल किया जा सके. इसका एक उदाहरण static में देखा जा सकता है.// The executor opened /proc/version and passes it to the sandboxee as stdin executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);socketpair एंडपॉइंट बनाने के लिए,
ReceiveFd()का इस्तेमाल करें. इसका इस्तेमाल, सैंडबॉक्स किए गए प्रोग्राम के स्टैंडर्ड आउटपुट या स्टैंडर्ड गड़बड़ियों को पढ़ने के लिए किया जा सकता है. इसके इस्तेमाल का एक उदाहरण, टूल में देखा जा सकता है.// The executor receives a file descriptor of the sandboxee stdout int recv_fd1 = executor->ipc())->ReceiveFd(STDOUT_FILENO);
Comms API का इस्तेमाल करना
Sandbox2, comms एपीआई उपलब्ध कराता है. यह एक्ज़ीक्यूटर और सैंडबॉक्स के बीच पूर्णांक, स्ट्रिंग या बाइट बफ़र शेयर करने का एक आसान तरीका है. यहां कुछ कोड स्निपेट दिए गए हैं. इन्हें crc4 उदाहरण में देखा जा सकता है.
Comms API का इस्तेमाल शुरू करने के लिए, आपको सबसे पहले Sandbox2 ऑब्जेक्ट से comms ऑब्जेक्ट पाना होगा:
sandbox2::Comms* comms = s2.comms();
comms ऑब्जेक्ट उपलब्ध होने के बाद, Send* फ़ैमिली के किसी एक फ़ंक्शन का इस्तेमाल करके, सैंडबॉक्स किए गए प्रोग्राम को डेटा भेजा जा सकता है. crc4 के उदाहरण में, Comms API के इस्तेमाल का उदाहरण देखा जा सकता है. नीचे दिए गए कोड स्निपेट में, उस उदाहरण का एक अंश दिखाया गया है. एग्ज़ीक्यूटर, SendBytes(buf, size) के साथ unsigned char
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 */
}
बफ़र के साथ डेटा शेयर करना
डेटा शेयर करने की एक और सुविधा यह है कि बहुत सारा डेटा शेयर करने के लिए, 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. सैंडबॉक्स से बाहर निकलना
सैंडबॉक्स को चलाने के तरीके के आधार पर (यह चरण देखें), आपको सैंडबॉक्स को बंद करने के तरीके में बदलाव करना होगा. साथ ही, सैंडबॉक्स को भी बंद करना होगा.
सिंक किए गए सैंडबॉक्स से बाहर निकलना
अगर सैंडबॉक्स को सिंक्रोनस तरीके से चलाया जा रहा है, तो Run फ़ंक्शन सिर्फ़ तब वैल्यू देगा, जब Sandboxee का काम पूरा हो जाएगा. इसलिए, आपको सदस्यता खत्म करने के लिए कोई और कार्रवाई करने की ज़रूरत नहीं है. नीचे दिए गए कोड स्निपेट में, इस स्थिति के बारे में बताया गया है:
Sandbox2::Result result = s2.Run();
LOG(INFO) << "Final execution status: " << result.ToString();
एसिंक्रोनस तरीके से चल रहे सैंडबॉक्स से बाहर निकलना
अगर सैंडबॉक्स एसिंक्रोनस तरीके से चल रहा है, तो उसे बंद करने के लिए दो विकल्प उपलब्ध हैं. सबसे पहले, सैंडबॉक्स के पूरा होने का इंतज़ार करें और एक्ज़ीक्यूशन की फ़ाइनल स्थिति पाएं:
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++ टेस्ट जितने आसान हो सकते हैं. आइडिया पाने के लिए, उदाहरण देखें.
नतीजा
यहां तक पढ़ने के लिए धन्यवाद. हमें उम्मीद है कि आपको हमारी गाइड पसंद आई होगी. साथ ही, अब आपको अपने उपयोगकर्ताओं को सुरक्षित रखने के लिए, सैंडबॉक्स बनाने में मदद मिलेगी.
सैंडबॉक्स और नीतियां बनाना एक मुश्किल काम है. इसमें छोटी-छोटी गड़बड़ियां होने की संभावना होती है. सुरक्षित रहने के लिए, हमारा सुझाव है कि आप किसी सुरक्षा विशेषज्ञ से अपनी नीति और कोड की समीक्षा कराएं.