Security


ColdFusion provides two ways to secure the functionality that you encapsulate in a ColdFusion component: roles-based authorization, and access control. You've actually already had some exposure to each of these in previous chapters. Chapter 7 introduced you to application user authentication and authorization, which allows the assignment of roles to your application's users. This roles-based security can also be applied to the functions in a CFC. The second technique, access control, was used in the last chapter in every cffunction tag as the attribute access="...".

Access Control

The access attribute of the <cffunction> tag basically answers the question, "Who can use this function?" The attribute has four options: private, package, public (the default), and remote. These four options represent, in that order, the degree of openness that the function has (Figure 19.8).

Figure 19.8. The four options for the access attribute of <cffunction>.


As illustrated in the figure, the access options range from a narrow group of potential consumers to a very broad audience. The consumers allowed by each option are as follows:

  • Private. Only other functions within the same CFC can invoke the function.

  • Package. Only components in the same package can invoke the function.

  • Public. Any CFML template or CFC on the same server can invoke the function.

  • Remote. Any CFML template or CFC on the same server can invoke the function, as can Macromedia Flash applications (using Flash Remoting) and Web Services clients.

Within any given CFC, you can have functions of any type mixed together. Remember, it's the function and not the component itself to which you grant or deny access.

Let's take a look at each access option to see how they can be used.

Private

I was at a hotel one weekend and passed by a room from which drifted some terrific music and the sound of laughing, happy people. Naturally I wanted to see what the occasion was and join in the festivities. At the door, though, I was met by a gruff and rather large gentleman who blocked my way and shook his head. He pointed to a small sign on a brass post that read "Private Function."

Silly story, but you see the connection. Private functions are the most "exclusive." Any consumer (that is, a CFML page, Web service client, Flash application, and so on) outside of a CFC cannot use a function that has been designated as access="private". Private functions are for use only in the CFC where they are coded. How can they be used at all, then, you ask? They are used by other functions in the same CFC. Generally, private functions are functions whose only purpose is to help other functions. In the shopping cart example, there were two private functions: GetCartItem() and GetCartPos(). Neither of these functions would ever be accessed directly from outside of the component. Rather, they are called from other functions, as we saw with the add() method. We should mark them private, because they are only used internally to the CFC, and by exposing them to the outside world we risk letting people in on our implementation.

Why bother? You should always hide as much information about implementation as you can. Hiding information gives you the flexibility to change the component's implementation at a later time, due to changed requirements or developing a better algorithm. It also protects the user from changes in your implementation, because code can become dependent on functionality that was never intended to be exposed, and break when it changes. Marking the code private forces your CFC's users to play by your rules when using it.

TIP

Within a component, methods can call each other using function syntax instead of <cfinvoke> (see the earlier section "Inheritance"). Since private methods are only available for use by other methods within the same component, you'll probably find yourself always invoking them as functions.


Now, here's something to watch out for: If you try to invoke a private function from anywhere but inside the component itself, you'll get an error that says "The method 'myPrivateFunction' could not be found in component (whatever)." You'll then think that you typed the name wrong, and go back and check and reenter your code, and still get the same error. "But I can see the function right there! How can it not be found?!?" you'll cry to the heavens. You've not lost your mindit's just a private function. To be certain, look at the self-documenting view of the CFC in a browser. You'll see an asterisk next to the function's name: "aPrivateFunction*". Then, under the methods list at the top of the page, you'll see a teeny explanation: *private method.

Package

Earlier in this chapter, you saw how component packages could help you organize the components in an application. Likewise, access="package" can help you control the access to your component's functionality within applications. A primary use of package access can be to prevent applications that happen to have functions with the same name from accidentally calling each other.

For example, Orange Whip Studios uses an application that lets the catering department order sandwiches for the movie shoots. A component in this application has a method that adjusts the number of sandwiches needed for a given day. The studio also uses a casting department application that can hire and fire actors. In that application, there is a component that helps calculate staff levels and make the firing decisions. Both of these methods have the apt but far-from-original name of updateQuantity. Without access control at the package level, someone developing an application could inadvertently end up firing 300 actors and leaving the remaining ones very hungry.

Public

The default option for a function's access setting is public. It's a somewhat confusing term, in that it doesn't mean "Anyone in the public can use this function." People who mistake the meaning of public usually find themselves coming to the assumption, "I don't want this function to be available as a Web service, so I shouldn't make it publicly accessible. It's just for my application, so I guess that would make it a private function." They then code a page somewhere that tries to invoke that function, and they get the less-than-intuitive error type that was mentioned earlier for private access: "The method (whatever) could not be found."

