Home > Zend Framework > Security with Zend_AMF and Flex – Part 2: Practise

Security with Zend_AMF and Flex – Part 2: Practise

April 15th, 2009

In my previous post “Security with Zend_AMF and Flex – Part 1: Theory“, I explained the theory behind securing your Flex-PHP calls. After the theory comes the practise. I will only provide snippets for the PHP side of this story, as I’m totally ignorant about Flex and ActionScript. I used Zend_AMF, written by Wade Arnold, to handle all the communications between Flex and PHP. If you need to know the basics, please read the documentation first.

Project structure

For the project, I used a slightly modified Conventional Modular layout. The basics remains the same, but I have added support for so called versions of the application. The layout I maintain is not really important for the project. However, at the end of the article, you’ll be able to download everything, and I thought it necessary to explain the structure first. All code is part of the default module, and all classes are located in the “models” directory.

Application layout

Application layout

The remoting endpoint

Flex and AIR need to connect to an endpoint. A URL you provide, the point of access to your API. I have created only 1 endpoint. Ideally, you should make 2 endpoints: one for logging in, and one for the rest of the calls. That way, in the future, you could do the login process via an SSL secured line. The remoting endpoint is located in the IndexController.php, since that file is the door to the outside world for the project.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	// Explicitly loading dependencies
	Zend_Loader::loadClass("Proxies_AMF_System");
	Zend_Loader::loadClass("Proxies_AMF_Login");
	Zend_Loader::loadClass("AMF_VO_Error");
	Zend_Loader::loadClass("AMF_VO_Message");
 
	class IndexController extends Zend_Controller_Action{
		/**
		 * Everything to handle the rest of the API requests
		 */
		public function indexAction() {
			$this->getHelper("Layout")->disableLayout();
			$this->getHelper("ViewRenderer")->setNoRender();
 
			$server = new Zend_Amf_Server();
			$server->addDirectory(dirname(__FILE__) . "../models/");
			$server->setClassMap("MessageVO","AMF_VO_Message");
			$server->setClassMap("ErrorVO","AMF_VO_Error");
			$server->setProduction(false);
			$response = $server->handle();
			echo $response;
		}
	}

Basically, the indexAction is where my endpoints is. There I set up the Zend_AMF server. After setting it up, the server is ready to listen to requests. All requests are handled by the classes from the folder specified with addDirectory(). In our case, the Proxy classes will handle all the requests. After specifying the directory of the classes, we set up the class mapping. Here, we tell Zend_AMF, how the mapping should be between our PHP classes on the server side, and the Flex classes on the client side. In my case, all Flex classes end with “VO”. Once the mapping is complete, our server is ready to give a response to the client. Note that I explicitly load every class in IndexController.php. For some reason, Zend_AMF isn’t able to use autoloading, which is a too bad.

Responses to the client

To have a cleaner interface, I have opted to return structured responses to the Flex client. When the client makes a call, there are only 2 kind of responses he can expect. When the call is successful, the response will be an object of the type AMF_VO_Message that’s being mapped to a MessageVO in Flex. Or it will be an object of the type AMF_VO_Error, mapped to ErrorVO, when an exception is thrown. This is very easy for the client side coder. All other data is encapsulated in the AMF_VO_Message object, and that makes it easy to process everything. It’s also easier for the PHP developer, since it is now possible to return a lot more information to the client. If you decide to use caching, then the AMF_VO_Message response can contain information about that; timestamps; transaction id’s; … Anything you would expect an API to return.

1
2
3
4
5
6
7
8
9
10
11
12
    /**
     * This class is used to wrap data for output to the AMF adapter
     *
     */
    class AMF_VO_Message {
        // Properties:
        public $status		= null; // string
        public $timestamp	= null; // string
	public $transaction	= null; // string
	public $data		= null; // mixed
	public $cachehit	= false; // boolean
    }

The class is basically a collection of public properties, where I can add data that is useful for the client, like timestamps, transaction-id’s, …

1
2
3
4
5
6
7
8
9
10
11
12
    /**
     * This class is used to wrap data for output to the AMF adapter
     *
     */
    class AMF_VO_Error {
        // Properties:
        public $timestamp	= null; // string
        public $errorCode	= null; // string
        public $file		= null; // string
        public $linenumber	= null; // integer
        public $messages	= null; // string
    }

Again, some properties that are useful for error tracking.

First phase: login security

