1. Trang chủ
  2. » Công Nghệ Thông Tin

advanced Flex Application Development Building Rich Media X phần 5 pps

51 210 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 51
Dung lượng 12,46 MB

Nội dung

8962CH07.qxd 11/7/07 11:42 AM Page 184 CHAPTER Figure 7-14 Using the search service Figure 7-15 Results of the search service Upgrading the Flex application Now that you know the service is working, you can move on to rewiring your Flex app to use your new service For this, you’re going to create a clone of Chapter7_Exercise1, so open up Flex Builder and create a new Flex project using the same process as you did for the last project and entitle it Chapter7_Exercise2 There will be two enhancements to this version of the application The first will be the addition of a form that will allow users to enter keywords to search the database for The second will be a page indicator in the form of x–y of z Once you have your application file open, copy the finished code from the previous exercise and paste it into this file This brings up a good point: always reuse code whenever possible And, since this project is very similar to the last, it’s a prime candidate for this methodology Just like before, you want to add your interface elements to the stage before adding any more code The simplest of the two enhancements is the page indicator It will be nothing more than a Text component bound to a variable that will be set based on the current results Ensure you’re in code view, and then insert the following code between the MXML markup for the two navigation buttons: Modularization With one of the design enhancements completed, you could switch to design view and drag out a TextInput component and a Button component and be done with the visualization, but this seems like an excellent opportunity to implement modularization In this context, the goal is to group similar application elements into their own component file One benefit is that any interactions necessary with these elements is confined to this component file, making debugging a little easier if something 184 8962CH07.qxd 11/7/07 11:42 AM Page 185 SECONDARY NAVIGATION should go wrong It also keeps your main application file lean and mean while promoting reusability Before creating the search component, you need to add the folder structure to the project first Next, you want to create a new MXML component and place it inside the components folder Although you could also create the component in the application root, if you are going to modularize your application into different pieces, it’s also best to organize those pieces in their own space, so to speak Right-click the components folder and select New ® MXML Component Specify the values shown in Figure 7-16 for Filename and Based on Once Flex finishes creating the component, it’s time to start adding the visual elements This component will be visual-only—its functionality will be coded in the main application In your choice of either design view or code view, add a TextInput component and a Button component Once you have added those components, switch to code view and ensure both of these components have the same attributes as the following code: Figure 7-16 Naming the new component After you have saved your work, you can close your SearchBox component and jump back over to the main application to integrate it Before you can add SearchBox to your application, you first need to create a namespace, which is basically a mapping that tells the compiler where your component class files are located To so, add xmlns:components=“components.*” beneath the default namespace definition, as shown in Figure 7-17 After adding the namespace, you can add the MXML markup for the SearchBox component by inserting the following code between the closing tag and the opening tag: Figure 7-17 Creating the components namespace 185 8962CH07.qxd 11/7/07 11:42 AM Page 186 CHAPTER The curly brace notation for the x-coordinate ensures that SearchBox will be neatly aligned to the right edge of the DataGrid With all the visual elements in place, you can now modify the code in the block to accommodate these changes Enhancing the code First, you want to add any new variables that the enhancements will require What variables you need? Well, you’ll need a variable to hold the contents of the TextInput component inside SearchBox You’ll also need two variables to hold the low and high limits for the current page And finally, you’ll need the pageText variable, which is to be bound to the text property of the page indicator component So insert the following code at the end of your variable declarations section at the top of the block: [Bindable] private var keywords:String = ''; [Bindable] private var lowLimit:uint; [Bindable] private var highLimit:uint; [Bindable] private var pageText:String; This will take care of all the variable declarations for this project The next thing you can is to create the searchClickHandler() method that you assigned for the Button inside of the SearchBox component This method will allow you to access the current keywords input by the user, call the required services, and modify the Button’s label based on the current state of the component Here’s the code that comprises searchClickHandler()—add this beneath the setButtonVisibility() method: public function searchClickHandler(event:MouseEvent):void{ var button:Button = event.target as Button; var widget:Object = button.parentDocument; if(button.label == 'Search'){ keywords = widget.keywords.text; if(keywords != ''){ searchProducts(0); button.label = 'Cancel'; widget.keywords.enabled = false; } else { Alert.show('Please enter something to search for.', ¯ 'Search Error:'); } } else { keywords = ''; widget.keywords.text = ''; getProducts(0); button.label = 'Search'; widget.keywords.enabled = true; } } Here you create local variables for both the Button as well as the SearchBox component itself so that you can easily communicate with the component Next, you use conditional logic to determine the 186 8962CH07.qxd 11/7/07 11:42 AM Page 187 SECONDARY NAVIGATION current label of the clicked button If it’s “Search,” you want to grab the text from the TextInput and store that in the keywords variable, and then you want to a quick integrity check to ensure that keywords is not empty If it’s not, you want to call the searchProducts() method, which you’ll soon write, change the Button’s label to “Cancel,” and disable the TextInput to prevent unexpected behavior; otherwise, you want to notify the user that your application doesn’t like performing empty searches Now, on the other hand, if the Button’s label is “Cancel,” you want to clear both the keywords variable and the TextInput’s text value Additionally, you want to call the original product browse() method getProducts() And finally, you want to change the Button’s label to “Search” again and reenable the TextInput for user interaction That was the most intensive part of the application enhancements—the remaining methods are essentially clones of other methods you’ve already created The first of the clones is the searchProducts() method It is very similar to getProducts() except for the actual service called, the result handler, and the passing of an additional argument Insert the following code for the searchProducts() method into your block: private function searchProducts(startID:uint):void{ amfphp.call('com.almerblank.ISBN8962.Ch7.Products.search', ¯ searchResultHandler, faultHandler, keywords, startID); CursorManager.setBusyCursor(); } The searchResultHandler() method is even more similar to its counterpart, resultHandler() The only difference besides the name of the method is the addition of a call to a soon-to-be-created method, setPageText(), and the language of the trace for trapped errors Add the following code immediately below the searchProducts() method: private function searchResultHandler(result:Array):void{ try { productsDP = new ArrayCollection(result); var total:uint = productsDP.length - 1; pageSize = productsDP.getItemAt(total).pageSize; offset = productsDP.getItemAt(total).offset; totalProducts = productsDP.getItemAt(total).totalProducts; setPageText(); setButtonVisibility(); trace('offset = '+offset+'\npageSize = ¯ '+pageSize+'\ntotalProducts = '+totalProducts); } catch(error:*) { trace('search products error: '+error.message); } finally { CursorManager.removeBusyCursor(); } } The setPageText() method also needs to be added to resultHandler() since it needs to determine the text for the page indicator component regardless of the service used The method is pretty straightforward It sets the values of the lowLimit, highLimit, and pageText variables, nothing more, nothing less Insert the following code for setPageText() into your block: 187 8962CH07.qxd 11/7/07 11:42 AM Page 188 CHAPTER private function setPageText():void{ lowLimit = offset+1; highLimit = (totalProducts > 5) ? lowLimit+4 : totalProducts; pageText = 'Displaying items '+lowLimit+' - '+highLimit+' ¯ of '+totalProducts; } Let’s briefly examine this method lowLimit is derived by adding a value of to the current offset value This is done because the offset index is a 0-based index, and while that’s fine for computer programming, it would be pretty weird reading something like “Displaying items 0–4 of 20.” highLimit is obtained by first checking to see whether the value for totalProducts is greater than If it is, highLimit should be set to the value for lowLimit plus 4, since the index is 0-based and the page size is five products per page With those two variables defined, the value for pageText should be obvious, since it just creates a variable text phrase based on those previously derived values The last of the new methods for this project is fetchPage() Its sole purpose is to be a traffic controller to the navigation buttons It will first check to see whether the value for the keywords variable is an empty string or not If it’s not, the searchProducts() method should be called; but if it is, the getProducts() method should be called Insert the following code at the bottom of your block: private function fetchPage(startID:uint):void{ if(keywords != ''){ searchProducts(startID); } else { getProducts(startID); } } That completes the application enhancements All that’s left to is to update the navigation buttons to call this latest method instead of getProducts() and pass the same arguments as before Figure 7-18 shows the updated click properties for both buttons Although this is not the only way to searchenable a Flex application, you now have an application that successfully allows you to both browse and search a product catalog In the next section, I’ll show you options for enabling history management in the application Figure 7-18: Updating the navigation buttons History management What is history management? Flex’s HistoryManager lets users navigate through a Flex application by using the web browser’s back and forward navigation commands For example, a user could make use of a TabNavigator component to navigate through various views of an application Since the TabNavigator component is history management–enabled, the user could use the web browser’s Back 188 8962CH07.qxd 11/7/07 11:42 AM Page 189 SECONDARY NAVIGATION button to traverse back through those views and eventually return the application to its initial state In other words, it allows you to keep track of where you are in an application; however, it doesn’t provide undo/redo functionality (that is, it doesn’t “remember” what you have done along the way) History management is made possible in Flex applications by implementing a set of files that are referenced in the application’s wrapper Wrapper in this context collectively refers to the HTML page that houses the Flex SWF file and the JavaScript file that embeds that SWF into the HTML page Flex Builder’s default wrapper comes prewired to support history management, but if you’re compiling via the command line or are implementing a custom wrapper, you will have to wire the wrapper to include this support Fear not though Some wrapper templates are included with your Flex Builder installation You can find the following wrapper template directories in your /flex_builder_install_ path/Flex SDK 2/resources/html-templates directory: /client-side-detection-with-history /express-installation-with-history /no-player-detection-with-history When creating a customized wrapper with history management support, remember to keep your JavaScript separate from the HTML page When you put the JavaScript inside the HTML, you ensure that you will invoke the wrath of the great and powerful Internet Explorer Eolas patent god (http://en.wikipedia.org/wiki/Eolas#Browser_changes) Other than that, it’s not difficult to enable history management in your custom wrapper Here’s a list of what’s typically required: your_wrapper.html your_flex_app.swf history.js history.htm history.swf The last three files on the list can be obtained from the templates directory I mentioned earlier, or if you’re using Flex Builder, they are added to the html-template folder of your Flex project automatically And, the good thing is that you don’t have to modify them when using a custom wrapper; you just ensure your wrapper references them For history.js, use a tag in your HTML page’s to reference the file For history.htm, use an tag placed at the bottom of the HTML page just before the closing tag to reference it Additionally, the name of the iframe element must be set to _history, and the width of the element must be set to a minimum of 22 to ensure smooth sailing with Internet Explorer I’ll discuss in a moment how Flex interacts with these files to provide this functionality, but first, here are code examples of the required tags for your custom wrapper: ¯ History management is enabled by default for the Accordion and TabNavigator containers but disabled in the ViewStack container To toggle this feature on or off for one of these controls, set the 189 8962CH07.qxd 11/7/07 11:42 AM Page 190 CHAPTER container’s historyManagementEnabled property to true or false, respectively Now, this doesn’t mean that other elements in your Flex app are left out—quite the contrary To enable custom history management for other objects in an application, you’ll need to the following: Implement the IHistoryManagerClient interface (mx.managers.IHistoryManagerClient) Register the component with the HistoryManager’s register() method Save the component’s state when its state changes Implement the interface’s saveState() and loadState() methods So, say for example you have created a component, YourComponent.mxml, with several view states, and you want to enable history management for the component Here’s some example code on how to make that happen: 190 8962CH07.qxd 11/7/07 11:42 AM Page 191 SECONDARY NAVIGATION This effectively registers YourComponent with the HistoryManager class and satisfies the other requirements for enabling history management for custom components But, how does HistoryManager integrate with the web browser? First, it uses navigateToURL() to invisibly load your into the current browser window Next, it encodes your Flex application’s navigation states into the iframe’s URL query parameters From here, history.swf, which lives inside history.htm, decodes those parameters and sends the navigation state back to the HistoryManager class This sequence is illustrated in Figure 7-19 Figure 7-19 HistoryManager flowchart 191 8962CH07.qxd 11/7/07 11:42 AM Page 192 CHAPTER That’s just a basic overview of what happens behind the scenes But, rather than delve too deeply into custom history management theory, I’ll show you how you can further enhance the previous exercises to include basic history management So, create a new project the same way you did in the “Search integration” section and name it Chapter7_Exercise3 This project will start as a clone of Chapter7_Exercise2, so copy the contents of the Chapter7_Exercise2 application file and paste it into Chapter7_Exercise3 once Flex Builder finishes creating your application file Also, remember to copy over the com and components folders to the new project as well The following list details the enhancements to be made in this project: Adding a header to the main view Creating a component to display detailed product information Adding a ViewStack container to house both the main view and detail view Enabling history management on the ViewStack The header that you’re going to add to the application will be comprised of a Label and Text component The Label will serve as the view’s title, while the Text will serve as instructions to the end user on how to view more information about a particular product Insert the following code just above your SearchBox component: The next step is to build a product details component that can display extended information about a selected product as well as give the option to purchase the product Follow the same steps to create this component as you did in the “Search integration” section for the SearchBox component, but name it ProductDetails Once the component file has been created, add the following code to the file: In addition to the fields displayed in the DataGrid, the ProductDetails component will also display a thumbnail for the product, as well as the average rating for the product This component also provides the means for the user to navigate back to the catalog browser Purchasing ability will be added at a later time This component also makes reference to a property of the parent application named selectedProduct, but this variable has yet to be defined Switch back to the main application and add a new, bindable public variable named selectedProduct of type Object to the end of your variable declarations section While you’re there, import the ListEvent class found in the mx.events package This class will allow you to capture and process the user selections as you see fit How exactly will that happen? By assigning a new method, fetchProductDetails(), to the DataGrid’s itemClick event property, you’ll be able to learn which product the user clicked and initialize ProductDetails with that data Figure 7-20 shows the Figure 7-20 Assigning an event handler for the DataGrid’s opening tag for the DataGrid with the itemClick itemClick event property added The code for fetchProductDetails() is relatively simple in that it takes the currently selected DataGrid item, which is of type Object, and assigns it to the selectedProduct you just created The selectedProduct object is in turn bound to the various form items of your ProductDetails component This method also switches the selectedIndex of the ViewStack to the index occupied by your component Insert the code for this method at the end of your block in your main application file: private function fetchProductDetails(event:ListEvent):void{ selectedProduct = event.target.selectedItem; productViews.selectedIndex = 1; } 193 8962CH08.qxd 11/7/07 10:09 AM Page 220 CHAPTER Except for the tag namespace, the ConfirmTextFieldValidators look almost identical to Flex validators Two new properties appear after the first seven common to most validators; these are responsible for assigning a field to validate against and an error to display if the fields not match When the application is compiled now, the confirmation fields will glow red and display the custom error on hover while you type until the text component contains the same value as the field it is assigned to confirm Now all the code is in place to make sure that any input that users are making gets stored and validated, presenting users with uniform errors for all text fields when they not meet necessary requirements Next, let’s take a look at handling the form submission 220 8962CH08.qxd 11/7/07 10:09 AM Page 221 FORMS AND VALIDATION Submitting the form With all the validators in place, the form is ready to take user input What the current code doesn’t provide is a way to make sure that all the validators come back as valid when a user attempts to submit the form In this section, I talk about handling this, and I also talk about how I manage all the different remoting calls of an application Validating the form on submission In order to validate the form on submission, I demonstrate using another validator The Validator class, which is the parent of all validators, provides a public static method named validateAll(), which accepts an array of validators The method takes the array and executes each of the validators, storing all of the returned errors in an array that it returns In the NewMemberForm class, I add a new method as follows: package com.almerblank.rmx.forms { import mx.containers.Form; import com.almerblank.rmx.dto.Member; import mx.validators.Validator; import mx.controls.Alert; public class NewMemberForm extends Form { [Bindable] public var memberInfo:Member = new Member(); [Bindable] public var validators:Array; public function NewMemberForm() { super(); } public function validateForm():void { var validationResults:Array; validationResults = Validator.validateAll(this.validators); if (validationResults.length == 0) { // form is valid } else { Alert.show("The form still contains invalid fields, ¯ please fill out the form completely and make sure ¯ none of the fields glow red.", "Invalid Fields"); 221 8962CH08.qxd 11/7/07 10:09 AM Page 222 CHAPTER } } } } The new method, validateForm(), takes the public property validators, which is bound to the array of validators in the application and runs it through the validateAll() method of the Validator class The result is stored in a validationResults function variable Next, the method checks whether the length of the results is 0; if so, the form is valid, and I can add the code to handle a valid form within the if statement Otherwise, the length would be greater than 0, indicating the presence of errors In this case, an alert dialog box is displayed informing the user that there are invalid fields in the form To call this method in the MXML, I can simply this: Near the bottom of the code I add a click event to the submit button Here it executes the new validateForm() method just added to the NewMemberForm class This form is now ready to be sent off to the server Managing remoting calls In most of today’s rich media applications, the list of calls to the server can start to get pretty long Later down the road, the need to change the name of a remote call or path to a remote call can be pretty daunting if the developer has not looked at the code in a very long time Global searches in Eclipse are fine and help to get this kind of job done, but properly preparing for this in advance can make the job even easier To manage remoting calls in projects I code, I write a gateway class to centralize all the service calls, making it easier to edit and call different remote methods A sample class would look something like this: package com.almerblank.rmx.utils { import flash.net.Responder; import mx.managers.CursorManager; 222 8962CH08.qxd 11/7/07 10:09 AM Page 223 FORMS AND VALIDATION public class RMXGateway { // service calls public static const ADD_NEW_MEMBER:String = "Members.addMember"; public static const DELETE_MEMBER:String = "Members.deleteMember"; // gateway url private static const _gatewayURL:String =¯ "http://www.yoururl.com/amfphp/gateway.php"; // gateway variables private static var _gateway:RemotingConnection; private static var _onSuccess:Function; private static var _onFault:Function; /** * The constructor, should not be used to instantiate a variable * as this class is not meant to be instantiated * * @return * */ public function RMXGateway() {} /** * The call() method is used to make a remoting call * * @param command String path to the remote method being called * @param success Function that is called on a successful * communication with the server * @param fault Function that is called when the server * returns an error * @param data Data object sent to the remote method * @param showBusyCursor Defaults to true to handle the busy * cursor while communicating with the server Set to false if you * not want a busy cursor * */ public static function call(command:String, success:Function, ¯ fault:Function, data:* = null, showBusyCursor:Boolean = true):void { _gateway = new RemotingConnection(_gatewayURL); _onSuccess = success; _onFault = fault; var responder:Responder = new Responder(onSuccess, onFault); 223 8962CH08.qxd 11/7/07 10:09 AM Page 224 CHAPTER if (data) { _gateway.call(command, responder, data); } else { _gateway.call(command, responder); } if (showBusyCursor) { CursorManager.setBusyCursor(); } } /** * Private method that handles firing the function that was * specified in the call method and removes the busy cursor * * @param result * */ private static function onSuccess(result:*):void { _onSuccess(result); CursorManager.removeBusyCursor(); } /** * Private method that handles firing the fault function that was * specified in the call method and removes the busy cursor * * @param fault * */ private static function onFault(fault:Object):void { _onFault(fault); CursorManager.removeBusyCursor(); } } } 224 8962CH08.qxd 11/7/07 10:09 AM Page 225 FORMS AND VALIDATION import flash.net.NetConnection; import flash.net.ObjectEncoding; class RemotingConnection extends NetConnection { public function RemotingConnection(sURL:String) { objectEncoding = ObjectEncoding.AMF0; if(sURL){ connect(sURL); } } public function AppendToGatewayUrl(s:String):void{ // } } This class has public static constants, one for each remoting call that exists in the application The class is set up with static methods, allowing me to call remoting calls from anywhere in the application without having to instantiate a new object The class also handles the busy cursor management for me, so that I don’t have to write that extra code each time To use this method, I place the call to the ADD_NEW_MEMBER remoting call in the successful if conditional The code would look like this: package com.almerblank.rmx.forms { import mx.containers.Form; import com.almerblank.rmx.dto.Member; import mx.validators.Validator; import mx.controls.Alert; import com.almerblank.rmx.utils.RMXGateway; public class NewMemberForm extends Form { [Bindable] public var memberInfo:Member = new Member(); [Bindable] public var validators:Array; public function NewMemberForm() { super(); } public function validateForm():void { var validationResults:Array; validationResults = Validator.validateAll(validators); 225 8962CH08.qxd 11/7/07 10:09 AM Page 226 CHAPTER if (validationResults.length == 0) { // form is valid RMXGateway.call(RMXGateway.ADD_NEW_MEMBER, onSuccess,¯ onFault, this.memberInfo); } else { Alert.show("The form still contains invalid fields, please ¯ fill out the form completely and make sure none of the ¯ fields glow red.", "Invalid Fields"); } } } } The one line I added is using the static call method of the RMXGateway class The first parameter is the remote method to call, which I access using the public static constants I set up Next are the onSuccess event handler and the onFault event handler As the code stands, the application will not compile The onSuccess and onFault methods need to be declared and handled However, the remoting call is ready, and the DTO object with all the data is being sent off Whether you’re using a JSP, PHP, or ASP page, the concepts are the same; only the server-side pages and returns will differ Forms, PHP, and security The validation techniques covered for Flex provide a great first line of defense on the client side, ensuring user input is both a valid entry and a valid type the form is expecting Even with your nice validation done on the client side, you still need to always validate on the server side In the case of the RMX, the server language is PHP This section, written by Chris Charlton, covers some security fundamentals to implement when accepting user-generated content Security: Being careful with user-supplied content Trust no one Trust nothing This is the serious tone you should respect when dealing with usergenerated content Be cautious with any input freedom you provide any set of users Being wary of what’s submitted keeps you sharp to analyze and scrutinize areas that could be used to hurt or break your application, by mistake or on purpose Additionally, everyone wants their applications to soar without any friction, but to be honest, the number one area applications experience bumps is around features that allow or deal with user input or generated content The honest answer is, “Just don’t it.” Here, I cover scenarios of accepting user input and ways you can deal with these demands The Flash Platform and frameworks like Flex offer great development features that are much safer than standard JavaScript because they go beyond restricted input and simple masking techniques found in most DHTML/Ajax applications Additionally, JavaScript can be disabled, leaving only your server-side validation as your sole defense, but with a SWF that wouldn’t be the case 226 8962CH08.qxd 11/7/07 10:09 AM Page 227 FORMS AND VALIDATION The PHP Security Consortium is a great resource for methods and information on all security concerns PHP web apps should deal with You can visit the PHP Security Consortium at www.phpsec.org I’d recommend reading all their published articles and their library of publicly available articles There is even a free tool available on their web site, PhpSecInfo, that outputs security reports and information about your PHP environment and offers suggestions for improvement Accepting text and dealing with form data All applications accept user input generally through forms No matter the technology, accepting plain text has tons of security concerns, including, but not limited to, SQL injections, database hacks, crosssite scripting (XSS), or at least malformed form data that may result in orphaned (bunk) records Allowing users markup of any kind, like HTML, CSS, or, even scarier, JavaScript, is treading into deep shark-infested waters Those seemingly harmless languages can hurt your design, functionality, and even possibly upset advertisers How? If your users were allowed to add any CSS code freely, folks would learn how to hide all the advertising throughout the application that you may require on free memberships Obviously, this would destroy all advertising revenue Before allowing users to submit fancy markup, you must take care of good old plain text Not an exciting subject on the surface, but I assure you this is the most dangerous stuff around So what’s the danger? I mentioned things like SQL injections, database hacks, and orphaned or malformed data These are all very real issues with every web application, public or not Since RMX is an RIA, you use the Flex framework’s form validation features to reduce most, if not all, improper data Still, you should take no chances; your application tier (PHP) and database (MySQL) layers should each have their own logic for checking what gets posted from your Flex forms Accepting user-generated HTML Allowing HTML can be dangerous if you blindly permit any HTML tag to get saved and parsed through to your presentation layer It may be true that most social networking sites allow custom HTML to be pasted into member profiles, but that doesn’t mean that they let every HTML tag pass untouched PHP provides a strip_tags() function that disallows all HTML tags from a string variable This function strips both HTML tags and PHP code Now what if you don’t want to strip every tag—you want to allow a list of accepted tags? The strip_tags() function has an optional argument that allows you to define an array of allowed HTML tags Here’s an example: Notice the tag in the preceding example The $allowed_tags array contains the list of HTML tags allowed There are a lot of HTML tags to remember, so I’ve categorized the most common tags into groups 227 8962CH08.qxd 11/7/07 10:09 AM Page 228 CHAPTER Formatting: This refers to markup used to beautify bodies of text, like , , , , , , and These tags are generally harmless if you’ve accounted for them in your CSS ahead of time If you only wish to support or allow XHTML versus HTML, you’ll have to run some conversion of tags like the tag into their XHTML equivalent, which in this case would be the tag Structure: These are tags that help define content structure, like , , , , , ,

, , , , ,

    ,
      ,
    • , , , and Layout: These are tags that can be used to control or modify layout, like and Redundant: Since user-generated HTML content will be parsed inside your tag, it’d be no use and harmful for your site to end up with more than one tag You need to be sure these tags (, , , , , and the like) are not coming in with the user’s content Script, frame, and style tags: These are tags that allow additional code or languages embedded to run when a page loads and are therefore extremely dangerous Suspect tags are , , , , , , , and This is nowhere near an exhaustive list, but it does cover many of the common tags that can be responsible for causing damage to your site The official HTML and XHTML specifications are available at the web site of the W3C (www.w3.org), also known as the World Wide Web Consortium For some, these documents may be boring and too technical for their needs, so another recommendation for quick references of HTML and XHTML is a web site called W3Schools, which can be found at www.w3schools.com To be honest, the best and safest solution is to not accept any HTML code, but commercial demands may require you to allow some markup to give users a degree of personalization Compile a whitelist of tags that will be allowed across your entire application or site Limits like this need to be documented so every team member, and clients themselves, know what restrictions will always apply when adding object properties and input elements Too bad this is only half the problem when allowing markup Unfortunately, strip_tags() does not modify tag attributes Tag attributes should be taken as seriously as tags themselves Any attribute could be exploited An tag’s href attribute could activate some scary JavaScript or a link that’s not acceptable under your terms-of-use policy What’s worse is that even if you managed to filter href attributes, someone could use an onmouseover attribute that you let get by to run some hidden malicious code The last thing you want to find is users hiding code that would allow them to siphon cookies from other users, enabling the hacker to log in to the site disguised as another user A good solution would be hunting down some reliable regular expressions Or check out a PHP Extension and Application Repository (PEAR) class called HTML_Safe (available online at http://pear.php.net/package/HTML_Safe), which does a good job at cleaning up tags, links, and tag attributes of your choice If you have any plans for accepting JavaScript or similar code from users, you multiply your headaches of safely accepting and rendering user content Most advertisements use JavaScript in their ad code, and you can’t trust any code from users People can use JavaScript for malicious attacks on you and your users, and that would suck You also can’t be sure that everyone’s JavaScript code works as intended Any snippet of copied JavaScript could potentially break the rendering or other scripts of 228 8962CH08.qxd 11/7/07 10:09 AM Page 229 FORMS AND VALIDATION your pages Not good for you, and not good for your users If your application doesn’t need to accept JavaScript code, don’t let it Starting with plain text or only HTML is more than enough for many users Accepting user-generated CSS Cascading Style Sheets, or CSS, is a W3C recommended standard that helps reduce markup by taking care of all the formatting, styling, and layout for documents Here, I’ll touch on the main aspects of CSS you need to watch out for when securing your application Since CSS is popular, you can surf the Web on your own to find more detailed discussion on how CSS can compromise your design or security The first two questionable CSS properties are display and visibility: both can be used against any area of the page and easily hide required branding and advertisements Something like this should be a violation of your usage policies, since it’s affecting the application in an unintended way and hurts advertisement campaigns Here’s a usable CSS style declaration that won’t fix or prevent anything, but each line should give you some insight into how that style property would hurt your application or site .fakeEvilStyle, #header, #ads, ad, #footer, body, #content:before, #content:after, #content:hover { /* your page will be hidden entirely */ display: none; visibility: hidden; /* your page can be rendered offstage */ top: -1000; left: -1000; /* can add unexpected content to the page */ content: string('hacked by CSSx0r'); /* masks your entire site to a 1px by 1px */ clip: rect(0px,0px,1px,1px); /* fake (invisible) image used for some bad reason */ backround-image: url('http://site.com/tracking/you_with/fake.jpg'); } lessHarmfulStyles, #content, h1, h2, h3, h4, h5, h6, p { /* All harmless but when used together they're suspicious */ width: 0; /* no width is not what you intended I'm sure */ height: 0; /* no height is not what you intended either */ overflow: hidden; /* mixing false width/height makes it suspect */ z-index: -100; /* moves elements to the "back of the canvas" */ } Again, you should think how each line of the preceding code sample would affect your site if a proficient user has the chance to override your style sheets As mentioned earlier, just not accepting the input is always the safest move, but you’re going to hear it from someone upstairs that his teenage kid can’t tweak his profile like on some other site The only real way to get around this is to take a smarter approach than just looking for style properties You need to ensure your application forces your own CSS declarations over any set by users while tossing out everything people try to slip by 229 8962CH08.qxd 11/7/07 10:09 AM Page 230 CHAPTER A good recommendation for this server magic is CSS Parser, a small PHP class that is available freely at www.phpclasses.org/cssparser PEAR fans can use a package called HTML_CSS, available at http://pear.php.net/package/HTML_CSS Both provide methods for handling style sheet declarations and can parse strings and CSS documents to inspect any CSS selector and property For CSS cleanup jobs, I use classes like CSS Parser to make sure advertisements and their tags don’t get altered with “harmless” CSS First, imagine your pages have tags wrapped around each advertisement with their class="advertisement" applied You’ll use the CSS Parser class to read your official styles and inspect all user input to make sure users aren’t sneaking anything in that could hurt your ads The following code loads the CSS Parser class, loads your CSS documents, and then prunes unwanted usergenerated content:

Ngày đăng: 14/08/2014, 11:21

TỪ KHÓA LIÊN QUAN