Restricted Subsets of Perl

Discussion in 'Perl Misc' started by Andy Glew, Dec 15, 2004.

  1. Andy Glew

    Andy Glew Guest

    Q: are there any ways to restrict a bit of Perl code to a restricted
    subset, a sandbox, of Perl - apart from taint?

    I'm lazy.

    I want a config file format for a tool. I want the config file format
    to support expressions, so that the user can say fairly arbitrary
    things like "concatenate in different places to the existing tree of
    patterns", "replace", "form an outer product".

    In fact, the most general form is to allow the user to specify an
    arbitrary function from string to string.

    I could write a mini-language. But I'm lazy.

    In the past, I have occasionally just slurped the config file in and
    eval'ed it. But that has obvious security issues; and apart from the
    security issues it can be fragile, e.g. if the user code being eval'ed
    has name collisions with the rest of my Perl code.

    So, what I ask is, are there any restricted subsets of Perl?

    ---+ Flavoursof Restricted Subsets

    Such restricted subsets might vary from

    ---++ Secure Sandboxes

    Like Java, where stuff like filesystem access, etc., is disabled, so
    that the code being eval'ed cannot access it. Ditto removal of the
    ability to load packages, access to environment variables, etc.

    ---++ Insulation from calling context

    Maybe not secure, but at least preventing access to any variables that
    are in the calling environment that are not part of standard Perl.

    ---++ Read-only insulation

    Forgetting the security concerns, I think that I can see a way that
    the eval'ed code could be made less fragile. E.g. define the config
    file to be an expression that returns a hash array, or the like.

    Then:

    a) fork
    b) the child process has read-only (or, copy-on-write) access
    to all state from the parent, but cannot modify any of the
    parents' state [*]
    c) let the child eval the config file, and generate the
    value to be returned
    d) dump the return value to a pipe, and read it
    in in the parent.

    [*] at least, the child cannot accidentally modify the parent.
    If really malicious, it might open a debugger on the parent
    - but that's beyond what I care about in this insecure version
    (and would be handled by the secure sandbox).


    ---+ Creating the Restricted Context

    I can imagine that you could use reflection to walk over the
    namespace. If there were a standard way to query - e.g. to ask
    "what does this Perl core function do?" (access filesystem, etc.)
    then the introspecting code might modify the namespace
    to prevent access.


    ---+ Why am I asking instead of rolling my own?

    Because I'm lazy. If it already exists, I will use it.

    Perfunctory googling doesn't seem to find any package like
    this, but I may not be using the right terminology.

    It has been my experience that, if I can describe how to
    something, Perl already has done it.

    ---++ Isn't taint what I want?

    I don't think so. But am willing to be educated.

    E.g. eval'ing in a taint context may make you moderately more secure,
    but doesn't really address the issues of fragility.
     
    Andy Glew, Dec 15, 2004
    #1
    1. Advertising

  2. A. Sinan Unur, Dec 15, 2004
    #2
    1. Advertising

  3. Andy Glew

    Andy Glew Guest

    Andy Glew <> writes:

    > Q: are there any ways to restrict a bit of Perl code to a restricted
    > subset, a sandbox, of Perl - apart from taint?
    >
    > Perfunctory googling doesn't seem to find any package like
    > this, but I may not be using the right terminology.


    Embarassed. Slightly less perfunctory googling led me to the Safe
    package. It's what I need.

    However, my pondering how to implement this via reflection may still
    be worth discussing. If there were a standard query format, allowing
    us to enumerate all language elements and identifying what they do,
    it might allow more flexible Safe'ing.
     
    Andy Glew, Dec 15, 2004
    #3
  4. A. Sinan Unur wrote:

    > Andy Glew <> wrote in
    > news::
    >
    >
    >>Q: are there any ways to restrict a bit of Perl code to a restricted
    >>subset, a sandbox, of Perl - apart from taint?

    >
    >
    > CPAN is your friend:
    >
    > Safe - Compile and execute code in restricted compartments
    >
    > http://search.cpan.org/~nwclark/perl-5.8.6/ext/Opcode/Safe.pm


    Be aware - Safe is flawed. The best known way to escape a Safe
    compartment involves returning blessed values. This can easily be
    avoided by disabling the bless opcode. Similarly it's probably a good
    idea to disable the tie opcode. However there are probably other less
    well known exploits.
     
    Brian McCauley, Dec 15, 2004
    #4
  5. Andy Glew

    Anno Siegel Guest

    Andy Glew <> wrote in comp.lang.perl.misc:
    > Andy Glew <> writes:
    >
    > > Q: are there any ways to restrict a bit of Perl code to a restricted
    > > subset, a sandbox, of Perl - apart from taint?
    > >
    > > Perfunctory googling doesn't seem to find any package like
    > > this, but I may not be using the right terminology.

    >
    > Embarassed. Slightly less perfunctory googling led me to the Safe
    > package. It's what I need.
    >
    > However, my pondering how to implement this via reflection may still
    > be worth discussing. If there were a standard query format, allowing
    > us to enumerate all language elements and identifying what they do,
    > it might allow more flexible Safe'ing.


    Alternatively, one could consider a Parse::RecDescent grammar for an
    appropriate subset of Perl. It would only have to recognize the
    subset, interpretation would still be done by eval(), so the grammar
    could be kept simple, it doesn't have to *do* anything.

    The grammar below allows only assignment of arithmetic expressions
    to scalars. So it allows "$deg = 2 * $pi / 360;", but rejects
    "unlink '/etc/passwd';", as it should. It is incomplete (no unary
    "-") and too simple to be practically useful, but it shows that the
    approach is feasible.

    Anno


    my $grammar = << 'EOG';

    confex: variable '=' expr ';' # start rule

    expr: term add_op expr
    expr: term

    term: factor mult_op term
    term: factor

    factor: '(' expr ')'
    factor: constant
    factor: variable

    constant: string
    constant: number

    add_op: /[+-]/
    mult_op: m{[*/]}

    string: /".*"/
    number: /-?\d+/
    variable: /\$[A-Za-z_]\w*/
    EOG
     
    Anno Siegel, Dec 15, 2004
    #5
  6. Andy Glew

    Andy Glew Guest

    >> However, my pondering how to implement this via reflection may still
    >> be worth discussing. If there were a standard query format, allowing
    >> us to enumerate all language elements and identifying what they do,
    >> it might allow more flexible Safe'ing.

    >
    >Alternatively, one could consider a Parse::RecDescent grammar for an
    >appropriate subset of Perl. It would only have to recognize the
    >subset, interpretation would still be done by eval(), so the grammar
    >could be kept simple, it doesn't have to *do* anything.


    It's the usual
    1. build up, out of known safe components
    or
    2. limit down, taking a general purpose
    facility like Perl and trying to restrict it

    1. is safer. Less likely to have security holes.

    But 1. violates OAOO - the principle of Once and Only Once. Moreover,
    if new, safe, features get added to Perl, 2. automatically includes
    them, but 1. doesn't.

    2. is more likely to have a security hole, because not all of the
    holes in the big ciomplicated system may be known.
     
    Andy Glew, Dec 16, 2004
    #6
  7. Andy Glew

    Andy Glew Guest

    >Be aware - Safe is flawed. The best known way to escape a Safe
    >compartment involves returning blessed values. This can easily be
    >avoided by disabling the bless opcode. Similarly it's probably a good
    >idea to disable the tie opcode. However there are probably other less
    >well known exploits.


    That's what I was worried about.

    I'm guessing that the blessing and tie'ing use paths which are
    relative to the Safe compartment, but which get exported, and
    interpreted relative to the true Root.

    Unfortunately, the design I came up with for an extensible config file
    involved allowing the user inside the Safe to do objects...

    Forking the Safe code into its own thread probably makes it safe
    against exploits that involve modifying stuff in the parent thread.
    Probably have to dump the return values, and strip out anything except
    vanilla ints, strings, arrays and lists, maybe refs: no structured,
    blessed, or tie'd data to be returned.

    ---

    If I understand you correctly, the problem with Safe is not that the
    Safe code can reach out, but that the Safe code can return a data
    structure that is a Trojan Horse, which when used by the outside world
    feeds stuff back into the compartment.

    I.e. the problem that I understand seems to be that you have to
    validate very carefully the values returned from Safe. I can even,
    possibly, imagine that there is no way to do such validation,
    if everything is tied. I don't know how much can be tied.

    But if you ignore the return value, are you safe? Or can you tie
    something to the destruction / garbage collection of the return value?

    Ignoring the return value wouldn't be terribly useful. Can you query
    the contents of the Safe, without executing tied code or the like?

    Or is it a more general problem? the code inside the Safe can get
    access to the outside world even without fooling the outside world to
    execute tied code or the like? That would be just a bug in Safe.
     
    Andy Glew, Dec 16, 2004
    #7
  8. Andy Glew

    Andy Glew Guest

    >There are only security issues if the config file is supplied by other,
    >untrusted, people. Are you running this as root using user config files?
    >If it's just a regular tool, there are no security issues - nothing
    >can be gained by 'eval'ling some code the user couldn't do in a much
    >simpler way.


    I'm not sure yet of the trust model. It's just generally good software
    to design to not require trust.
    At the moment, there are three parties involved in the trust
    model:
    a) the user
    b) the guy who coded up my tool
    c) currently, I can pick up a config file from
    . and a few other places. that may not be a good idea,
    but is convenient, just like putting . in
    your path, or using any relative path
    in your Perl library path

    Anyway, I'm not sure of the trust model. Not damning.




    I am more sure of the fragility issues. When I've evaled user
    provided strings in the past, I have had bugs when the user
    accidentally provided code that collided with names in my Perl code.
    I'd certainly like to avoid that.


    Therefore, Safe may be good enough for me as it stands now, with
    regards to reducing the chance of such accidental collisions.
    I may not care enough about security in this situation to
    worry about Safe's security holes.
     
    Andy Glew, Dec 16, 2004
    #8
  9. Andy Glew wrote:

    >>Be aware - Safe is flawed. The best known way to escape a Safe
    >>compartment involves returning blessed values. This can easily be
    >>avoided by disabling the bless opcode. Similarly it's probably a good
    >>idea to disable the tie opcode. However there are probably other less
    >>well known exploits.

    >
    >
    > That's what I was worried about.
    >
    > I'm guessing that the blessing and tie'ing use paths which are
    > relative to the Safe compartment, but which get exported, and
    > interpreted relative to the true Root.


    Actually that's not the case (I made the same mistake myself when I
    first started looking for weaknesses in Safe).

    But there is still a security hole because if the code within the safe
    compartment returns a value that is blessed into a package with a
    DESTROY then that destructor will be called with the true symbol table
    root accessible.

    > Unfortunately, the design I came up with for an extensible config file
    > involved allowing the user inside the Safe to do objects...


    To be really safe you should have a SUID helper and a chroot() :)

    > If I understand you correctly, the problem with Safe is not that the
    > Safe code can reach out, but that the Safe code can return a data
    > structure that is a Trojan Horse, which when used by the outside world
    > feeds stuff back into the compartment.


    That is the nature of the exploits I'm aware of. They can be avoided by
    simply disabling the bless opcode (and I'd do the tie opcode too, even
    though I don't know how to exploit it).

    I am also aware that a lot of people say there are other types exploits
    that are possible with Safe.

    > I.e. the problem that I understand seems to be that you have to
    > validate very carefully the values returned from Safe. I can even,
    > possibly, imagine that there is no way to do such validation,
    > if everything is tied. I don't know how much can be tied.
    >
    > But if you ignore the return value, are you safe?


    No. It still gets destroyed outside the safe compartment. The same may
    apply to blessed $@.

    > Ignoring the return value wouldn't be terribly useful. Can you query
    > the contents of the Safe, without executing tied code or the like?


    Yes. You can wrap the user-supplied code with more code that is
    executed inside the safe compartment simply stringifies the result.

    > Or is it a more general problem? the code inside the Safe can get
    > access to the outside world even without fooling the outside world to
    > execute tied code or the like? That would be just a bug in Safe.


    As I said, there are respected people who say there are such bugs in Safe.

    For a look at how I've tried to work-round some of the more obvious
    problems with Safe, take a look at String::Interpolate.
     
    Brian McCauley, Dec 16, 2004
    #9
  10. Andy Glew

    Andy Glew Guest

    Brian McCauley <> writes:

    > But there is still a security hole because if the code within the safe
    > compartment returns a value that is blessed into a package with a
    > DESTROY then that destructor will be called with the true symbol table
    > root accessible.


    I must conclude that this is a bug in Safe. The destructor should be
    compiled in such a manner that it is placed back into the Safe
    compartment before being run.

    And I think this points to the real problem with Safe. So far as I can
    tell, Safe seems to affect the execution of the code, not the
    compilation of the code. During execution inside the Safe, various
    pseudo-opcodes are disabled, and the namespace is restricted. But
    code compiled inside the Safe which is executed outside may not be
    properly restricted.

    Q: is it possible to identify all code compiled inside the safe?
    Surely so.

    Simply disabling the generation of certain opcodes by compilation is
    not good enough. It's the namespace.

    > No. It still gets destroyed outside the safe compartment. The same
    > may apply to blessed $@.
    >
    > > Ignoring the return value wouldn't be terribly useful. Can you query
    > > the contents of the Safe, without executing tied code or the like?

    >
    > Yes. You can wrap the user-supplied code with more code that is
    > executed inside the safe compartment simply stringifies the result.
    >
    > For a look at how I've tried to work-round some of the more obvious
    > problems with Safe, take a look at String::Interpolate.


    Thanks.


    > > Or is it a more general problem? the code inside the Safe can get
    > > access to the outside world even without fooling the outside world to
    > > execute tied code or the like? That would be just a bug in Safe.

    >
    > As I said, there are respected people who say there are such bugs in Safe.


    Where are those discussed.... nah, I don't need to know. For my present
    usage model , I'm not really that worried about security. I'm more
    interested in fragility, reduction thereof.
     
    Andy Glew, Dec 17, 2004
    #10
  11. Andy Glew wrote:

    > Brian McCauley <> writes:
    >
    >>But there is still a security hole because if the code within the safe
    >>compartment returns a value that is blessed into a package with a
    >>DESTROY then that destructor will be called with the true symbol table
    >>root accessible.

    >
    >
    > I must conclude that this is a bug in Safe. The destructor should be
    > compiled in such a manner that it is placed back into the Safe
    > compartment before being run.


    It is important that support for Safe does not put too much of an
    overhead on the rest of Perl. However, IIRC, this is the way it will
    work in Perl6 where Safe is not so much of an afterthought.

    > And I think this points to the real problem with Safe. So far as I can
    > tell, Safe seems to affect the execution of the code, not the
    > compilation of the code.


    Actually it does both.

    > During execution inside the Safe, various
    > pseudo-opcodes are disabled, and the namespace is restricted.


    No, the opcodes are disabled at compilation stage not runtime.
    Subroutines that are compiled put into the compartment's namespace from
    outside can use any opcodes. When such a subroutine is called from
    within the comparment it still sees the false namespace root and cannot
    compile restricted opcodes with eval() or require() - that's why we need
    Safe::Hole. (

    Note: Until fairly recently Safe::Hole did not re-instate *INC and the
    opcode mask so code called via a Safe::Hole callback couldn't require()
    additional modules. This was not good enough for something I was doing
    with Safe so I fixed it.

    > But
    > code compiled inside the Safe which is executed outside may not be
    > properly restricted.


    It will be restricted in terms of opcodes but not in terms of symbol
    tables. Of course if it was compiled with an opcode mask that permitted
    eval() then it can do further (unrestricted) compilation at runtime.

    > Q: is it possible to identify all code compiled inside the safe?
    > Surely so.


    Not that I know of. It would however be possible to detect upon exit
    from a safe compartment (either by a return or through a Safe::Hole
    callback) all the uppercase named subroutines and overload handlers that
    exist within package prefix of the Safe compartment and wrap then using
    a varient of Safe::Hole if they were not already so wrapped.

    I considered the scale of this task last time I was looking at
    Safe::Hole but decided that for my purposes it was simpler to simply
    disable bless, tie, overload. If you need code inside the comartment to
    create objects it can do so in a controlled manner by calling
    subroutines that have been compiled outside and passed in.

    > Simply disabling the generation of certain opcodes by compilation is
    > not good enough. It's the namespace.


    Er, yes that's my point above (and the opposite of what you said above).

    >>>Or is it a more general problem? the code inside the Safe can get
    >>>access to the outside world even without fooling the outside world to
    >>>execute tied code or the like? That would be just a bug in Safe.

    >>
    >>As I said, there are respected people who say there are such bugs in Safe.

    >
    >
    > Where are those discussed...


    In private, I think.
     
    Brian McCauley, Dec 17, 2004
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Roger Varley
    Replies:
    20
    Views:
    915
    jan V
    Aug 14, 2005
  2. will

    xsl subsets of nodesets

    will, Jul 7, 2003, in forum: XML
    Replies:
    3
    Views:
    860
    Marrow
    Jul 8, 2003
  3. Replies:
    2
    Views:
    538
  4. Simon

    List Subsets

    Simon, Nov 18, 2003, in forum: Python
    Replies:
    3
    Views:
    532
    Stian =?iso-8859-1?Q?S=F8iland?=
    Nov 22, 2003
  5. Bart Nessux

    sets and subsets

    Bart Nessux, Feb 11, 2004, in forum: Python
    Replies:
    16
    Views:
    531
    =?iso-8859-1?Q?Fran=E7ois?= Pinard
    Feb 17, 2004
Loading...

Share This Page