In the first part of this series, I explained how securing the login process works. To initialize a login procedure, the client first needs to notify the server that an authentication attempt will be made. This is done by means of the challenge() function from the Proxies_AMF_Login class. Remember, all available calls are exposed via my so called Proxy classes. This is not mandatory, but a convention I want to impose on the users of the API I’m going to write. In Flex, the front-end developer can specify the class (proxy) and method (API call) to invoke.

When a client request a challenge from the server, I don’t necessarily want to know. Therefore, I can keep this function clean and simple. All code is explained with in-line comments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
 * Login proxy class to expose classes to AMF
 *
 */
class Proxies_AMF_Login extends Proxies_AMF {
        /**
         * Start the authentication session, by requesting a challenge.
         * The client then uses this challenge to calculate a signature (or digest)
         * When signing on, the username & signature is used, instead of the password
         *
         * @return AMF_VO_Message
         */
        public function challenge(){
		$arguments = func_get_args();
		return $this->processRequest(__FUNCTION__,$arguments);
	}
 
        protected function _challenge(){
            // 1. generate the challenge
            $challengeString = AMF_Login::generateChallenge();
            // 2. remember the challenge !
            $amfNamespace = new Zend_Session_Namespace('AMF_Auth');
            $amfNamespace->challenge = $challengeString;
            // 3. return the challenge to the client
            return $challengeString;
	}
 
        /**
         * Authenticate the user
         *
         * @param string $username
         * @param string $signature
         * @return bool|AMF_VO_Error
         */
        public function signOn($username,$signature){
			$arguments = func_get_args();
			return $this->processRequest(__FUNCTION__,$arguments);
	}
 
	protected function _signOn($username,$signature){
            // 1. Digg up the challenge from our memory:
            $amfNamespace = new Zend_Session_Namespace('AMF_Auth');
            $challenge = $amfNamespace->challenge;
 
            // 2. Verify if we can authenticate the user
            if (AMF_Login::signOn($username,$challenge,$signature)){
            	// User authenticated, let's forget the challenge
                unset($amfNamespace->challenge);
                return true;
            } else {
            	// Failed to authenticate the user
            	unset($amfNamespace->challenge);
		Zend_Auth::getInstance()->clearIdentity();
                throw new Exception("Could not authenticate the user");
            }
	}
}

A challenge() call is logically followed by a signOn() request (verifySignature() will be explained later on) to complete the login process. As you can see, for both challenge() and signOn(), there is a protected counterpart _challenge() and _signOn(). This has to do with the processRequest() call in both of the methods. More about that call later on, but for now, you only need to know that processRequest() does some administrative tasks, and wraps the responses for Zend_AMF server in a structured response.

An attentive reader will have noticed that I make use of an AMF_Login class, and I forgot to explain what and how. No wories, it’s not very mystical. I have just created that class to bundle some functionality. However, it does use some functionality from classes I have not written myself. They fall under the copyright of my employer, and I don’t want to infringe on that. So I won’t show you how AMF_User is written, but from the comments, it should be obvious what the purpose is. In the downloadable source, you’ll find a simplified version of AMF_User, which doesn’t really do anything, except illustrating the purpose.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class AMF_Login {
	/**
	 * Returns a random string, that is used as Challenge for the sign-on
	 * procedure
	 *
	 * @return string
	 */
	public static function generateChallenge(){
		return uniqid(rand()+time(),true);
	}
 
	public static function signOn($username,$challenge,$signature){
		$user = new AMF_User();
		$result = $user->authenticate($username, $challenge, $signature);
 
		return $result;
	}
 
	/**
	 * Method to verify if each signature for an API call is valid
	 */
	public static function verifySignature($challenge,$signature){
		// 1. get the password from the session (it was put there during authentication)
		$amfNamespace = new Zend_Session_Namespace('AMF_Auth');
 
		// 2. calculate signature with given $challenge
		$calcSignature = md5(md5($amfNamespace->password) . $challenge);
 
		// 3. compare calculated signature, with given signature
		if ($calcSignature == $signature){
			return true;
		}
		throw new Exception("Incorrect signature given");
		return false;
	}
}

This concludes the first part of the security measures: logging in. By now, a user can be logged in, and stay logged in, by means of the browser session.

Second phase: API call security

In the second phase, I want to verify all API calls. What we need, is a way to process every API call, and do a number of actions for each of those call. I want to be able to:

  • Log what calls were made, by what user, what arguments, and what was the response
  • Have the possibility to cache certain calls
  • Use Zend_ACL to verify if a certain user has access to certain functionality
  • Create my structured response
  • Trap errors (yes, they should be trapped in the ErrorController.php, but I haven’t fully investigated that option yet)
  • Write code once, use it multiple times

