views:

368

answers:

6

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?

+16  A: 

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.

John Saunders
Amazing to unexplained downvotes after all this time.
John Saunders
+7  A: 

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.

NewCom
@John and @NewComLet me try to understand: In a classic secured web app, you have operation Login that returns the result (t/f). Each following operation depends that Login performed earlier was true. How is Login in this example any different than EnoughFunds?
Dan
This is *not* how classic web apps work. There would be a Login operation that returns a token. The token needs to be supplied on all subsequent calls. Without supplying that token, no subsequent call would be permitted.NEVER depend on the client for ANYTHING!!!
John Saunders
Rather, you can depend on the client to be under the control of a hacker who is trying to put you out of business and make you look like a fool in the process. The client is your *enemy*, and there are hundreds of millions of them!
John Saunders
@John, in a classic web app. I do depend on client to return the token don't I?I understand your answer as a best practice and I agree, but my requirement is as explained and I need not change services on the server.
Dan
But if the client doesn't return the correct token, then the service should simply return an error - not perform the transaction. Further, the token should be encrypted in such a way that only a properly-authenticated client could have sent it - and the token should have an expiration time.
John Saunders
Exactly! Now, how can I implement something that will make Transfer return an error if EnoughFunds was not performed before? Maybe EnoughFunds could return a token to flex client that client would have to send back when executing Transfer? (Sorry for being stubborn; I appreciate your time :) )
Dan
Dan, if you get that token good enough, it contains everything about the call to enoughFunds - timing, login status, input parameters, result. It's basically everything the server should know about it anyway, so why not let it make the call internally upon processing Transfer? see my answer, too.
Hanno Fietz
A: 

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)

adir1
A: 

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.
David Robbins
+4  A: 
Hanno Fietz
+3  A: 

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.

Dan