Public really means "accessible by the ColdFusion server in which the component is running." So any other component can invoke the function, as can any CFML page.

NOTE

Public (the default access level) makes CFCs behave just like custom tags and are as equally secure.


Here again, those people who cannot use a public function are precisely those you would ordinarily consider to be the "public"other people on the Internet somewhere trying to use a Web service, for example.

Watch out for HTTP and form posts and public functions. Because the page that you post to happens to be on the same machine as the component that it invokes, many people think that posting a form to a public function should work. But a form post, just like a URL invocation, uses HTTPit does not access the function from within the server. An HTTP request is just like any other remote request and thus necessitates the most open access option, remote.

Remote

A remote function is what you might have thought a public function is. It's open to anyone who wants itthe whole "public." The function can be used by the same component, another component (regardless of the package in which it's placed), any CFML page (on the server), HTTP requests, Web Services clients, and Macromedia Flash applications.

To sum up, you can see how a lack of attention to the access attribute of a function can cause lots of trouble. A function that performs some sensitive business function could be inadvertently made available to the whole world if it were set as remote instead of private. Likewise, a business partner who tries to consume a Web service from your application may not find it in the WSDL if you forgot to set the function's access to remote.

Role-Based Security in CFCs

In some applications, you'll want to control access to a component's functions based on who is using your application. This will be most common in traditional, HTML-based user interface applications, but it may also be true of Macromedia Flash applications. Role-based security is not, however, a common approach to securing access to Web services, since a Web service client is a program and not an individual.

To see this technique in action, let's go back to the actors component that was created earlier in this chapterthe one that retrieves information about all actors. Part of the Orange Whip Studios Web application allows studio executives to review the salaries of the starshow much should the studio expect to fork over for their next box-office smash? Of course, this information is not exactly something that they want just anybody seeing. After all, there was that incident last spring with the tabloid and the high-profile divorce, and the Orange Whip secretary….

First we need to create the basic security framework for this part of the application, with the security tags in ColdFusion: <cflogin>, <cfloginuser>, and <cflogout>. The code in Listing 19.15 is a very basic security architecture and, when placed in the Application.cfm file, will let us force a login by any user accessing a page in the directory. Of course, this listing is not actually doing any authentication. You would add the code appropriate to your authentication mechanism: LDAP, NT, Active Directory, database, or something else. (See Macromedia ColdFusion MX 7 Web Application Construction Kit for full coverage of implementing application login).

Listing 19.15. loginScript.cfmBasic Login Script

[View full width]

 <!---   loginScript.cfm   Typical Login script, generally would be included from application.cfm   Modified by Ken Fricklas (kenf@fricklas.com)   Modified: 2/15/2005   Code from Listing 19.15. ---> <cfif IsDefined("URL.logout")>  <cflogout> </cfif> <cflogin> <!---   Code inside CFLOGIN is only run if the user isn't already logged in.   First, check to see if they didn't get here from a login page,      and if they didn't, show it to them ---> <cfif (not IsDefined("FORM.username")) or (not IsDefined("FORM.password"))>   <cfinclude template="login.cfm">   <cfabort> <cfelse>   <!--- they got here from the login page; check to see if they left the username     or password blank; if so show a message and the login page, and abort --->   <cfif FORM.username IS "" or FORM.password IS "">     <cfoutput>       <H2>You must enter text in both the User Name and Password fields</H2>     </cfoutput>     <cfinclude template="login.cfm">     <cfabort>   <cfelse>   <!---     This is where the actual authentication occurs:       LDAP, DB, NT Domain (with COM), etc.  We'll pretend we have a local function       called "AuthenticateUser()" that does the work for us, and returns a string with a  list of roles,       or blank if bad login.    --->      <cfinclude template="authUserFn.cfm">      <cfset userRoles = authenticateUser(username, password)>      <cfif userRoles EQ "">        <!--- An unsuccessful login... show the login page and abort --->        <cfoutput>        <H2>Your login information is not valid.<br>          Please try again</H2>        </cfoutput>        <cfinclude template="login.cfm">        <cfabort>      <cfelse>        <cfset loginSuccess = 1>        <!--- Finally, a successful login...         The ROLES="..." attribute is where the comma-delimited set of roles is specified.         These will be used later with things like the isUserInRole() function OR with the          ROLES attribute of the <cffunction > tag --->       <cfloginuser name="#form.Username#" password ="#FORM.password#" roles="#userRoles#">     </cfif> <!--- user roles not blank, auth ok --->   </cfif> <!--- blank username or password ---> </cfif> <!--- not from login page ---> </cflogin> 