As you can see, there are plenty of requirements. There is one option to fulfil them all: Make sure all calls invoke 1 function that handles all the “administration”. The perfect solution exists, and is named __call() in PHP5. If a method does not exist in a class, then the __call() magic function is called with the exact same arguments. There, you can create all functionality for the administration, and then request another function that will do the real work. I know, quite the abstract explanation, but I won’t go any further on that road because using __call() fails. Zend_AMF uses Zend_Reflection, a PHP reflection class. Simply put, a reflection calls detects all publicly accessible methods and properties of a given class. Since __call() is a magic function, it isn’t publicly accessible. Hence, it won’t be discovered by Zend_Reflection, and consequently, Zend_AMF will not be able to make use of your functionality if you rely on __call(). That took me a while to discover, but if you think about it, it’s quite logical.

To work around this problem, I have decided to create my own __call() method, and I have named it processRequest(). This will do the exact same as I have explained in the paragraph above: handle the administration (logging, caching, …). The diagram below will immediately shine a light on the working.

Diagram: handle requests

Diagram: handle requests

A request comes in via helloWorld(). It then travels to processRequest() for the administrative tasks. Next it travels to _helloWorld() that will do what needs to be done and give a response. The response then will travel back from _helloWorld() to processRequest(). There, the response is packed in the structured response, and then that structured response is sent back to helloWorld(). There, it is delivered to the Zend_AMF server, and ultimately, to the Flex client.

To write the code only once and use it many times, the only option is to place the processRequest() function in the parent of our Proxy classes: Proxies_AMF.php. Below, you will find a simplified version of the code I have written for a recent project. Everything is explained in in-line comments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
 * AMF proxy class to expose classes to AMF
 *
 */
class Proxies_AMF {
	// Some classes do not need verification, like Login. Only the other API calls should be verified
	private $exemptVerification = array("Proxies_AMF_Login");
 
	protected function processRequest($method,$arrArguments){
		try {
			$className = get_class($this);
 
			// Retrieve the parameters to verify the identiy of the requesting party:
			if (!in_array($className,$this->exemptVerification)){
				$challenge = array_shift($arrArguments);
				$signature = array_shift($arrArguments);
			}
 
			// Verify the given signature, before executing the call, unless the request is to a method from an exempt class:
			if (in_array($className,$this->exemptVerification) || AMF_Login::verifySignature($challenge,$signature)){
				// Verify if the requested method actually exists:
				if (method_exists($this,"_{$method}")){
					$cachehit = false; // support for caching could be added, but not now.
					$returnValue = call_user_func_array(array($this,"_{$method}"),$arrArguments);
 
					// wrap the response of the API call in an AMF_VO_Message object, and return it:
					return $this->wrapOutput($returnValue,$signature,$cachehit);
				} else {
					throw new Exception("The method '{$method}' does not exist.");
				}
			} else {
				// An exception is already thrown in AMF::Login::verifySignature()
			}
		} catch (Exception $e){
			$errorVO = new AMF_VO_Error();
 
			// Get the currently logged in user
			if (Zend_Auth::getInstance()->hasIdentity()) {
				$user = Zend_Auth::getInstance()->getIdentity();
				$errorVO->user_id = $user->user_id;
			} else {
				$errorVO->user_id = 0;
			}
			$errorVO->timestamp		= strftime("%Y-%m-%d %H:%M:%S");
			$errorVO->errorCode		= get_class($e); // usually, this will be "Exception", unless we introduce custom Exceptions
			$errorVO->file			= $e->getFile();
			$errorVO->linenumber		= $e->getLine();
			$errorVO->messages		= array($e->getMessage());
			$errorVO->stacktrace		= debug_backtrace();
 
			// return response
			return $errorVO;
		}
	}
 
	private function wrapOutput($data,$transaction,$cachehit){
		$message				= new AMF_VO_Message();
		$message->status		= "success";
		$message->timestamp	= strftime("%Y-%m-%d %H:%M:%S");
		$message->transaction	= (!is_null($transaction))?$transaction:"no_transaction";
		$message->data		= $data;
		$message->cachehit		= $cachehit;
 
		return $message;
	}
}

The function processRequest() is now very basic. It only incorporates authentication of the requester, and catching errors. I will not implement the rest of the requirements during this article, since that would lead me outside the scope of this article. If you feel like implementing extra functionality for all your calls, then processRequest() is the method to elaborate on. For a business project, I have extended it with everything mentioned in my list earlier.

