M
Matthew Braid
Hi all,
I've been spending a lot of time lately writing a lot of objects for a project
here (so far about 60 objects and counting), and while I've been considering
security the whole way I've started thinking one level down on how to secure
objects from direct alteration if they were used as a library in some other
code. (At the moment this isn't _really_ an issue as the packages are not
available to users unless they go through controlled programs that don't allow
unknown code to run).
After tinkering, I've come up with the following pseudo-rules (please correct me
if I'm wrong here, and preferably give me pointers
1. If an object inherits from another object (in perl), it is free to
change/override any sub inside the parent.
2. There is no way to stop code from altering a class directly (eg, by doing
something like 'sub Object::_internal { something_unexpected() }'
3. The fixes I've so far run across (my'd hashes keyed on blessed references to
store instance data instead of using hashes for the base instance structure,
my'd subroutines for truly private methods) work (well, as far as they can
considering 1 and 2) but make inheritance an absolute nightmare - children can't
get hold of parent's protected data. Writing accessor methods for them is
problematic because of 2 - the accessor can be replaced to return something else.
4. Even if perl strictly adhered to OO paradigms I've learnt, some of these
problems are very hard to fix.
I've been thinking of theoretical ways to fix problems like this, and my
amateurish ideas so far have been:
1. Closing packages - from then on, no other code can enter that package's
namespace to specify subs, ie this:
package Example is closed; # Made up syntax - maybe should be fatal if the
# Example namespace already exists
our($Bar) = '123';
sub foo { ... }
package main;
sub Example::foo { ... } # Fatal - modify closed package
sub Example:ther { ... } # Fatal - add to closed package
$Example::Bar = '321'; # OK - not changing a sub
package OtherExample;
use base qw/Example/;
sub foo { ... } # OK - this is an override, doesn't
# alter the Example package
This could be expanded to making packages 'append only' (only new subs added) as
well etc etc.
2. Closing subs - from then on, child packages and external code cannot override
a particular sub:
package Example;
sub foo is closed { ... } # More made up syntax. Maybe a warning if
# Example::foo is already defined
sub bar { ... }
package OtherExample;
use base qw/Example/;
sub foo { ... } # Fatal - the sub is closed and cannot be
# overriden, even by children
sub bar { ... } # OK
package main;
sub Example::foo { ... } # Fatal - same reason as above
3. 'Closed Object Families' or 'Willed Inheritance'. This one breaks the idea
that a parent need not know anything about its children a bit, but it limits who
can inherit from it:
package Example wills qw(OtherExample # Made up syntax - 'wills'
MoreExamples); # as in 'I hereby will my
# stuff to the following
# children...'
....
package MoreExamples;
use base qw/Example/; # OK - this is one of our 'trusted' children
....
package UnexpectedExample;
use base qw/Example/; # Fatal - not one of our trusted children, not allowed
# to inherit.
Using mixes of these, you can start to be confident to write accessors for
objects for finding out if they can do something potentially dangerous, or for
accessors to find out parameters for potentially dangerous actions, eg:
package Example is closed, wills qw(OtherExample);
my %Private;
sub new {
my $class = shift;
my $self = bless('', $class); # short version
$Private{$self} = {CanDelete => 0,
ID => 'someid'};
return $self;
}
sub can_delete is closed {
my $self = shift;
return 0 if not exists $Private{$self}; # Safe default
return $Private{$self}{CanDelete};
}
sub do_delete {
my $self = shift;
return 0 if not $self->can_delete;
# Do the delete...
return 1;
}
Now instances of Example and OtherExample can safely assume that:
$eg->can_delete
hasn't been overridden like:
sub Example::can_delete { return 1 }
(and do_delete hasn't been modified by 'untrusted' code), so can be used in
arbitrary code as library code (...I think )
This is all just musings off the top of my head - for all I know perl has some
of this already that I don't know about, and I haven't descended to the level
below (how can I be sure the user loaded the right module and not something from
a private directory etc), _and_ nothing here defines what happens if a package
tries to inherit from a package that has inherited from a _willed_ package (sheesh!)
All up I think I've been thinking too much about this stuff anyway
MB
I've been spending a lot of time lately writing a lot of objects for a project
here (so far about 60 objects and counting), and while I've been considering
security the whole way I've started thinking one level down on how to secure
objects from direct alteration if they were used as a library in some other
code. (At the moment this isn't _really_ an issue as the packages are not
available to users unless they go through controlled programs that don't allow
unknown code to run).
After tinkering, I've come up with the following pseudo-rules (please correct me
if I'm wrong here, and preferably give me pointers
1. If an object inherits from another object (in perl), it is free to
change/override any sub inside the parent.
2. There is no way to stop code from altering a class directly (eg, by doing
something like 'sub Object::_internal { something_unexpected() }'
3. The fixes I've so far run across (my'd hashes keyed on blessed references to
store instance data instead of using hashes for the base instance structure,
my'd subroutines for truly private methods) work (well, as far as they can
considering 1 and 2) but make inheritance an absolute nightmare - children can't
get hold of parent's protected data. Writing accessor methods for them is
problematic because of 2 - the accessor can be replaced to return something else.
4. Even if perl strictly adhered to OO paradigms I've learnt, some of these
problems are very hard to fix.
I've been thinking of theoretical ways to fix problems like this, and my
amateurish ideas so far have been:
1. Closing packages - from then on, no other code can enter that package's
namespace to specify subs, ie this:
package Example is closed; # Made up syntax - maybe should be fatal if the
# Example namespace already exists
our($Bar) = '123';
sub foo { ... }
package main;
sub Example::foo { ... } # Fatal - modify closed package
sub Example:ther { ... } # Fatal - add to closed package
$Example::Bar = '321'; # OK - not changing a sub
package OtherExample;
use base qw/Example/;
sub foo { ... } # OK - this is an override, doesn't
# alter the Example package
This could be expanded to making packages 'append only' (only new subs added) as
well etc etc.
2. Closing subs - from then on, child packages and external code cannot override
a particular sub:
package Example;
sub foo is closed { ... } # More made up syntax. Maybe a warning if
# Example::foo is already defined
sub bar { ... }
package OtherExample;
use base qw/Example/;
sub foo { ... } # Fatal - the sub is closed and cannot be
# overriden, even by children
sub bar { ... } # OK
package main;
sub Example::foo { ... } # Fatal - same reason as above
3. 'Closed Object Families' or 'Willed Inheritance'. This one breaks the idea
that a parent need not know anything about its children a bit, but it limits who
can inherit from it:
package Example wills qw(OtherExample # Made up syntax - 'wills'
MoreExamples); # as in 'I hereby will my
# stuff to the following
# children...'
....
package MoreExamples;
use base qw/Example/; # OK - this is one of our 'trusted' children
....
package UnexpectedExample;
use base qw/Example/; # Fatal - not one of our trusted children, not allowed
# to inherit.
Using mixes of these, you can start to be confident to write accessors for
objects for finding out if they can do something potentially dangerous, or for
accessors to find out parameters for potentially dangerous actions, eg:
package Example is closed, wills qw(OtherExample);
my %Private;
sub new {
my $class = shift;
my $self = bless('', $class); # short version
$Private{$self} = {CanDelete => 0,
ID => 'someid'};
return $self;
}
sub can_delete is closed {
my $self = shift;
return 0 if not exists $Private{$self}; # Safe default
return $Private{$self}{CanDelete};
}
sub do_delete {
my $self = shift;
return 0 if not $self->can_delete;
# Do the delete...
return 1;
}
Now instances of Example and OtherExample can safely assume that:
$eg->can_delete
hasn't been overridden like:
sub Example::can_delete { return 1 }
(and do_delete hasn't been modified by 'untrusted' code), so can be used in
arbitrary code as library code (...I think )
This is all just musings off the top of my head - for all I know perl has some
of this already that I don't know about, and I haven't descended to the level
below (how can I be sure the user loaded the right module and not something from
a private directory etc), _and_ nothing here defines what happens if a package
tries to inherit from a package that has inherited from a _willed_ package (sheesh!)
All up I think I've been thinking too much about this stuff anyway
MB