Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building and Breaking a Python Sandbox by Jessi...

PyCon 2014
April 11, 2014
2.5k

Building and Breaking a Python Sandbox by Jessica McKellar

PyCon 2014

April 11, 2014
Tweet

More Decks by PyCon 2014

Transcript

  1. Why? • Learning a language • Providing a hosted scratch

    pad • Distributed computation • Inspecting running processes safely
  2. Examples in the wild • Seattle’s peer-to-peer computing network •

    Google App Engine’s Python shell • Codecademy’s empythoned • CheckIO.org’s online coding game
  3. How do we execute arbitrary code? exec: compiles and evaluates

    statements >>> exec "print 'Hello world'" Hello world eval: compiles and evaluates expressions >>> eval("1 + 2") 3
  4. from sandbox import Sandbox s = Sandbox() code = """

    print "Hello world!" """ s.execute(code) test_sandbox.py
  5. $ python test_sandbox.py Hello world! from sandbox import Sandbox s

    = Sandbox() code = """ print "Hello world!" """ s.execute(code)
  6. What should we disallow? • Resource exhaustion • Information disclosure

    • Running unexpected services • Disabling/quitting/erroring out of the sandbox
  7. from sandbox import Sandbox s = Sandbox() code = """

    file("test.txt", "w").write("Kaboom!\\n") """ s.execute(code)
  8. >>> __builtins__.__dict__.keys() ['bytearray', 'IndexError', 'all', 'help', 'vars', 'SyntaxError', 'unicode', 'UnicodeDecodeError',

    'memoryview', 'isinstance', 'copyright', 'NameError', 'BytesWarning', 'dict', 'input', 'oct', 'bin', 'SystemExit', 'StandardError', 'format', 'repr', 'sorted', 'False', 'RuntimeWarning', 'list', 'iter', 'reload', 'Warning', '__package__', 'round', 'dir', 'cmp', 'set', 'bytes', 'reduce', 'intern', 'issubclass', 'Ellipsis', 'EOFError', 'locals', 'BufferError', 'slice', 'FloatingPointError', 'sum', 'getattr', 'abs', 'exit', 'print', 'True', 'FutureWarning', 'ImportWarning', 'None', 'hash', 'ReferenceError', 'len', 'credits', 'frozenset', '__name__', 'ord', 'super', '_', 'TypeError', 'license', 'KeyboardInterrupt', 'UserWarning', 'filter', 'range', 'staticmethod', 'SystemError', 'BaseException', 'pow', 'RuntimeError', 'float', 'MemoryError', 'StopIteration', 'globals', 'divmod', 'enumerate', 'apply', 'LookupError', 'open', 'quit', 'basestring', 'UnicodeError', 'zip', 'hex', 'long', 'next', 'ImportError', 'chr', 'xrange', 'type', '__doc__', 'Exception', 'tuple', 'UnicodeTranslateError', 'reversed', 'UnicodeEncodeError', 'IOError', 'hasattr', 'delattr', 'setattr', 'raw_input', 'SyntaxWarning', 'compile', 'ArithmeticError', 'str', 'property', 'GeneratorExit', 'int', '__import__', 'KeyError', 'coerce', 'PendingDeprecationWarning', 'file', 'EnvironmentError', 'unichr', 'id', 'OSError', 'DeprecationWarning', 'min', 'UnicodeWarning', 'execfile', 'any', 'complex', 'bool', 'ValueError', 'NotImplemented', 'map', 'buffer', 'max', 'object', 'TabError', 'callable', 'ZeroDivisionError', 'eval', '__debug__', 'IndentationError', 'AssertionError', 'classmethod', 'UnboundLocalError', 'NotImplementedError', 'AttributeError', 'OverflowError']
  9. >>> __builtins__.__dict__.keys() ['bytearray', 'IndexError', 'all', 'help', 'vars', 'SyntaxError', 'unicode', 'UnicodeDecodeError',

    'memoryview', 'isinstance', 'copyright', 'NameError', 'BytesWarning', 'dict', 'input', 'oct', 'bin', 'SystemExit', 'StandardError', 'format', 'repr', 'sorted', 'False', 'RuntimeWarning', 'list', 'iter', 'reload', 'Warning', '__package__', 'round', 'dir', 'cmp', 'set', 'bytes', 'reduce', 'intern', 'issubclass', 'Ellipsis', 'EOFError', 'locals', 'BufferError', 'slice', 'FloatingPointError', 'sum', 'getattr', 'abs', 'exit', 'print', 'True', 'FutureWarning', 'ImportWarning', 'None', 'hash', 'ReferenceError', 'len', 'credits', 'frozenset', '__name__', 'ord', 'super', '_', 'TypeError', 'license', 'KeyboardInterrupt', 'UserWarning', 'filter', 'range', 'staticmethod', 'SystemError', 'BaseException', 'pow', 'RuntimeError', 'float', 'MemoryError', 'StopIteration', 'globals', 'divmod', 'enumerate', 'apply', 'LookupError', 'open', 'quit', 'basestring', 'UnicodeError', 'zip', 'hex', 'long', 'next', 'ImportError', 'chr', 'xrange', 'type', '__doc__', 'Exception', 'tuple', 'UnicodeTranslateError', 'reversed', 'UnicodeEncodeError', 'IOError', 'hasattr', 'delattr', 'setattr', 'raw_input', 'SyntaxWarning', 'compile', 'ArithmeticError', 'str', 'property', 'GeneratorExit', 'int', '__import__', 'KeyError', 'coerce', 'PendingDeprecationWarning', 'file', 'EnvironmentError', 'unichr', 'id', 'OSError', 'DeprecationWarning', 'min', 'UnicodeWarning', 'execfile', 'any', 'complex', 'bool', 'ValueError', 'NotImplemented', 'map', 'buffer', 'max', 'object', 'TabError', 'callable', 'ZeroDivisionError', 'eval', '__debug__', 'IndentationError', 'AssertionError', 'classmethod', 'UnboundLocalError', 'NotImplementedError', 'AttributeError', 'OverflowError']
  10. class Sandbox(object): def execute(self, code_string): keyword_blacklist = ["file", "quit", "eval",

    "exec"] for keyword in keyword_blacklist: if keyword in code_string: raise ValueError("Blacklisted") exec code_string Idea: keyword blacklist
  11. from sandbox import Sandbox s = Sandbox() code = """

    file("test.txt", "w").write("Kaboom!\\n") """ s.execute(code) Testing: keyword blacklist
  12. from sandbox import Sandbox s = Sandbox() code = """

    file("test.txt", "w").write("Kaboom!\\n") """ s.execute(code) $ python test_sandbox.py Traceback (most recent call last): File "test_sandbox.py", line 11, in <module> s.execute(code) File "/Users/jesstess/Desktop/sandbox/ sandbox.py", line 86, in execute raise ValueError("Blacklisted") ValueError: Blacklisted Testing: keyword blacklist
  13. Circumvention idea: encryption func = __builtins__["file"] func("test.txt", "w").write("Kaboom!\n") func =

    __builtins__["svyr".decode("rot13")] func("test.txt", "w").write("Kaboom!\n")
  14. from sandbox import Sandbox s = Sandbox() code = """

    func = __builtins__["svyr".decode("rot13")] func("test.txt", "w").write("Kaboom!\\n") """ s.execute(code) Testing: keyword blacklist Kaboom
  15. builtins_whitelist = set(( # exceptions 'ArithmeticError', 'AssertionError', 'AttributeError', ... #

    constants 'False', 'None', 'True', ... # types 'basestring', 'bytearray', 'bytes', 'complex', 'dict', ... # functions '__import__', 'abs', 'all', 'any', 'apply', 'bin', 'bool', ... # block: eval, execfile, file, quit, exit, reload, etc. ))
  16. import sys main = sys.modules["__main__"].__dict__ orig_builtins = main["__builtins__"].__dict__ builtins_whitelist =

    set(( ... )) for builtin in orig_builtins.keys(): if builtin not in builtins_whitelist: del orig_builtins[builtin]
  17. from sandbox import Sandbox s = Sandbox() code = """

    file("test.txt", "w").write("Kaboom!\\n") """ s.execute(code) Testing: builtins whitelist
  18. from sandbox import Sandbox s = Sandbox() code = """

    file("test.txt", "w").write("Kaboom!\\n") """ s.execute(code) $ python test_sandbox.py Traceback (most recent call last): File "test_sandbox.py", line 9, in <module> s.execute(code) ... File "<string>", line 2, in <module> NameError: name 'file' is not defined Testing: builtins whitelist
  19. from sandbox import Sandbox s = Sandbox() code = """

    import os fd = os.open("test.txt", os.O_CREAT|os.O_WRONLY) os.write(fd, "Kaboom!\\n") """ s.execute(code) Testing: builtins whitelist Kaboom
  20. >>> importer = __builtins__.__dict__.get("__import__") >>> os = importer("os") >>> os

    <module 'os' from '/Library/Frameworks/ Python.framework/Versions/2.7/lib/python2.7/os.pyc'> >>> os.getcwd() '/Users/jesstess/Desktop/sandbox' Idea: import whitelist How does importing a module work in Python?
  21. >>> def my_importer(module_name, globals={}, ... locals={}, fromlist=[], ... level=-1): ...

    print "Using my importer!" ... return __import__(module_name, globals, ... locals, fromlist, level) ... >>> os = my_importer("os") Using my importer! >>> os.getcwd() '/Users/jesstess/Desktop/sandbox' Idea: import whitelist Cool, let’s write our own importer:
  22. def _safe_import(__import__, module_whitelist): def safe_import(module_name, globals={}, locals={}, fromlist=[], level=-1): !

    if module_name in module_whitelist: return __import__(module_name, ! ! ! ! ! ! ! ! ! ! ! ! globals, locals, fromlist, level) else: raise ImportError( "Blocked import of %s" ( module_name,)) return safe_import
  23. import sys main = sys.modules["__main__"].__dict__ orig_builtins = main["__builtins__"].__dict__ for builtin

    in orig_builtins.keys(): if builtin not in builtins_whitelist: del original_builtins[builtin] safe_modules = ["string", "re"] orig_builtins["__import__"] = _safe_import( __import__, safe_modules)
  24. from sandbox import Sandbox s = Sandbox() code = """

    import os fd = os.open("test.txt", os.O_CREAT|os.O_WRONLY) os.write(fd, "Kaboom!\\n") """ s.execute(code) Testing: import whitelist
  25. from sandbox import Sandbox s = Sandbox() code = """

    import os fd = os.open("test.txt", os.O_CREAT|os.O_WRONLY) os.write(fd, "Kaboom!\\n") """ s.execute(code) Testing: import whitelist $ python test_sandbox.py Traceback (most recent call last): File "test_sandbox.py", line 11, in <module> ... raise ImportError("Blocked import of %s" % (module_name,)) ImportError: Blocked import of os
  26. class ReadOnlyBuiltins(dict): def __delitem__(self, key): ValueError("Read-only!") def pop(self, key, default=None):

    ValueError("Read-only!") def popitem(self): ValueError("Read-only!") ... def setdefault(self, key, value): ValueError("Read-only!") def __setitem__(self, key, value): ValueError("Read-only!") def update(self, dict, **kw): ValueError("Read-only!")
  27. main = sys.modules["__main__"].__dict__ orig_builtins = main["__builtins__"].__dict__ for builtin in orig_builtins.keys():

    if builtin not in builtins_whitelist: del original_builtins[builtin] safe_modules = ["string", "re"] orig_builtins["__import__"] = _safe_import( __import__, safe_modules) safe_builtins = ReadOnlyBuiltins( original_builtins) main["__builtins__"] = safe_builtins
  28. >>> dir([]) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__',

    '__format__', '__ge__', ...] >>> [].__class__ <type 'list'> What can we find out about an object’s base classes?
  29. >>> [].__class__ <type 'list'> >>> [].__class__.__bases__ (<type 'object'>,) >>> [].__class__.__bases__[0]

    <type 'object'> What can we find out about an object’s base classes? list subclasses object
  30. >>> [].__class__.__subclasses__() [] >>> int.__subclasses__() [<type 'bool'>] >>> basestring.__subclasses__() [<type

    'str'>, <type 'unicode'>] What can we find out about an object’s subclasses? subclasses of basestring
  31. >>> [].__class__.__bases__ (<type 'object'>,) >>> [].__class__.__bases__[0] <type 'object'> >>> [].__class__.__bases__[0].__subclasses__()

    [<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'string.Template'>, <class 'string.Formatter'>, <type 'operator.itemgetter'>, <type 'operator.attrgetter'>, <type 'operator.methodcaller'>, <type 'collections.deque'>, <type 'deque_iterator'>, <type 'deque_reverse_iterator'>, <type 'itertools.combinations'>, <type 'itertools.combinations_with_replacement'>, <type 'itertools.cycle'>, <type 'itertools.dropwhile'>, <type 'itertools.takewhile'>, <type 'itertools.islice'>, <type 'itertools.starmap'>, <type 'itertools.imap'>, <type 'itertools.chain'>, <type 'itertools.compress'>, <type 'itertools.ifilter'>, <type 'itertools.ifilterfalse'>, <type 'itertools.count'>, <type 'itertools.izip'>, <type 'itertools.izip_longest'>, <type 'itertools.permutations'>, <type 'itertools.product'>, <type 'itertools.repeat'>, <type 'itertools.groupby'>, <type 'itertools.tee_dataobject'>, <type 'itertools.tee'>, <type 'itertools._grouper'>, <type '_thread._localdummy'>, <type 'thread._local'>, <type 'thread.lock'>, <class 'sandbox.Protection'>, <type 'resource.struct_rusage'>, <class 'sandbox.config.SandboxConfig'>, <class 'sandbox.proxy.ReadOnlySequence'>, <class 'sandbox.sandbox_class.Sandbox'>, <class 'sandbox.restorable_dict.RestorableDict'>] All of the subclasses of object!
  32. >>> [].__class__.__bases__ (<type 'object'>,) >>> [].__class__.__bases__[0] <type 'object'> >>> obj_class

    = [].__class__.__bases__[0] >>> for c in obj_class.__subclasses__(): ... print c.__name__ ... wrapper_descriptor instance ellipsis member_descriptor file PyCapsule cell callable-iterator iterator ...
  33. >>> [].__class__.__bases__ (<type 'object'>,) >>> [].__class__.__bases__[0] <type 'object'> >>> obj_class

    = [].__class__.__bases__[0] >>> for c in obj_class.__subclasses__(): ... print c.__name__ ... wrapper_descriptor instance ellipsis member_descriptor file PyCapsule cell callable-iterator iterator ... !!!
  34. from sandbox import Sandbox s = Sandbox() code = """

    obj_class = [].__class__.__bases__[0] obj_subclasses = dict((elt.__name__, elt) for \ elt in obj_class.__subclasses__()) func = obj_subclasses["file"] func("text.txt", "w").write("Kaboom!\\n") """ s.execute(code) Testing: read-only builtins Kaboom
  35. >>> type.__bases__ (<type 'object'>,) >>> del type.__bases__ Traceback (most recent

    call last): File "<stdin>", line 1, in <module> TypeError: can't set attributes of built- in/extension type 'type' Let’s delete __bases__ and __subclasses__ Imposed by the underlying C implementation!
  36. from ctypes import pythonapi, POINTER, py_object _get_dict = pythonapi._PyObject_GetDictPtr _get_dict.restype

    = POINTER(py_object) _get_dict.argtypes = [py_object] del pythonapi, POINTER, py_object def dictionary_of(ob): dptr = _get_dict(ob) return dptr.contents.value cpython.py Let’s delete __bases__ and __subclasses__
  37. from cpython import dictionary_of main = sys.modules["__main__"].__dict__ ... safe_builtins =

    ReadOnlyBuiltins( original_builtins) main["__builtins__"] = safe_builtins type_dict = dictionary_of(type) del type_dict["__bases__"] del type_dict["__subclasses__"]
  38. >>> def foo(): ... print "Meow" ... >>> dir(foo) ['__call__',

    '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
  39. >>> def foo(): ... print "Meow" ... >>> dir(foo) ['__call__',

    '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name'] ???
  40. >>> foo.func_code <code object foo at 0x100509d30, file "<stdin>", line

    1> >>> dir(foo.func_code) ['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames'] >>> foo.func_code.co_code 'd\x01\x00GHd\x00\x00S'
  41. >>> def foo(): ... print "Meow" ... >>> def evil_function():

    ... print "Kaboom!" ... >>> foo() Meow
  42. >>> def foo(): ... print "Meow" ... >>> def evil_function():

    ... print "Kaboom!" ... >>> foo() Meow >>> foo.__setattr__("func_code", evil_function.func_code)
  43. >>> def foo(): ... print "Meow" ... >>> def evil_function():

    ... print "Kaboom!" ... >>> foo() Meow >>> foo.__setattr__("func_code", evil_function.func_code) >>> foo() Kaboom! Kaboom
  44. from cpython import dictionary_of from types import FunctionType ... type_dict

    = dictionary_of(type) del type_dict["__bases__"] del type_dict["__subclasses__"] function_dict = dictionary_of(FunctionType) del function_dict["func_code"] Delete func_code
  45. Whew. Let’s recap tactics: • Keyword blacklist • Builtins whitelist

    • Import whitelist • Making important objects read-only (builtins) • Deleting problematic implementation details (__bases__, __subclasses__, func_code) • Deleting the ability to construct arbitrary code objects
  46. builtins_whitelist = set(( # exceptions 'ArithmeticError', 'AssertionError', 'AttributeError', 'BufferError', 'BytesWarning',

    'DeprecationWarning', 'EOFError', 'EnvironmentError', 'Exception', 'FloatingPointError','FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError','LookupError', 'MemoryError', 'NameError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError','PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError','UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', # constants 'False', 'None', 'True', '__doc__', '__name__', '__package__', 'copyright', 'license', 'credits', # types 'basestring', 'bytearray', 'bytes', 'complex', 'dict', 'float', 'frozenset', 'int', 'list', 'long', 'object', 'set', 'str', 'tuple', 'unicode', # functions '__import__', 'abs', 'all', 'any', 'apply', 'bin', 'bool', 'buffer', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'delattr', 'dir', 'divmod', 'enumerate', 'filter', 'format', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', 'isinstance', 'issubclass', 'iter', 'len', 'locals', 'map', 'max', 'min', 'next', 'oct', 'ord', 'pow', 'print', 'property', 'range', 'reduce', 'repr', 'reversed', 'round', 'setattr', 'slice', 'sorted', 'staticmethod', 'sum', 'super', 'type', 'unichr', 'vars', 'xrange', 'zip', )) def _safe_import(__import__, module_whitelist): def safe_import(module_name, globals={}, locals={}, fromlist=[], level=-1): if module_name in module_whitelist: return __import__(module_name, globals, locals, fromlist, level) else: raise ImportError("Blocked import of %s" % (module_name,)) return safe_import class ReadOnlyBuiltins(dict): def clear(self): ValueError("Read-only!") def __delitem__(self, key): ValueError("Read-only!") def pop(self, key, default=None): ValueError("Read-only!") def popitem(self): ValueError("Read-only!") def setdefault(self, key, value): ! ValueError("Read-only!") def __setitem__(self, key, value): ValueError("Read-only!") def update(self, dict, **kw): ValueError("Read-only!") class Sandbox(object): def __init__(self): ! import sys ! from types import FunctionType ! from cpython import dictionary_of ! original_builtins = sys.modules["__main__"].__dict__["__builtins__"].__dict__ ! for builtin in original_builtins.keys(): if builtin not in builtins_whitelist: ! ! del sys.modules["__main__"].__dict__["__builtins__"].__dict__[builtin] original_builtins["__import__"] = _safe_import(__import__, ["string", "re"]) safe_builtins = ReadOnlyBuiltins(original_builtins) sys.modules["__main__"].__dict__["__builtins__"] = safe_builtins ! type_dict = dictionary_of(type) ! del type_dict["__bases__"] ! del type_dict["__subclasses__"] ! function_dict = dictionary_of(FunctionType) ! del function_dict["func_code"] def execute(self, code_string): ! exec code_string builtins whitelist import whitelist read-only builtins deleting __bases__, __subclasses_, and func_code
  47. What should we disallow? • Resource exhaustion • Information disclosure

    • Running unexpected services • Disabling/quitting/erroring out of the sandbox
  48. Experiments • How does an alternative Python implementation like PyPy

    handle these issues? • How does the CPython interpreter compile and run bytecode? • What does the Python stack look like? • How do ctypes work? • How can the operating system help provide a secure environment?
  49. Bedtime reading • The full pysandbox implementation: https://github.com/haypo/pysandbox/ • A

    retrospective on pysandbox’s challenges: https://lwn.net/Articles/574215/ • PyPy’s sandbox implementation: http://pypy.readthedocs.org/en/latest/sandbox.html • How PythonAnywhere’s sandbox works: http://blog.pythonanywhere.com/83/