On Tue, Aug 6, 2024, at 10:41, Nick Lockheart wrote:
> 
> Sandbox: Security
> 
> A SandBox has two use cases:
> 
> 1. Unit Testing of code with mocks or stubs, and also, allowing testing
> with different environments.
> 
> 2. The secure running of 3rd party code inside a 1st party application.
> 
> 
> For the second use case, I will use a fictional blogging software
> called "Hot Blog" as the example.
> 
> Hot Blog is a very popular Open Source blogging platform. Hot Blog
> allows third party developers to write plugins.
> 
> While many Hot Blog plugin developers have the best of intentions, some
> of them are novice coders that make security mistakes.
> 
> So let's talk about how Hot Blog could benefit from using the new
> SandBox API.
> 
> By default, a SandBox instance is a blank slate. There's nothing inside
> of it, unless the SandBox is told to have something in it.
> 
> That means that sandboxed code that tries to read $_SESSION will find
> an empty array. Same with $_SERVER, $_POST, and $_GET.
> 
> That's by default. This allows the code that controls the sandbox to
> create custom access to application level resources.
> 
> Let's say that Hot Blog wants plugin developers to be able to access
> certain $_POST variables, but only *after* Hot Blog has checked the
> strings for multi-byte attack vulnerabilities.
> 
> To do this, Hot Blog creates a class called PluginAPI with a
> GetCleanPost method. This lets sandboxed plugins get $_POST data
> without being able to bypass Hot Blog's mandatory security check.
> (Remember, $_POST is empty inside the sandbox).
> 
> The code looks like this:
> 
> $oSandbox = new SPLSandBox();
> $oSandbox->MockClass('\HotBlog\PluginAPI','\HotBlog\PluginAPI');
> $oUserPlugin = $oSandbox->GetInstance('BobsMagicPlugin');
> $oUserPlugin->Run();
> 
> Because "Bob" has written his plugin as a Hot Blog plugin and knows
> that Hot Blog's rules require him to use
> \HotBlog\PluginAPI::GetCleanPost() to access a $_POST variable, he
> calls that instead of using $_POST.
> 
> Now, Hot Blog can impose mandatory security checks on incoming data
> making their application more secure.
> 
> Next, let's talk about includes. By default, if sandboxed code tries to
> include or require a file, a SandBoxAccessViolation is thrown.
> 
> Letting sandboxed code include whatever it wants defeats the point of a
> sandbox, at least for security use cases.
> 
> Of course, includes are useful, and plugins may need them. But the
> outer application should be able to control that access.
> 
> Enter SPLSandBox::RegisterIncludeHandler().
> 
> RegisterIncludeHandler accepts a callable.
> 
> The callable's signature is:
> 
> (int $IncludeType, string $FileName, string $FilePath)
> 
> Where:
> 
> $IncludeType is:
> 0 - require
> 1 - require_once
> 2 - include
> 3 - include_once
> 
> $FileName is the file without the path, and $FilePath is the path with
> trailing `/`.
> 
> If the sandbox should allow includes, the sandbox should have an
> Include Handler registered.
> 
> The SandBox API calls the include handler, if defined, when sandboxed
> code tries to include or require files.
> 
> Let's setup a function so our plugin authors can include files, but
> only from their own plugin directory:
> 
> // Sandbox setup for includes:
> 
> $oSandbox = new SPLSandBox();
> $oSandbox->RegisterIncludeHandler('HotBlogInclude');
> 
> $oUserPlugin = $oSandbox->GetInstance('BobsMagicPlugin');
> $oUserPlugin->Run();
> 
> 
> // Include Handler:
> 
> function HotBlogInclude($Type, $FileName, $FilePath){
>   
>    if(file_exists($PluginDirectoy.$FileName)){
>       $oSandbox->Include($PluginDirectoy.$FileName);
>       return 0;
>    }
>    return 1; // error!
> }
> 
> In the above example, $FilePath contained the path that Bob requested
> with his include statement. But we ignored it! Bob is only allowed to
> include from his plugin's own directory, so we see if the file is in
> $PluginDirectoy instead.
> 
> If the file is in Bob's directory, we include it *into* the sandbox
> with SPLSandBox::Include(), making it available to Bob's code, but
> keeping the main application code clean of any registrations the
> include may cause.
> 
> 
> ** Back to Unit Testing **
> 
> For the Unit Testing use case, however, certain code under test may
> normally read from $_GET, and that shouldn't change under test.
> 
> In this next example, we are running a unit test on a FrontController
> class, and we want to see if it works with many different URL
> structures.
> 
> Normally, the web server will map example.com/a/b/c to $_GET vars, so
> the FrontController class expects something like:
> 
> $_GET = [
>    'a' => 'Forum',
>    'b' => 'Post'
>    'c' => '123'
> ];
> 
> Let's make sure our FrontController is doing everything right with a
> battery of tests:
> 
> $aControllerTests = [
>    ['Forum','Post','123'],
>    ['Blog','Post','123'],
>    ['Article','acb'],
>    ['Cart','Product','723'],
>    ['Cart','Category','Jeans']
> ];
> 
> $aTestResults = [];
> foreach($aControllerTests as $TestID => $GetVars){
> 
>    $oSandbox = new SPLSandBox();
>    $oSandbox->MockGlobal('$_GET',$GetVars);   
>    $oController = $oSandbox->GetInstance('FrontController');
>    $aTestResults[$TestID] = $oController->Init();
>    $oSandbox->Destroy();
> }
> 
> SPLSandBox::MockGlobal() lets us set global variables (including super
> globals) inside the sandbox.
> 
> Now, $aTestResults contains the results of each test, run with separate
> $_GET parameters.
> 
> With this structure, you could get **every** valid URL from a database
> and run a unit test with custom $_GET params on your FrontController to
> make sure everything works.
> 

Hey Nick,

This looks quite valuable, and I assume auto loading would work just like 
normal? Register an autoloader that will eventually require the file and call 
this function?

It would be nice to provide a simplified api as well, maybe 
“CopyCurrentEnvironment()” or something?  In most cases, it is easier/faster to 
find things to remove vs. adding everything on every plugin/request every time. 

In saying that, it would be great if there was an api for “sharing” a 
base-sandbox pool via shm (or similar to a pool) so that the base vm doesn’t 
need to be recreated potentially hundreds of times per request. 

— Rob

Reply via email to