Question

I am designing the application in Flex that connects to some web services to perform some financial transactions. Web services are secured using https protocol and are asking for user token created at login on each request. This is used to authenticate and authorize the user. So far so good.

The trick part is that not all of our web services are coarsely grained. To give you an example, we can have two web service methods: EnoughFounds and Transfer. So, only after the method EnoughFounds replies “true” will I execute Transfer. This logic is programmed inside the Flex application code.

The scenario I am presented is the following: What if someone downloads the application and decompiles it. Than modifies the code so the step EnougFunds is not executed. Or maybe writes a completely new client maybe in even other technology that will execute Transfer without passing through EnoughFunds step. On executing Transfer, user will be authorized and authenticated on the server; but since he is using his real credentials, he will be able to execute the Transfer. The check that he skipped belongs to business logic and not security domain. I need somehow to make sure that the code that is executing the application is unmodified Flex code that I wrote and user downloaded. How can I do that? I know I can rewrite services so that the sequence is executed on the server, but that implies significant effort and I am looking for some other kind of solution.

It seems to me that there must be some security mechanisms that would resolve this particular problem.

Please note that I am not looking for advice on best practices. My requirement is not to change anything on the server-side. How can I secure the sequence on protocol level, without changing services?

Was it helpful?

Solution

This is a big mistake. Any business rules important enough to matter must be checked in the service. It must be the case that, no matter what the client does, the service never lets anything bad happen.

In particular, it makes sense to have an EnoughFunds operation because if EnoughFunds returns false, you can tell the user that he doesn't have enough funds. However, the Transfer operation must check to see if there are enough funds, and not depend on the client for such an important check.

OTHER TIPS

Let me add in this bit - I have kids decompiling my games and changing/editing the messages sent back and forth through tools that manipulate the http communication - and this is just for games that have no outside value. My users actually load up fiddler and firefox header tools to manipulate service calls just to get a high score on a gameboard that resets itself everyday.

I shudder to think what would happen if I threw money or "real" value into the mix.

Do not trust any data the client sends your way... use the "enoughFunds" call to update your user-interface but when doing the "transfer" stage you need to re-evaluate that call purely on the server end - do not trust that just because the client asked for a transfer that you should accept it.

You cannot solve this without changing the server side because, as all other answers have noted, that's where the problem is. Also, there's going to be no suitable protocol that doesn't involve changing the server side.

It is quite appropriate to compare this to a person at the bank counter. That person has presented to the bank employee some credentials that verify who she is and that she actually owns some account X. That's the login.

Now the person wants to order a transfer from account X to Y. Whatever she has to say on her available funds is not going to matter. If she presented a signed document from the bank's president saying that she had enough funds, the bank still would have to check before the transfer was made because the money simply has to be there.

This means, even if you get a secure sequence logic into the protocol that ensures "EnoughFunds" has been called before "Transfer", and called only once, and not too long ago, and with the right parameters, it's still the client telling the bank "it's OK, I have enough funds". Which just isn't going to cut it, ever.

The closest you could get would be the equivalent of the bank client leaning over the counter, pointing at the employee's screen and saying "look, here, in your system, it says the transaction can be done because there are enough funds", i. e. the client can verifiably reproduce time, input and output of a call to EnoughFunds that the server just made, which is OK, but quite pointless.

So the solution is to have Transfer call EnoughFunds internally, period. If the client needs to know, you can return a descriptive Exception / Error in case there aren't enough funds.

It's like this.
(source: userfriendly.org)

I honestly think you need to seriously reevaluate your development plans. As others here have so correctly stated, you can not rely on the client's integrity.

Simply put, there is no such thing as 'secure, trusted code' outside of the environment you have control over (namely, the server - even then, it's debatable). You have no idea of the software you're actually talking to, so no matter how you may 'tweak' an application-layer protocol, you still have no guarantees that a bad actor isn't simply deceiving you with a reverse-engineered implementation of your 'tweaked' protocol.

Your 'domain of trust' extends only to the web service api and no further. If your service cannot validate the integrity of its operations, then it is a badly designed service.

I think you should consider a few things:

  • Tamper Proof URL
  • Expiring Web Pages
  • Combine your EnoughFunds and Transfer logic into a single call. As John Saunders stated, you have to check EnoughFunds before you transfer anyway, so it makes more sense to check it once.

First, I agree with other answers -- client is absolutely your enemy, and should never be trusted. To also expand on some comments you placed -- even if "EnoughFunds" returns some sort of encrypted token, whats the assure that it wasn't still called by the hacker with amount of 10, and subsequent transfer was activated with amount of 10000000.

The approach must be to place atomic business logic together, such as "EnoughFunds and Transfer".

I would recommend adding a sequence also, to every server call, so that older server calls can never be re-executed, since they are now out of sequence. Server should return "next sequence token" as some sort of "encrypted" number. This can be as easy just generate random number and return as part of response, while also placing it in server session and reconfirming it on any subsequent call from that client.

Again, this trick is not a security measure as much as simply trying to help avoid too easy of a "fiddlering" situation.

This can be combined with obfuscating your API. If you call web-services by their name (for ex: EnoughFunds service will be called just that), it is becoming that much easier to reverse engineer. Instead, do something as simple as enumerate services, and go through central controller - activateTask=12?param1=200 . This is still pretty easy to reverse engineer, though... So better if you can invest in encrypting each request altogether, to look like : SFASDFsdq1231sd4DFGTRsdf2rDF

And of course any such good request encryption, should be in-part based on the session-id (aka: login authentication token, usually)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top