Last build of my Flex 2/ColdFusion Security Homework
Ok, so this is the last revision of my Flex Thanksgiving homework. Thanks to everyone for the tips. My only purpose here was to learn a bit and I've definitely done that - thanks to you guys out there. So - enough kissing up. Let me talk about what I did in this last build.
First I expanded core.cfc to include a few new methods: unsecure and secure. They simply return random strings with the secure method adding a static string in front:
<cffunction name="unsecure" access="remote" returnType="numeric" output="false">
<cfreturn randRange(1,10000)>
</cffunction>
<cffunction name="secure" access="remote" returnType="string" output="false" roles="admin">
<cfreturn "secure function " & randRange(1,10000)>
</cffunction>
You may be wondering why I bothered with the unsecure method. I mean - I've done it before easily enough. My authenticate method wasn't secured. Frankly there was no good reason - I just wanted to be complete. Ignore the roles equals part. I'll be getting to that in a second.
To my main stage (remember I was using a ViewStack and I named my different pages as stages) I added two buttons:
<mx:Button id="unsecureButton" label="Call unsecured method." click="callUnsecure()" />
<mx:Button id="secureButton" label="Call secured method." click="callSecure()" />
These two buttons were what I used to test my methods. Now lets talk security. I started off this series saying I wanted to avoid ColdFusion's roles based security. I am not of fan of it. It is probably the only thing about ColdFusion I don't like. Turns out though that it is very darn handy when working with Flex. So handy that I decided to swallow my pride a little and just use it. How difficult is it to use in Flex? Incredibly difficult. Very complex. In fact, I shouldn't even tell you how to do this. I should charge outrageous consulting fees instead. (Actually more than a few people posted this.)
Basically what you do is set login information into your RemoteObject tag. I moved my username/password values out of the old method and into the global scope. I then used setRemoteCredentials. Here is my new function that is run after authentication:
private function checkAuthResult(event):void {
var result = event.result;
if(result == 'false') {
Alert.show("Authentication failed", "Errors", mx.controls.Alert.OK);
} else {
core.setRemoteCredentials(usernameValue,passwordValue);
mainView.selectedChild = mainStage;
}
}
The only real difference here from the last version is the setRemoteCredentials function. You can think of it like adding a badge to your remoteObject. Now all calls will also pass along authentication information. How do you use that? With the CFLOGIN scope. I added an Application.cfc file to the project and used this onRequestStart:
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfargument name="thePage" type="string" required="true">
<cflogin>
<cfif isDefined("cflogin.name") and isDefined("cflogin.password")>
<cfif application.core.authenticate(cflogin.name, cflogin.password)>
<cfloginuser name="#cflogin.name#" password="#cflogin.password#" roles="#application.core.getRoles(cflogin.name)#">
</cfif>
</cfif>
</cflogin>
<cfreturn true>
</cffunction>
So for those of you who have not used CFLOGIN, the basic thing to remember is that it can auto detect calls made with authentication data (not just from Flash/Flex) and will give you access to the username and password. I took these values and passed them to the authentication method. If it returns true (which it should), then I grab the roles (hard coded in my CFC as "admin") and run the CFLOGINUSER tag. So remember my secured method above that had roles=admin? It will now work correctly since the CFLOGINUSER tag will end up giving me a role of "admin."
So I said this was the last entry, but earlier today J Fernandes showed me a good way to make my source attribute dynamic based on FlashVars. So I'll do one more build tomorrow demonstrating that.
I'm happy this was so easy in the end. But - I'm not happy that I ended up using CFLOGIN. To me it is almost as bad as Evaluate(). How could I get around it? One idea would be to build a proxy CFC. This CFC would have contain the methods I need to run remotely, and each method would
- have a username and password attribute. These would get passed to an authenticate() method and logic would only continue if the authentication passed
- any other arguments would be passed in as a structure and then expanded as arguments to the "real" CFC behind the scenes
This seems like extra work. But it does get rid of CFLOGIN. It also lets me keep my original CFC a bit slimmer and more focused on business logic while my proxy can worry about security.
For those of you out there doing Flex 2 development - how do you handle security?
p.s. Oops, forgot to link to the new build. You can view it here: http://ray.camdenfamily.com/demos/flexsec4/SimpleRemotingTest.html
Comments
This doesn't seem like much of a convenience on the surface, but each request packet requires knowledge of how to construct a secret hash that makes the request only work for 24 hours after its request.
So thats, how we coped with the situation of secure vs. insecure...and of course we have AS classes for automatically generating the request packets and what not.
Just wanted to give you a glimpse of how we handle it (even if its wrong :P)
I'm not sure I follow your logic in your last example. By placing the cflogin tag in onRequestStart in application CFC are you not authenticating the user every time the Flex application calls one of the CFC's methods?
If that is correct, why not just use CFLogin in a separate authentication method (see my comment and demo flex app I referenced on your previous example). You could setRemoteCredentials in your remote object before calling that authenication method and then those values would be available to CFLogin.
Once the user has been authenticated once by CFLogin and their role set I don't see the need to authenticate the same user again. The role stays with that user and the Flex app will be able to call the methods secured by role="admin" for example without having to reauthenticate that user.
Bruce
Basically my way uses something built in to CF (the firing of onReqStart) whereas yours is more work. (I'd say.)
I don't think you are right about not needing to re-authenticate. If session variables were in use then it would make sense, but my Flex app isn't using session variables.
In my demo app (see your previous example and my comment) I don't reauthenticate the user after he logs in successfully. After the user has been authenticated, the flex app can call the 'role secured' CFC function and the call is now allowed.
You can right click on the demo to see all the code including the CFC.
Bruce
First a quick side note. In the past when I published source with Flex apps that included CFM/CFC files, it didn't work as the CFM/CFC would try to run when you viewed them. I guess Adobe fixed that?
So - I see your app and I'm curious how it works. By default CFLOGIN will set cookies. So your Flex app makes the FR call and correctly picks up the cookies? Even though though you must have a CFLOGIN executed on EVERY request. The mere fact that you run CFLOGIN sets up security for the rest of the request. If you are already logged in the code inside the CFOGIN block won't rerun, but it has to be encountered in order for it to run. Thats not a Flex issue but how CFLOGIN works in general. I'm truly confused as to how your application is running right. :)
I think it my application works because...
The default for cfLoginUser is to use a memory-only cookie to store the user information (see http://livedocs.macromedia.com/coldfusion/6.1/html...). So once I use cfLoginUser to set the name, password, and role for a user, that information is stored in a memory cookie.
The Flex the RemoteObject setRemoteCredentials sets the username and password. After using setRemoteCredentials just once, the username and password are sent to CF each time we use that RemoteObject to call a CFC function.
CF receives the username and password as part of the RemoteObject call to a CFC function. CF checks for the cookie that matches the unique id created using that username and password. CF then finds the role in that cookie.
Bruce
Well, thanks for sharing this!
DK


A user logs in using username and password. I retrieve the userId for the suer based on their username, password and whether or not the account is active. I then return an instance of a user object to Flex.
So, I would have a user ActionScript class to be used in Flex. Inside the method I use to handle the login, I check to see if there is a valid userID (if it doesn't equal zero) and if so, set the values of the ActionScript class to those that are returned.
Right or wrong, I have found its easier for me to follow how information is getting updated and passed around if I keep everything as an object like I am used to in my 'regular' web apps.