Notice where the <cfloginuser> tag is used. Any roles listed here will be the roles that correspond to those listed in our component function. More on that after we create the function (Listing 19.16).

The function will be simpleit takes an Actor ID as an argument, queries that actor's salary history, and returns a recordset. Notice, though, that the roles attribute in the <cffunction> tag has a comma-delimited list of values. Only users who have authenticated and been assigned one or more of those roles will be allowed to invoke the method.

Listing 19.16. Actors.cfcThe Salary Method
 <!--- Demonstrate roles ---> <cffunction name="getActorSalary" returnType="query" roles="Producers, Executives">   <cfargument name="actorID" type="numeric" required="true"     displayName="Actor ID" hint="The ID of the Actor">   <cfquery name="salaries" dataSource="ows">     SELECT Actors.ActorID, Actors.NameFirst, Actors.NameLast,       FilmsActors.Salary, Films.MovieTitle     FROM Films     INNER JOIN (Actors INNER JOIN FilmsActors        ON Actors.ActorID = FilmsActors.ActorID)        ON Films.FilmID = FilmsActors.FilmID     WHERE Actors.ActorID = #Arguments.actorID#   </cfquery>   <cfreturn salaries> </cffunction> 

The roles assigned to this function are Producers and Executivesthey don't want any prying eyes finding this sensitive data. All we need now, then, is a page to invoke the componentsomething simple, as in Listing 19.17.

Listing 19.17. showSalary.cfmShow Salary Page
 <!---   showSalary.cfm   Demonstrate CFC roles   Modified by Ken Fricklas (kenf@fricklas.com)   Modified: 2/15/2005 ---> <HTML> <HEAD> <TITLE>What were they paid?</TITLE> </HEAD> <BODY> <!--- Make sure they are logged in. ---> <cfinclude template="loginScript.cfm"> <!--- They only get here if they are logged in.   Show link to let them log out ---> <cfoutput><a href="#CGI.script_name#?logout=1">Log out</a><BR></cfoutput> <!--- Invoke actors component.  getActorSalary method will fail unless   they have sufficient access. ---> <cfinvoke  component="actors"  method="getActorSalary"  returnVariable="salaryHistory">   <cfinvokeargument name="actorID" value="17"/> </cfinvoke> <h1>Salaries of our stars...</h1> <cfoutput> <H2> #salaryHistory.NameFirst# #salaryHistory.NameLast#</H2> <cfloop query="salaryHistory">   #MovieTitle# - #dollarFormat(Salary)#<BR> </cfloop> </cfoutput> </BODY> </HTML> 

ColdFusion now has all it needs to control the access to the component. The process will work in this order:

1.

The Show Salary page is requested by a browser.

2.

ColdFusion checks to see whether the user has already logged in.

3.

If not, the user is redirected to a login page with a name and password form.

4.

When the login form is posted, ColdFusion performs whatever authentication we've coded (in this case, none!).

5.

Upon a successful login, the <cfloginuser> tag sets the user name, password, and roles for the user.

6.

The Show Salary page is allowed to remain.

7.

The <cfinvoke> tag is encountered, specifying the Actors component and the getActorsSalary method.

8.

An instance of the component is created, and the function is found.

9.

Since the function has values specified in the roles attribute, a comparison is automatically made between the values in the roles attribute of the <cffunction> tag and those in the roles attributes that were set in the <cflogin> tag.

10.

A match will allow the function to be executed as usual; a failure will cause the following error: "Error Occurred While Processing RequestCurrent user was not authorized to invoke this method…".

Notice that an unauthorized attempt to execute a secured function causes ColdFusion to throw and error. Consequently, you should put a cftry around any code that invokes secured functions (Listing 19.18).

Listing 19.18. showSalary.cfmCatching a Secure Function Invocation
 <cftry>   <cfinvoke     component="actors"     method="getActorSalary"     returnVariable="salaryHistory">     <cfinvokeargument name="actorID" value="17"/>   </cfinvoke>   <cfcatch>     What? Trying to steal company secrets? Get lost!     <cfabort>   </cfcatch> </cftry> 

This is not the only way to secure component functionality, of course. You could use the isUserInRole() function to check a user's group permissions before even invoking the function, or you could use Web server security for securing the CFML files themselves. The role-based security in CFCs is, however, a good option particularly if you are already using the ColdFusion authentication/ authorization framework in an application.



Advanced Macromedia ColdFusion MX 7 Application Development
Advanced Macromedia ColdFusion MX 7 Application Development
ISBN: 0321292693
EAN: 2147483647
Year: 2006
Pages: 240
Authors: Ben Forta, et al

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net