The hard part is over now. Most of the functionality has been written. Each API call will be accompanied by 2 extra parameters: a client-chosen challenge, and a calculated signature. The given challenge and signature are just the first two parameters of each of our service calls. These parameters will be verified in processRequest().

The actual verification is done by the verifySignature() method in AMF_Login class. To make it easy, below is a copy of that method again. The parameters are the challenge, chosen by the client, and the signature, calculated by the client. The method will then use that same challenge, and the password stored in a session object, and then calculate the accompanying signature. If both signatures match, then we have a winner! If they don’t match, then somebody is trying to tamper with the requests. It’s up to you how you deal with that. I just output an AMF_VO_Error object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * Method to verify if each signature for an API call is valid
 */
public static function verifySignature($challenge,$signature){
	// 1. get the password from the session (it was put there during authentication)
	$amfNamespace = new Zend_Session_Namespace('AMF_Auth');
 
	// 2. calculate signature with given $challenge
	$calcSignature = md5(md5($amfNamespace->password) . $challenge);
 
	// 3. compare calculated signature, with given signature
	if ($calcSignature == $signature){
		return true;
	}
	throw new Exception("Incorrect signature given");
	return false;
}

With this last functionality explained, I think I have everything covered. Only thing left, is giving you an example on how our processRequest() method is used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * System proxy class to expose classes to AMF
 *
 */
class Proxies_AMF_System extends Proxies_AMF {
	/**
	 * Say hello to the world
	 *
	 * @return string
	 */
	public function helloWorld(){
		$args = func_get_args();
		return $this->processRequest(__FUNCTION__,$args);
	}
 
	protected function _helloWorld(){
		return "Hello World !!";
	}
}

I mentioned the helloWorld() function earlier, and here is the implementation. The Flex client calls the helloWorld() function (without the underscore !!), and gives as paremeters the challenge and signature. You’ll see that those parameters aren’t explicitly mentioned in the function definition, but that is no problem. In that function, processRequest() is called, which will do the verificiation and then call _helloWorld(). The response from _helloWorld() will then travel back, over processRequest(), via helloWorld(), to Zend_AMF, stopping at the Flex client.

Ammendments

This article is the fruit of a quite lengthy development process at the company I work at. During this period, I have made many mistakes regarding Zend_AMF, and learned from them. To illustrate that development is a continuous learning process, I will end with a “mistake” I just recently discovered:

When I first implemented these security measures, I used the URL to transport the challenge and signature for each API call. This way, could keep my interfaces clean, without having to take into account the challenge and signature. In Flex, I had the front-end developer create a dynamical endpoint for each call. Each endpoint connection only made one call, since in the URL, the challenge and signature were added. However, when an endpoint connection is established, it is standard procedure of AMF to ping the server once. As a result, each call generated a new ping request, effectively doubling the amount of calls. I don’t have to explain that this is quite a big overhead. To overcome this, I had to extend each API call to accept the challenge and signature as a parameter of that call. And the result of my mistake is that you can enjoy a “better” implementation than what I had 2 days ago.

What I want to say: This method is not fool proof, and I have no doubt that there are probably many ways of improving the security. If you happen to have suggestions, by all means, let me know. I’m happy to learn from you.

Download all files: zip | tar
Credits where credits are due: Thanks Vic Rau for the small UnitTest (yes, it is also in the downloadable archive)

Share and Enjoy:
  • DZone
  • del.icio.us
  • StumbleUpon
  • Digg
  • Ma.gnolia
  • Technorati
  • TwitThis

