I have a phpfiddle application in Heroku where an user can play with any php code.
Basically this is the application structure (sample):
/app/
|
|--fiddle/ <-- contains all php fiddles generated by the user.
| |
| |-- abcde/ <-- random directory.
| |
| |--index.php <--- custom user code.
|
|--app.php <--- entry point and main app file.
The user can have many phpfiddles in their account, each of them corresponding to a random directory and the index.php
file is executed through an <iframe>
later.
How to avoid any app user can Create/List/Delete any file/directory outside their random directory through index.php
file?
My current solution was to parse the PHP user code (before execute it) and detect all possible functions like file_put_contents()
, unlink()
, mkdir()
, dir()
, rmdir()
and others, but that limits the scope of the test and I need the user can play with these functions inside their context.
I'm afraid that I need to set specials permission to the file system "dynamically" base on resource execution (I guess). How to do that? It's possible achieve that?
EDIT: Currently php.net/runkit does not support PHP 7 yet (https://github.com/zenovich/runkit/issues/87) which is required in this case.
Realistically, no. The security implications of running a service such as this where you would execute untrusted third party code is something that requires a lot of technical planning and infrastructure.
To understand the magnitude of the problem, there is a company that provides such a service (ideone.com, no affiliation). It's parent company Sphere Engine offers this as a service to run untrusted code in a secure environment.
Especially running on Heroku, your service would likely give you a lot of headache if it were to be maliciously used (e.g. sending bulk SPAM using PHP's mail(), targeting other users via DoS using PHP's CuRL). These problems would extend past just one user deleting files of another user and would likely get you banned from Heroku. A malicious actor can and will figure out ways around your system.
--
To take this a step further, let's consider what you theoretically would need to do to accomplish such a task.
You would begin by using something like Docker, a container that essentially runs like an operating system. You would need to configure the environment so that code running on the container has no internet access, is limited in CPU time, and is destroyed after each code execution.
Every time a user submits code, you would essentially copy the code into the container and allow the code to run. You would then be able to capture the output of the container and return that to the user.
This is a very rough outline of what you would need to accomplish this.