How could not using “final” be a security issue?

2019-04-06 07:33发布

问题:

From page 113 of O'Reilly's Essential ActionScript 3.0 (2007):

Methods that are final help hide a class’s internal details. Making a class or a method final prevents other programmers from extending the class or overriding the method for the purpose of examining the class’s internal structure. Such prevention is considered one of the ways to safeguard an application from being maliciously exploited.

Does this refer to users of the API of a compiled, closed-source package, and "maliciously exploited" to learning things about the class design? Is this really a problem?

For some more context, it's the second of two reasons to use final. In the 2007 edition, it's on page 113 in the chapter Inheritance under subtitle Preventing Classes from Being Extended and Methods from Being Overridden.

The final attribute is used for two reasons in ActionScript:

  • In some situations, final methods execute faster than non-final methods. If you are looking to improve your application’s performance in every possible way, try making its methods final. Note, however, that in future Flash runtimes, Adobe expects non-final methods to execute as quickly as final methods.

  • Methods that are final help hide a class’s internal details. Making a class or a method final prevents other programmers from extending the class or overriding the method for the purpose of examining the class’s internal structure. Such prevention is considered one of the ways to safeguard an application from being maliciously exploited.

回答1:

In many languages, overriding methods is opt-in from the base class. Often, it is the virtual keyword that allows base class authors to opt-in for the possibility of overriding.

In AS3, however, the ability to have methods overridden is opt-out. That is what the final keyword does. It allows the base class author to say "this method may not be overridden".

There are some old-school thoughts about encapsulation that would suggest that it is a security problem for AS3 to do it this way. But this is mostly in cases of public APIs in which you want to hide your content but expose the functionality.

But, in more modern times, we have learned that disassembly and reflection will allow a malicious developer to do anything he/she wants anyways, so this is less of an issue today. Relying on final for security is a crutch, in my opinion, and any suggestions of it should be dismissed. Security needs to be thought of more carefully than that. APIs need to be architected such that the implementation lets developers do what then need to do, but security-critical information should not be included in public APIs.

That is not to say that final is not useful. final tells developers that derive from your class that you never intended them to override the function. It lets you say "please just call this function. Don't override." It is more of an interface or a communications mechanism than anything else, IMO.



回答2:

final keyword is not used for this kind of security. It is not a substitute for what would normally require a cryptographic solution.

What is usually meant by "security" in these kinds of discussions is the concept of a secure object model - that is to say an object model that cannot be manipulated by consumers for purposes unintended by the original author of the class.

It's Sort of, a well-constructed class will encapsulate its state and a class that only has final methods will not allow consumers to override the behavior of the class and change how the state is encapsulated. A class could expose its state (through public fields for example) and no final keyword would be able to protect the integrity of that state.

It is more about "changing" the stuff rather than "securing". The final keywords simply puts away the ability to change/modify/extend any method.

It doesn't make your code more secure, it is more for thread safety than anything else. If a variable is marked final, a value must be assigned to it when the object is created. After object creation, that variable cannot be made to refer to another value.

This behavior allows you to reason about the state of an object and make certain assumptions when multiple threads are accessing it concurrently.

I don't think that making a field final would add security against malicious attacks (more likely against mistakes and of course threading issues). The only "real form" of security is that if you have a final constant field it might get inlined at compilation so changing its value at runtime would have no impact.

I've heard of final and security more in the context of inheritance. By making a class final you can prevent someone from subclassing it and touching or overriding its protected members, but again I would use that more to avoid mistake than to prevent threats.



回答3:

Assume you release some fancy SWC Library to the public. In this case you can prevent a method from beeing overridden.

package
{
    import flash.display.Sprite;

    public class FinalDemo extends Sprite
    {
        public function FinalDemo()
        {
            super();
            var someClientInstance:ExtendedAPIClient = new ExtendedAPIClient();
            // doSomething is overridden by ExtendedAPIClient
            someClientInstance.doSomething();
            // activate cannot be overridden
            someClientInstance.activate("mySecretAPIKey");

            var myApp:MySupaDupaApplication = new MySupaDupaApplication(someClientInstance);

        }
    }
}

/**
 * Assume this class is within a swc that you release to the public.
 * You want every developer to get some APIKey
 * */
internal class MySupaDupaApplication{
    public function MySupaDupaApplication($apiClient:APIClient):void{
        if($apiClient.activated)trace("It's a valid user, do something very cool");
    }
}

/**
 * In order to activate a Client the developer needs to pass a 
 * instance of the API Client to the Application.
 * The application checks the activated getter in order to determine
 * if the api key is valid.
 * */
internal class APIClient{

    private var __activated:Boolean = false;

    public function APIClient(){        
        trace("APIClient Constructor");
    }

    /**
     * override possible
     * */
    public function doSomething():void{
        trace("doing something");
    }

    /**
     * override not possible
     * */
    public final function activate($key:String):void{
        trace("activate "+$key);
        if($key == "mySecretAPIKey"){
            __activated = true;
        }else{
            __activated = false;
            throw new Error("Illegal Key");
        }
    }

    /**
     * override not possible
     * */
    public final function get activated():Boolean{
        return __activated;
    }   
}

/**
 * Class within some developers library using MySupaDupaApplication
 * Changes the Client behaviour
 * Exploit of activation not possible
 * */
internal class ExtendedAPIClient extends APIClient{

    public function ExtendedAPIClient(){
        trace("ExtendedAPIClient Constructor");
        super();
    }

    override public function doSomething():void{
        trace("doing something else");
    }

    /* this will throw a compiler error */
    /*
    override public function activate($key:String):void{
        // do nothing
    }

    override public function get isActivated($key:String):Boolean{
        return true;
    }   
    */  
}