Tom Zend Framework , , ,

  1. Lasse
    April 19th, 2009 at 15:31 | #1

    Great read, thanks for sharing. I can follow most of your ideas, but your setup in /flex/index.php I don’t completely understand. What is CB_Application? and CodeBase? Is it your zend library? – if so what is CB/Application.php – it is not in the default zend distribution.

    But overall I think this article was an interesting read, however it does require quite a bit of prerequisite knowledge.

  2. April 19th, 2009 at 22:36 | #2

    Hi Lasse,

    My index file is just the bootstrap file. I have created my own class for that, because I had some requirements. If you want to use my code, you should just use your own bootstrap file instead of mine.

    Your comment is right when you say you need prerequisite knowledge. I just wanted to explain about Zend_AMF security, and not about Zend Frameowork setup. If you would need more specific details, just let me know. Perhaps I can work something out in a next blog post.

  3. Lasse
    April 21st, 2009 at 21:49 | #3

    Right, got it :)

    Luckily I have most of the knowledge, but I do however think you could have included the source code for the flex project, so others might study it. I don’t believe a walkthrough of it’s code is necessary, but include it as a download maybe?

  4. Lasse
    April 21st, 2009 at 22:19 | #4

    I mean, how do you in flex reference the class’s in your project? Normally I have all my services in one folder (in your case that would be the models folder). And then you would reference it like:

    But in your case the class’s are structured in folders, and when I try:

    I get this error:
    [User Login] – Error Connecting![FaultEvent fault=[RPC Fault faultString="Send failed" faultCode="Client.Error.MessageSend" faultDetail="Channel.Connect.Failed error NetConnection.Call.BadVersion: : url: 'http://www.domain.com/zend_framework_test/public/remoting/amf'"] messageId=”1D594DC5-5F0F-D1C9-2404-CA51189B473C” type=”fault” bubbles=false cancelable=true eventPhase=2]

  5. Lasse
    April 21st, 2009 at 22:21 | #5

    Oh code is stripped, see here: http://pastebin.com/m4b052675

  6. Lasse
    April 22nd, 2009 at 12:41 | #6

    A little tip I found, instead of explicitly loading every class in your IndexController you could setup multiple include paths in your bootstrapper and use auto loading, see this pastebin: http://pastebin.com/f5098700e

    Now you don’t have to specify any loading in IndexController.php :)

  7. April 22nd, 2009 at 15:05 | #7

    I’m sorry, I have no idea how the Flex code works, so I don’t know how to help you with it.
    However, the author of the Flex example will clean up his code first, and maybe then I can offer it as a download.

    Thanks for your tip about the include paths. I’m not sure if that would suffice though. It’s Zend_Reflection that complains if I don’t include the files, and rely on autoload. But I’ll test it out anyway.

  8. Lasse
    April 22nd, 2009 at 17:42 | #8

    Okay, I’m using this setup http://www.riaspace.net/2009/01/zend_amf-with-full-zend-framework/ – it work’s great. I have the flex part working, so if anyone else has trouble figuring out what source property to use its just the class name, example: Proxies_AMF_Login

  9. Lasse
    June 3rd, 2009 at 00:30 | #9

    In your verifySignature() function you use: $amfNamespace->password, but the password has never been saved to $amfNamespace? Isn’t that an error, and shouldn’t it be stored right after succesfull signOn in Proxies_AMF_Login after line 48? Is it secure to store the password as plain text in an Zend_Session_Namespace or should it be hashed?

  10. Lasse
    June 3rd, 2009 at 01:01 | #10

    And also, the way you make the client send a challenge and calculated signature in order to verify API calls, doesn’t that enable man-in-the-middle-attacks? A hacker could monitor the client and intercept the challenge and signature transmitted for further API calls. And then use that intercepted information combined with session-spoofing in order to actually fool the server in to thinking that he (the hacker) is the authenticated client, thus enabling him to make API calls? All that is required is that the user logs in and makes one API call, then the hacker could have all the information he needs – or am I mistaken?

  11. June 3rd, 2009 at 16:20 | #11

    Lasse, I don’t know if you have downloaded the archive with the code. But there, in the class AMF_User I have added (dummy)code that does the authentication. During the authentication process, the password is stored in the session namespace.

    Sending the challenge & signature together in a request doesn’t pose a man-in-the-middle risk according to me. As long as the attacker doesn’t know the password, it is impossible to forge a fake signature. And since the signature is a hash from a hash, it won’t be that easy to reverse-engineer the signature until you find out the password.

    I’m not familiar with how you can spoof a session. Even if the attacker can succesfully pretend he is the authenticated user, he still needs to know what the password is, in order to create signatures. That is the point of the signature. The service won’t just accept that you are logged in (that would be easy for session hijackers), but the service also demands that you give proof that you really are the correct user, by requesting a signature for each service call. Hijacking or spoofing the session doesn’t change that fact.

    Actually, this same concept is used on digital bank transfers too, with the so called tokens. You receive a challenge from the site, and then you have to use that challenge, your bank card + pincode and that token device, to generate a signature. With this, you gain entry to your accounts. Every time you make a transaction, you need to digitally sign it. The site gives you a challenge, and then again you need to calculate a signature. (At least, that’s how it’s done in Belgium, where I live)

  12. Lasse
    June 4th, 2009 at 22:31 | #12

    Hello Tom, the specific situation I’m thinking of is: The session has been hijacked and the hijacker has also intercepted the client generated signature and challenge, which he is now able to use, without ever having known the password? Am I mistaken or would that not enable him to make api calls as an authenticated user? Also wouldn’t it be a good idea to do Zend_Session::regenerateId(); on each request, in order to make the chance of session hijacking as low as possible?

  13. June 5th, 2009 at 11:17 | #13

    Hey Lasse,

    You have probably found a vulnerability in my system yes :)
    Perhaps Zend_Session::regenerateId() is a possible solution.
    Another solution would be that the challenge is in fact a checksum of the data you want to submit. This way, a hijacker could intercept the call, but he wouldn’t be able to tamper with the data, or the checksum & signature won’t be valid.

    Then it doesn’t matter if the attacker can manipulate the data and checksum (challenge). The signature won’t be valid, so the call won’t succeed.

    How does that sound? Perhaps this in combination with regenerateId().

    Thanks for pointing out the hole. It’s actually quite obvious, but I hadn’t looked at it this way :)

  14. seb
    July 2nd, 2009 at 22:42 | #14

    What about the flash client side, wich we can decompile and get the signature. Then, we brute force for a user:pass and boom…

  15. July 2nd, 2009 at 22:51 | #15

    Hi Seb,

    i don’t believe that with decompiling the flash, that you can do a lot of mischief. It would be possible for you to find out how all the hashing is done, and what the order of the parameters is. But then you would still need the user & password of someone who is known on the server side. If you brute force a user & pass that is not known on the server, then the server won’t be able to verify the validity of the signature. And then a call won’t be allowed.

    If you know a user & password, then there’s no need for you to decompile the flash, since you will just be able to log in, and do everything there. As I said before, as soon as the password is discovered, no security will matter.

  16. seb
    July 3rd, 2009 at 16:35 | #16

    I mean, since i have an error when the signature isnt good, i can bruteforce the user:pass and then all this is useless. But, this
    technique will indeed generally work.

  17. Rich
    September 4th, 2009 at 02:03 | #17

    I would not recommend storing the password in session. It’s just not secure. Nice try to replace SSL.:)

  18. Skuip
    November 23rd, 2009 at 15:59 | #18

    I think you got a problem with verifySignature($challenge,$signature). What is preventing a hacker from using the same challenge and signature hashed for an other API call (replay attack)? Probably can solve it by using a challenge that’s incrementing and enforcing that every received challenge is bigger than the last received challenge. That way an attacker can’t reuse a challenge and signature pair.

  19. November 23rd, 2009 at 16:38 | #19

    Hi Skuip, thanks for your feedback.
    I agree that the problem you describe is possible.

    The way I have used this setup, is by also implementing a transaction system: I keep track of all challenge/signature pairs. If I find a duplicate key for a pair during the session, I don’t execute the call, but simply return what was returned previously.

    This was not really to prevent such an attack, but rather to prevent an application of submitting twice the same data (while only once was needed). Like a user who is impatient, and refreshes the page, thus submitting the data again.

    I haven’t described the transactions in this blogpost, because I only implemented that part later on (and was too busy to blog about it).

    Do you think this is a viable solution to the problem you described? I’m open to input, because I would like to keep the setup as secure as possible.

  20. Skuip
    November 26th, 2009 at 18:26 | #20

    That means the challenge/signature pair should be remembered forever, because at the moment you decide to delete challenge/signature pair it becomes valid again for any API call.

    One thing you maybe can do is to generate challenge by creating a checksum based on the user name, method name and its parameters and repeat the same on the server to check/validate the challenge. It does not prevent any replay attack, but at least he can not change the method or any its parameters.

    But I still think it is better to create some incrementing challenge, so you can refuse any challenge that is not bigger than the last one. Maybe use both by generating a challenge and appending it with an incrementing counter. That way you can easily determine if the challenge is new and valid. The extra challenge data may slow the brute-force attempt by the attacker.

    A completely different way is to go with public/private key encryption and encrypt something what the server can verify after decryption with its private key. But public key encryption can be quite expensive and I do not have any ready available solutions. And there is the fact that public/private key encryption is not a standard Flash/Flex component. There is an as3crypto library, but who knows how bug free that is.

  21. Titi
    February 24th, 2010 at 18:19 | #21

    Hi Tom
    I know that this blog entry is a little bit old but it’s very helpful to me. Do you know if your colleague would agree to show me his flex code ? Because it’s the hardest part for me in the authentication process with zend_amf…

    Thanks a lot

  1. May 7th, 2009 at 23:06 | #1