首页资源分类电源技术 > Ajax Security.pdf

Ajax Security.pdf

已有 456409个资源



标    签: AjaxSecurity

分    享:


Ajax Security.pdf


“Ajax Security is a remarkably rigorous and thorough examination of an underexplored subject. Every Ajax engineer needs to have the knowledge contained in this book—or be able to explain why they don’t.” Jesse James Garrett “Finally, a book that collects and presents the various Ajax security concerns in an understandable format! So many people have hopped onto the Ajax bandwagon without considering the security ramifications; now those people need to read this book and revisit their applications to address the various security shortcomings pointed out by the authors.” Jeff Forristal “If you are writing or reviewing Ajax code, you need this book. Billy and Bryan have done a stellar job in a nascent area of our field, and deserve success. Go buy this book. I can’t wait for it to come out.” Andrew van der Stock, Executive Director, OWASP “Web technologies like Ajax are creating new networked business structures that remove the sources of friction in the new economy. Regrettably, hackers work to compromise this evolution by capitalizing on the weaknesses in this technology and those who develop it. Until now, few books told the whole Ajax security story, educating those using or planning to use this technology. This one does.” Managing Partner, Trellum Technologies This page intentionally left blank Ajax Security This page intentionally left blank Ajax Security Billy Hoffman and Bryan Sullivan Upper Saddle River, NJ • Boston • Indianapolis • San Francisco New York • Toronto • Montreal • London • Munich • Paris • Madrid Cape Town • Sydney • Tokyo • Singapore • Mexico City Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and the publisher was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals. The authors and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein. The publisher offers excellent discounts on this book when ordered in quantity for bulk purchases or special sales, which may include electronic versions and/or custom covers and content particular to your business, training goals, marketing focus, and branding interests. For more information, please contact: U.S. Corporate and Government Sales (800) 382-3419 corpsales@pearsontechgroup.com For sales outside the United States please contact: International Sales international@pearsoned.com Editor-in-Chief Karen Gettman Acquisitions Editor Jessica Goldstein Development Editor Sheri Cain Managing Editor Gina Kanouse Project Editor Chelsey Marti Copy Editor Harrison Ridge Editorial Services Indexer Lisa Stumpf Proofreader Kathy Ruiz Technical Reviewers Trellum Technologies, Inc. Jeff Forristal Joe Stagner Vinnie Liu Editorial Assistant Romny French Cover Designer Alan Clements Composition Jake McFarland Visit us on the Web: www.prenhallprofessional.com Library of Congress Cataloging-in-Publication Data: Hoffman, Billy, 1980- Ajax security / Billy Hoffman and Bryan Sullivan. p. cm. ISBN 0-321-49193-9 (pbk. : alk. paper) 1. Ajax (Web site development technology) 2. Computer networks—Security measures. 3. Computer security. I. Sullivan, Bryan, 1974- II. Title. TK5105.8885.A52H62 2007 005.8—dc22 2007037191 Copyright © 2008 Pearson Education, Inc. All rights reserved. Printed in the United States of America. This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise. For information regarding permissions, write to: Pearson Education, Inc Rights and Contracts Department 501 Boylston Street, Suite 900 Boston, MA 02116 Fax (617) 671 3447 ISBN-13: 978-0-321-49193-0 ISBN-10: 0-321-49193-9 Text printed in the United States on recycled paper at R.R. Donnelly in Crawfordsville, IN. First printing December 2007 This book is dedicated to my wife Jill. I am lucky beyond words to be married to such an intelligent, beautiful, and caring woman. Love you Sexy. For Amy. I can’t imagine living without your love and support. This page intentionally left blank Contents Chapter 1 Chapter 2 Preface Preface (The Real One) Introduction to Ajax Security An Ajax Primer What Is Ajax? Asynchronous JavaScript XML Dynamic HTML (DHTML) The Ajax Architecture Shift Thick-Client Architecture Thin-Client Architecture Ajax: The Goldilocks of Architecture A Security Perspective: Thick-Client Applications A Security Perspective: Thin-Client Applications A Security Perspective: Ajax Applications A Perfect Storm of Vulnerabilities Increased Complexity, Transparency, and Size Sociological Issues Ajax Applications: Attractive and Strategic Targets Conclusions The Heist Eve Hacking HighTechVacations.net xvii xvix 1 2 2 3 6 11 11 11 12 13 15 16 17 18 19 19 22 23 24 25 25 26 ix CONTENTS Hacking the Coupon System 26 Attacking Client-Side Data Binding 32 Attacking the Ajax API 36 A Theft in the Night 42 Chapter 3 Web Attacks 45 The Basic Attack Categories 45 Resource Enumeration 46 Parameter Manipulation 50 Other Attacks 75 Cross-Site Request Forgery (CSRF) 75 Phishing 76 Denial-of-Service (DoS) 77 Protecting Web Applications from Resource Enumeration and Parameter Manipulation 77 Secure Sockets Layer 78 Conclusions 78 Chapter 4 Ajax Attack Surface 81 Understanding the Attack Surface 81 Traditional Web Application Attack Surface 83 Form Inputs 83 Cookies 84 Headers 85 Hidden Form Inputs 86 Query Parameters 86 Uploaded Files 89 Traditional Web Application Attacks: A Report Card 90 Web Service Attack Surface 92 Web Service Methods 92 Web Service Definitions 94 Ajax Application Attack Surface 94 The Origin of the Ajax Application Attack Surface 96 Best of Both Worlds—for the Hacker 98 Proper Input Validation 98 The Problem with Blacklisting and Other Specific Fixes 99 Treating the Symptoms Instead of the Disease 102 Whitelist Input Validation 105 x Chapter 5 Chapter 6 Regular Expressions Additional Thoughts on Input Validation Validating Rich User Input Validating Markup Languages Validating Binary Files Validating JavaScript Source Code Validating Serialized Data The Myth of User-Supplied Content Conclusion Ajax Code Complexity Multiple Languages and Architectures Array Indexing String Operations Code Comments Someone Else’s Problem JavaScript Quirks Interpreted, Not Compiled Weakly Typed Asynchronicity Race Conditions Deadlocks and the Dining Philosophers Problem Client-Side Synchronization Be Careful Whose Advice You Take Conclusions Transparency in Ajax Applications Black Boxes Versus White Boxes Example: MyLocalWeatherForecast.com Example: MyLocalWeatherForecast.com “Ajaxified” Comparison Conclusions The Web Application as an API Data Types and Method Signatures Specific Security Mistakes Improper Authorization Overly Granular Server API Session State Stored in JavaScript Sensitive Data Revealed to Users CONTENTS 109 109 111 111 113 114 120 122 123 125 125 126 128 129 130 132 132 133 135 135 139 144 144 145 147 147 150 152 156 156 158 158 159 161 164 165 xi CONTENTS Comments and Documentation Included in Client-Side Code 166 Data Transformation Performed on the Client 167 Security through Obscurity 172 Obfuscation 173 Conclusions 174 Chapter 7 Hijacking Ajax Applications 175 Hijacking Ajax Frameworks 176 Accidental Function Clobbering 176 Function Clobbering for Fun and Profit 178 Hijacking On-Demand Ajax 184 Hijacking JSON APIs 190 Hijacking Object Literals 195 Root of JSON Hijacking 195 Defending Against JSON Hijacking 196 Conclusions 199 Chapter 8 Attacking Client-Side Storage 201 Overview of Client-Side Storage Systems 201 General Client-Side Storage Security 202 HTTP Cookies 204 Cookie Access Control Rules 206 Storage Capacity of HTTP Cookies 211 Lifetime of Cookies 215 Additional Cookie Storage Security Notes 216 Cookie Storage Summary 216 Flash Local Shared Objects 218 Flash Local Shared Objects Summary 225 DOM Storage 226 Session Storage 227 Global Storage 229 The Devilish Details of DOM Storage 231 DOM Storage Security 233 DOM Storage Summary 234 Internet Explorer userData 235 Security Summary 240 xii CONTENTS General Client-Side Storage Attacks and Defenses 240 Cross-Domain Attacks 241 Cross-Directory Attacks 242 Cross-Port Attacks 243 Conclusions 243 Chapter 9 Offline Ajax Applications 245 Offline Ajax Applications 245 Google Gears 247 Native Security Features and Shortcomings of Google Gears 248 Exploiting WorkerPool 251 LocalServer Data Disclosure and Poisoning 253 Directly Accessing the Google Gears Database 257 SQL Injection and Google Gears 258 How Dangerous Is Client-Side SQL Injection? 262 Dojo.Offline 264 Keeping the Key Safe 265 Keeping the Data Safe 266 Good Passwords Make for Good Keys 267 Client-Side Input Validation Becomes Relevant 268 Other Approaches to Offline Applications 270 Conclusions 270 Chapter 10 Request Origin Issues 273 Robots, Spiders, Browsers, and Other Creepy Crawlers 273 “Hello! My Name Is Firefox. I Enjoy Chunked Encoding, PDFs, and Long Walks on the Beach.” 275 Request Origin Uncertainty and JavaScript 276 Ajax Requests from the Web Server’s Point of View 276 Yourself, or Someone Like You 280 Sending HTTP Requests with JavaScript 282 JavaScript HTTP Attacks in a Pre-Ajax World 284 Hunting Content with XMLHttpRequest 286 Combination XSS/XHR Attacks in Action 290 Defenses 292 Conclusions 294 xiii CONTENTS Chapter 11 Web Mashups and Aggregators 295 Machine-Consumable Data on the Internet 296 Early 90’s: Dawn of the Human Web 296 Mid 90s: The Birth of the Machine Web 297 2000s: The Machine Web Matures 298 Publicly Available Web Services 299 Mashups: Frankenstein on the Web 301 ChicagoCrime.org 302 HousingMaps.com 303 Other Mashups 304 Constructing Mashups 304 Mashups and Ajax 306 Bridges, Proxies, and Gateways—Oh My! 308 Ajax Proxy Alternatives 309 Attacking Ajax Proxies 310 Et Tu, HousingMaps.com? 312 Input Validation in Mashups 314 Aggregate Sites 317 Degraded Security and Trust 324 Conclusions 327 Chapter 12 Attacking the Presentation Layer 329 A Pinch of Presentation Makes the Content Go Down 329 Attacking the Presentation Layer 333 Data Mining Cascading Style Sheets 334 Look and Feel Hacks 337 Advanced Look and Feel Hacks 341 Embedded Program Logic 345 Cascading Style Sheets Vectors 347 Modifying the Browser Cache 348 Preventing Presentation Layer Attacks 352 Conclusion 353 Chapter 13 JavaScript Worms 355 Overview of JavaScript Worms 355 Traditional Computer Viruses 356 JavaScript Worms 359 JavaScript Worm Construction 361 JavaScript Limitations 363 xiv CONTENTS Propagating JavaScript Worms 364 JavaScript Worm Payloads 364 Putting It All Together 372 Case Study: Samy Worm 373 How It Worked 374 The Virus’ Payload 377 Conclusions About the Samy Worm 379 Case Study: Yamanner Worm (JS/Yamanner-A) 380 How It Worked 380 The Virus’ Payload 383 Conclusions About the Yamanner Worm 384 Lessons Learned from Real JavaScript Worms 387 Conclusions 389 Chapter 14 Testing Ajax Applications 391 Black Magic 391 Not Everyone Uses a Web Browser to Browse the Web 396 Catch-22 398 Security Testing Tools—or Why Real Life Is Not Like Hollywood 399 Site Cataloging 400 Vulnerability Detection 401 Analysis Tool: Sprajax 403 Analysis Tool: Paros Proxy 406 Analysis Tool: LAPSE (Lightweight Analysis for Program Security in Eclipse) 408 Analysis Tool: WebInspect™ 409 Additional Thoughts on Security Testing 411 Chapter 15 Analysis of Ajax Frameworks 413 ASP.NET 413 ASP.NET AJAX (formerly Atlas) 414 ScriptService 417 Security Showdown: UpdatePanel Versus ScriptService 419 ASP.NET AJAX and WSDL 420 ValidateRequest 424 ViewStateUserKey 425 ASP.NET Configuration and Debugging 426 xv CONTENTS PHP 427 Sajax 427 Sajax and Cross-Site Request Forgery 430 Java EE 431 Direct Web Remoting (DWR) 432 JavaScript Frameworks 434 A Warning About Client-Side Code 435 Prototype 435 Conclusions 437 Appendix A Samy Source Code 439 Appendix B Source Code for Yamanner Worm 447 Index 453 xvi Preface Fire. The wheel. Electricity. All of these pale next to the monumental achievement that is Ajax. From the moment man first walked upright, he dreamed of, nay, lusted for the day that he would be able to make partial page refreshes in a Web application. Surely Jesse James Garrett was touched by the hand of God Himself the morning he stood in his shower and contemplated the word Ajax. But like Cortés to the Aztecs, or the Star Wars prequels, what was at first received as a savior was later revealed to be an agent of ultimate destruction. As the staggering security vulnerabilities of Ajax reared their sinister heads, chaos erupted in the streets. Civilizations crumbled. Only two men could dare to confront the overwhelming horror of Ajax. To protect the innocent. To smite the wicked. To stave off the end of all life in the universe. And we’re glad you’ve paid $49.99 for our book. xvii This page intentionally left blank Preface (The Real One) Ajax has completely changed the way we architect and deploy Web applications. Gone are the days of the Web browser as a simple dumb terminal for powerful applications running on Web servers. Today’s Ajax applications implement functionality inside a user’s Web browser to create responsive desktop-like applications that exist on both the client and the server. We are seeing excellent work from developers at companies like Google and Yahoo! as well the open source community pushing the bounds of what Ajax can do with new features like client-side storage, offline applications, and rich Web APIs. As Web programmers and security researchers, we rushed out and learned as much as we could about these cool new applications and technologies. While we were excited by all the possibilities Ajax seemed to offer, we were left with a nagging feeling: No one was talking about the security repercussions of this new application architecture. We saw prominent resources and experts in the Ajax field giving poor advice and code samples riddled with dangerous security vulnerabilities such as SQL Injection or Cross-Site Scripting. Digging deeper, we found that not only were these traditional Web vulnerabilities ignored or relegated to passing mention in an appendix, but there were also larger security concerns with developing Ajax applications: overly granular Web services, application control flow tampering, insecure practices for developing mashups, and easily bypassed authentication mechanisms. Ajax may have the inherent usability strengths of both desktop and Web applications, but it also has both of their inherent security weaknesses. Still, security seems to be an afterthought for most developers. xix PREFACE We hope to change that perspective. We wrote this book for the Ajax developer who wants to implement the latest and greatest Ajax features in their applications, while still developing them securely to avoid falling prey to evil hackers looking to exploit the applications for personal and financial gain. Throughout the book, we focus not just on presenting you with potential security problems in your Ajax applications, but also on providing guidance on how you can overcome these problems and deliver tighter, more secure code. We also analyze common Ajax frameworks like Prototype, DWR, and Microsoft’s ASP.NET AJAX to find out what security protections frameworks have built-in and what you, as a developer, are responsible to add. We also wrote this book for the quality assurance engineer and the professional penetration tester. We have tried to provide information about common weaknesses and security defects found in Ajax applications. The book discusses the testing challenges you will face in auditing an Ajax application, such as discovering the application’s footprint and detecting defects. We review a few tools that aid you in completing these challenging tasks. Finally, we give details on new Ajax attack techniques such as JavaScript hijacking, persistent storage theft, and attacking mashups. We also provide fresh takes on familiar attacks, such as a simplified Ajax-based SQL Injection method, which requires only two requests to extract the entire backend database. This is not a book for learning Ajax or Web programming—we expect you to have a pretty good handle on that already. Instead, we will focus on the mistakes and problems with the design and creation of Ajax applications that create security vulnerabilities and provide advice on how to develop Ajax applications securely. This book is not program language specific and does not force you to write the server-side of your application in any specific language. There are common components to all Ajax applications, including HTTP, HTML, CSS, and JavaScript. We focus our analysis on these components. When we do provide security advice with respect to your Web server code, we do so using techniques such as regular expressions or string operations that can be implemented using any language. This book also contains a great deal of material that should benefit both the developer and the tester. Case studies of real-world Ajax applications and how they were hacked, such as MySpace’s Samy worm and Yahoo!’s Yamanner worm, are discussed. Sample applications and examples, such as an online travel booking site, provide guidance on how to secure an Ajax application for testers and developers alike. xx PREFACE While we do mean for the book to be read cover-to-cover, front-to-back, each chapter stands on its own. If there’s a particular topic you can’t wait to discover, such as the analysis of specific Ajax frameworks for security issues (which can be found in Chapter 15, “Analysis of Ajax Frameworks”), feel free to skip ahead or read out of order. Ajax provides an exciting new philosophy for creating Web applications. This book is by no means an attempt to dismiss Ajax as silly or infeasible from a security perspective. Instead, we hope to provide a resource to help you develop powerful, feature-rich Ajax applications that are extremely useful, while at the same time robust and secure against malicious attackers. Enjoy, Billy and Bryan xxi This page intentionally left blank Acknowledgments JOINT ACKNOWLEDGMENTS The names on the cover of this book are Billy Hoffman and Bryan Sullivan, but the truth is that there are many incredibly talented and dedicated people who made this book a reality. Without their help, the entire text of this book would read something like “Securing Ajax is hard.” We’ll never be able to thank them enough for the gifts of their time and expertise, but we’re going to try anyway. First and foremost, we have to thank our lovely, intelligent, and compassionate wives, Jill and Amy, for their support over the last year. We can only imagine how difficult it was to tell us “Get back to work on the book!” when what you really wanted to say was “Forget the book and take me out to dinner!” You are amazing women and we don’t deserve you. We want to thank our technical editors Trellum Technologies, Inc., Jeff Forristal, Joe Stagner, and Vinnie Liu. You made this book better than we ever hoped it could be. No, you weren’t too nitpicky. Yes, we can still be friends. We also want to thank everyone at SPI for their contributions and their understanding. While there were many SPIs who pitched in with their help, we want to single out two people in particular. Caleb Sima, this book would not be possible without your infinite wisdom. You have built an amazing company and we are honored and humbled to be a part of it. Ashley Vandiver, you did more work on this book than we ever had the right to ask for. Thank you so much. Special thanks go out to Samantha Black for her help with the “Web Attacks” and “Attacking the Presentation Layer” chapters. xxiii ACKNOWLEDGMENTS Finally, we would like to acknowledge the amazing staff at Addison-Wesley Professional and Pearson Education who helped bring Ajax Security to life: Sheri Cain, Alan Clements, Romny French, Karen Gettman, Gina Kanouse, Jake McFarland, Kathy Ruiz, Lisa Stumpf, Michael Thurston, and Kristin Weinberger. We especially want to thank Marie McKinley for her marketing expertise (and the Black Hat flyers!); Linda Harrison for making us sound like professional writers instead of computer programmers; and Chelsey Marti for her efforts with editing a document that was blocked by antivirus software. Rot-13 to the rescue! Last but certainly not least, thanks to our acquisitions editor Jessica Goldstein for believing in two novice authors and for keeping us moving forward throughout this adventure. To think it all started with a short, curly-haired pregnant woman asking the innocent question “So have you thought about writing a book?” What a fun, strange ride this has been. BILLY’S ACKNOWLEDGMENTS Thanks to my wife Jill. She kept me motivated and focused when all I wanted to do was give up and this book simply would not have been completed without her. Thanks to my parents, Mary and Billy, and my brother Jason. Without their unwavering support and love in all my endeavors I wouldn’t be half the person I am today. And of course, thanks to my co-author Bryan. Through long nights and crazy deadlines we created something to be proud of all while becoming closer friends. I can’t think of anyone else I would have wanted to write this book with. BRYAN’S ACKNOWLEDGMENTS Once again—and it’s still not enough—I have to thank my wife, Amy, for her love and support, not just during the writing of this book, but for every minute of the past 14 years. Finally, I can’t think of anyone with whom I would rather have spent my nights and weekends guzzling Red Bull and debating the relative merits of various CSRF defense strategies than you, Billy. It may have taken a little more blood, sweat, and tears than we originally anticipated, but we’ll always be able to say that we saved an entire generation of programmers from the shame and embarrassment of PA. xxiv About the Authors Billy Hoffman is the lead researcher for HP Security Labs of HP Software. At HP, Billy focuses on JavaScript source code analysis, automated discovery of Web application vulnerabilities, and Web crawling technologies. He has worked in the security space since 2001 after he wrote an article on cracking software for 2600, “The Hacker Quarterly,” and learned that people would pay him to be curious. Over the years Billy has worked a variety of projects including reverse engineering file formats, micro-controllers, JavaScript malware, and magstripes. He is the creator of Stripe Snoop, a suite of research tools that captures, modifies, validates, generates, analyzes, and shares data from magstripes. Billy’s work has been featured in Wired, Make magazine, Slashdot, G4TechTV, and in various other journals and Web sites. Billy is a regular presenter at hacker conferences including Toorcon, Shmoocon, Phreaknic, Summercon, and Outerz0ne and is active in the South East hacking scene. Occasionally the suits make him take off the black t-shirt and he speaks at more mainstream security events including RSA, Infosec, AJAXWorld, and Black Hat. Billy graduated from the Georgia Institute of Technology in 2005 with a BS in Computer Science with specializations in networking and embedded systems. He lives in Atlanta with his wife and two tubby and very spoiled cats. xxv ABOUT THE AUTHORS Bryan Sullivan is a software development manager for the Application Security Center division of HP Software. He has been a professional software developer and development manager for over 12 years, with the last five years focused on the Internet security software industry. Prior to HP, Bryan was a security researcher for SPI Dynamics, a leading Web application security company acquired by HP in August 2007. While at SPI, he created the DevInspect product, which analyzes Web applications for security vulnerabilities during development. Bryan is a frequent speaker at industry events, most recently AjaxWorld, Black Hat, and RSA. He was involved in the creation of the Application Vulnerability Description Language (AVDL) and has three patents on security assessment and remediation methodologies pending review. He is a graduate of the Georgia Institute of Technology with a BS in Applied Mathematics. When he’s not trying to break the Internet, Bryan spends as much time as he can on the golf links. If any Augusta National members are reading this, Bryan would be exceedingly happy to tell you everything he knows about Ajax security over a round or two. xxvi 1 Introduction to Ajax Security Myth: Ajax applications are just Web pages with extra bells and whistles. Ajax—Asynchronous JavaScript and XML—is taking the World Wide Web by storm. It is not at all an overstatement to say that Ajax has the potential to revolutionize the way we use the Internet—and even computers in general. Ajax is a fundamental component of Web 2.0, a complete re-imagining of what the Web is and what it is capable of being. We are already seeing the emergence of Ajax-based versions of historically desktop-based applications, like email clients and word processors. It may not be long before the Ajax versions overtake the desktop versions in popularity. The day may even come when all software is Web- and Ajax-based, and locally installed desktop applications are looked at as something of an anachronism, like punch cards or floppy disks. Why are we so optimistic about the future of Ajax? Ajax represents one of the holy grails of computing: the ability to write an application once and then deploy that same code on virtually any operating system or device. Even better, because you access the application from a central server, the application can be updated every day, or hundreds of times a day, without requiring a reinstallation on the client’s machine. “This is nothing new,” you say. “We’ve had this since the Web was invented in 1991!” That is true; but until the invention of Ajax, the necessity of Web pages to reload after every request limited their usefulness as replacements for everyday desktop applications. A spreadsheet application that reloads the entire workspace every time a cell is edited would be unusable. By updating only a portion of the page at a time, Ajax applications can overcome this limitation. The Web may allow us to write an application once and use it anywhere, but Ajax allows us to write a practical and effective application once and use it anywhere. 1 CHAPTER 1 INTRODUCTION TO AJAX SECURITY Unfortunately, there is one huge buzzing, stinging fly in the Ajax ointment: security. From a security perspective, Ajax applications are more difficult to design, develop, and test than traditional Web applications. Extra precautions must be taken at all stages of the development lifecycle in order to avoid security defects. Everyone involved in creating your Ajax application must have a thorough understanding of Ajax security issues or your project may be doomed to a very expensive and humiliating failure before it even gets off the ground. The purpose of this book is to arm you, whether you are a software programmer, architect, or tester, with the security knowledge you need to fend off the hackers’ attacks and create a truly secure and trustworthy Ajax application. AN AJAX PRIMER Before we delve into the particulars of Ajax security, it is worthwhile for us to briefly review the basics of Ajax technology. If you’re confident that you have a solid grasp of Ajax fundamentals, feel free to proceed to the next section, “The Ajax Architecture Shift.” WHAT IS AJAX? Normally, when a browser makes a request to a server for a dynamic Web page, it makes a request for the complete page. The server application responds by creating HTML for the page and returning it to the browser. The browser completes the operation by discarding its current page and rendering the new HTML into the browser window through which the user can view and act on it. This process is straightforward but also wasteful. Server processing power is often used to regenerate a new page for the client that is almost identical to the one that the client just discarded. Network bandwidth is strained as entire pages are needlessly sent across the wire. Users cannot use the application while their requests are being processed. They are forced to sit and wait for the server to get back to them. When the server’s response finally gets back to the browser, the browser flickers while it re-renders the entire page. It would be better for all parties if a Web client could request only a fragment of a page instead of having to request the entire page from the server. The server would be able to process the request more quickly, and less bandwidth would be needed to send the response. The client would have a more responsive interface because the round-trip time of the request would be shorter, and the irritating flicker caused by redrawing the entire page would be eliminated. 2 AN AJAX PRIMER Ajax is a collection of technologies that steps up to the challenge and allows the clientside piece of a Web application to continuously update portions of itself from the Web server. The user never has to submit the Web form or even leave the current page. Clientside scripting code (usually JavaScript) makes asynchronous, or non-blocking, requests for fragments of Web pages. These fragments can be raw data that are then transformed into HTML on the client, or they can be HTML fragments that are ready to be inserted directly into the document. In either case, after the server fulfills the request and returns the fragment to the client, the script code then modifies the page document object model (DOM) to incorporate the new data. This methodology not only satisfies our need for quick, smooth updates, but because the requests are made asynchronously, the user can even continue to use the application while the requests are in progress. WHAT AJAX IS NOT It is worth noting not just what Ajax is, but what it is not. Most people understand that Ajax is not a programming language in itself, but rather a collection of other technologies. What may be more surprising is that Ajax functionality is not something that necessarily needs to be turned on by the server. It is client-side code that makes the requests and processes the responses. As we will see, client-side code can be easily manipulated by an attacker. In October 2005, the Web site MySpace was hit with a Web virus. The Samy worm, as it came to be known, used Ajax techniques to replicate itself throughout the various pages of the site. What makes this remarkable is that MySpace was not using Ajax at the time! The Samy worm actually injected Ajax code into MySpace through a vulnerability in the MySpace code. A thorough case study of this ingenious attack can be found in Chapter 13, “JavaScript Worms.” To understand how Ajax works, let’s start by breaking the word into the parts of its acronym1: asynchronous, JavaScript, and XML. ASYNCHRONOUS In terms of usability, the biggest advantage that desktop applications have over Web applications is their speed of response. An average thick-client desktop application 1 Jesse James Garrett, who coined the term Ajax, claims that it is not an acronym. Pretty much everyone else in the world believes that it is. 3 CHAPTER 1 INTRODUCTION TO AJAX SECURITY will respond to a user’s action (like a button click) in microseconds. An average Web application takes much longer than that. Even the fastest Web sites operating under the best conditions will usually take at least a quarter of a second to respond when the time to redraw the page is factored in. Ajax applications like Live Search and Writely need to respond to frequently occurring events like mouse pointer movements and keyboard events. The latency involved in making a complete page postback for each sequential event makes postbacks completely impractical for real-time uses like these. We can decrease the response time by making smaller requests; or more specifically, by making requests that have smaller responses. Generally, a larger response will take the server more time to assemble than a smaller one. Moreover, a larger response will always take more time to transfer across the network than a smaller one. So, by making frequent small requests rather than infrequent large requests, we can improve the responsiveness of the application. Unfortunately, this only gets us part of the way to where we want to go. The real problem with Web applications is not so much that it takes a long time for the application to respond to user input, but rather that the user is blocked from performing any useful action from the time he submits his request to the time the browser renders the response. The user basically has to simply sit and wait, as you can see in Figure 1-1. User Request page Server Wait for response Process request Return complete page Work on page Wait for request or handle other users Request new page Wait for response Process request Figure 1-1 Classic synchronous Web request/response model 4 AN AJAX PRIMER Unless we can get round-trip response times in the hundredths-of-seconds range (which with today’s technology is simply impossible to accomplish), the synchronous request model will not be as responsive as a locally installed desktop application. The solution is to abandon the synchronous request model in favor of an asynchronous one. Requests are made just as they were before, but instead of blocking any further activity until the response comes back from the server, the client simply registers a callback method. When the response does come back, the callback method is called to handle updating the page. Until then, the user is free to continue using the application, as illustrated in Figure 1-2. He can even queue up multiple requests at the same time. User Request partial update Server Keep using page Process request Return partial update Work on page Wait for request or handle other users Request partial update Keep using page Process request Figure 1-2 Asynchronous Ajax request/response model The asynchronous nature of Ajax is the key to its responsiveness. We can only reduce the round-trip time of a request so far. With today’s technology we can’t reduce it enough to compete with the response time of a desktop application. Asynchronous requests do not execute any faster than synchronous ones; but, because they don’t force the user to sit and twiddle his or her thumbs until the response is returned, the application appears faster and more responsive. 5 CHAPTER 1 INTRODUCTION TO AJAX SECURITY JAVASCRIPT Client-side scripting code (JavaScript in particular) is the glue that holds Ajax together. Without the ability to perform complex actions on the client tier, we would be relegated to developing strictly thin-client, traditional Web applications circa 1995. The other technology facets of Ajax—asynchronicity and XML—are useless without script code to command them. JavaScript is required to send an asynchronous request and to handle the response. JavaScript is also required to process XML or to manipulate the DOM without requiring a complete page refresh. The JavaScript Standard While it is possible to write the client-side script of Ajax applications in a language other than JavaScript, it is the de facto standard for the Web world. As such, we will refer to JavaScript, alone, throughout this chapter. However, it is important to note that the security risks detailed in this chapter are not specific to JavaScript; any scripting language would share the same threats. Switching to VBScript or any other language will not help you create a more secure application. To demonstrate this, let’s look at a very simple example application before and after Ajax. This application displays the current time, along with a Refresh button. If we look at the HTML source code for the page shown in Figure 1-3, we can see that there is really not that much to see. Figure 1-3 A simple, non-Ajax application that displays the current time 6 AN AJAX PRIMER What time is it?
The current time is: 21:46:02
Now, let’s look at the same application (see Figure 1-4) after it’s been “Ajaxified”: Figure 1-4 An Ajax-enabled Web application that displays the current time On the surface, the application looks exactly the same as its predecessor. Under the covers, however, it is very different. Pressing the Refresh button no longer causes a complete page refresh. Instead, it simply calls back to the server to get the current time. When the response fragment is received from the server, the page updates only the time portion of the page text. While this may seem a little silly given the simplicity of the application, in a larger, real-world application, the usability improvements from this partial update could be very significant. So, let’s look at the HTML source code and see what has changed: 7 CHAPTER 1 INTRODUCTION TO AJAX SECURITY What time is it? The current time is: 18:34:44 8 AN AJAX PRIMER We can certainly see that the Ajax application is larger: There are four times as many lines of code for the Ajax version as there are for the non-Ajax version! Let’s dig a little deeper into the source code and find out what has been added. The application workflow starts as soon as the page is loaded in the browser. The variable httpRequest is set by calling the method getHttpRequest. The getHttpRequest method creates an XMLHttpRequest object, which is the object that allows the page to make asynchronous requests to the server. If one class could be said to be the key to Ajax, it would be XMLHttpRequest (sometimes abbreviated as XHR). Some of the key properties and methods of XHR are open send onreadystatechange readyState responseText Specifies properties of the request, such as the HTTP method, to be used and the URL to which the request will be sent. It is worth noting that open does not actually open a connection to a Web server; this is done when the send method is called. Sends the request. Specifies a callback function that will be called whenever the state of the request changes (for instance, from open to sent). The state of the request. A value of 4 indicates that a response has been received from the server. Note that this does not necessarily indicate that the request was successful. The text of the response received from the server. The XHR object is first used when the user presses the Refresh button. Instead of submitting a form back to the server as in the first sample, the Ajax sample executes the JavaScript method getCurrentTime. This method uses XHR to send an asynchronous request to the page getCurrentTime.php and registers the function handleCurrentTimeChanged as a callback method (that is, the method that will be called when the request state changes). Because the request is asynchronous, the application does not block while it is waiting for the server’s response. The user is only blocked for the fraction of a second that getCurrentTime takes to execute, which is so brief that the vast majority of users would not even notice. When a response is received from the server, handleCurrentTimeChanged takes the response, which is simply a string representation of the current time, and alters the page DOM to reflect the new value. The user is only briefly blocked, as shown in Figure 1-5. None of this would be possible without JavaScript. 9 CHAPTER 1 INTRODUCTION TO AJAX SECURITY User Create XHR object Make request Continue using application Receive response Process response; modify DOM Server Figure 1-5 Ajax Application Workflow Same Origin Policy The Same Origin Policy is the backbone of the JavaScript security model. In short, the JavaScript for any origin can only access or manipulate data from that same origin. An origin is defined by the triplet Domain + Protocol + Port. For example, JavaScript on a Web page from google.com cannot access the cookies for ebay.com. Table 1-1 shows what other pages can be accessed by JavaScript on the page http://www.site.com/page.html. Table 1-1 Applying the Same Origin Policy against http://www.site.com/page.html URL Access allowed? Reason http://www.site.com/dir/page2.html Yes Same domain, protocol, and port https://www.site.com/page.html No Different protocol http://sub.site.com/page.html No Different host http://site.com/page.html No Different host http://www.site.com:8080/page.html No Different port 10 THE AJAX ARCHITECTURE SHIFT The Same Origin Policy also prevents JavaScript from opening XMLHttpRequests to any server other than the same Web server that the user is currently visiting. XML XML is the last component of Ajax; and, to be perfectly honest, it is probably the least important component. JavaScript is the engine that makes the entire process of partial updates possible; and asynchronicity is a feature that makes partial updates worth doing; but, the use of XML is really just an optional way to build the requests and responses. Many Ajax frameworks use JavaScript Object Notation (JSON) in place of XML. In our earlier example (the page that displayed the current time) the data was transferred across the network as plain, unencapsulated text that was then dropped directly into the page DOM. DYNAMIC HTML (DHTML) While dynamic HTML (DHTML) is not part of the Ajax “acronym” and XML is, clientside manipulation of the page content is a much more critical function of Ajax applications than the parsing of XML responses. We can only assume that “Ajad” didn’t have the same ring to it that “Ajax” did. Once a response is received from the asynchronous request, the data or page fragment contained in the response has to be inserted back into the current page. This is accomplished by making modifications to the DOM. In the time server example earlier in the chapter, the handleCurrentTimeChanged function used the DOM interface method document.getElementById to find the HTML span in which the time was displayed. The handleCurrentTimeChanged method then called additional DOM methods to create a text node if necessary and then modify its contents. This is nothing new or revolutionary; but the fact that the dynamic content can be refreshed from the server and not be included with the initial response makes all the difference. Even simple applications like stock tickers would be impossible without the ability to fetch additional content from the server. THE AJAX ARCHITECTURE SHIFT Most of the earliest Web applications to use Ajax were basically standard Web sites with some extra visual flair. We began to see Web pages with text boxes that automatically suggested values after the user typed a few characters or panels that automatically collapsed and expanded as the user hovered over them with her mouse. These sites 11 CHAPTER 1 INTRODUCTION TO AJAX SECURITY provided some interesting eye candy for the user, but they didn’t really provide a substantially different experience from their predecessors. However, as Ajax matured we began to see some new applications that did take advantage of the unique new architecture to provide a vastly improved experience. MapQuest (www.mapquest.com) is an excellent example of Ajax’s potential to provide a completely new type of Web application: a Web application that has the look and feel of a desktop application. The Ajax-based MapQuest of 2007 is more than just a flashier version (no pun intended) of its older, non-Ajax incarnation. A MapQuest user can find her house, get directions from her house to her work, and get a list of pizza restaurants en route between the two, all on a single Web page. She never needs to wait for a complete refresh and redraw of the page as she would for a standard Web site. In the future, this type of application will define what we think of as an Ajax application much more than the Web site that just uses Ajax to makes its pages prettier. This is what we call the Ajax architecture shift. In order to understand the security implications of this shift, we need to understand the differences between Ajax applications and other client/server applications such as traditional Web sites. Without being too simplistic, we can think of these client/server applications as belonging to one of two groups: either thick client or thin client. As we will see, Ajax applications straddle the line between these two groups, and it is exactly this property that makes the applications so difficult to properly secure. THICK-CLIENT ARCHITECTURE Thick-client applications perform the majority of their processing on the client machine. They are typically installed on a desktop computer and then configured to communicate with a remote server. The remote server maintains some set of resources that are shared among all clients, such as a database or file share. Some application logic may be performed on the server, for instance, a database server may call stored procedures to validate requests or maintain data integrity. But for the most part, the burden of processing falls on the client (see Figure 1-6). Thick-client programs enjoy a number of advantages. The most important of these is a responsive user interface. When a user performs an action such as pressing a button or dragging and dropping a file, the time it takes the application to respond is usually measured in microseconds. The thick-client program owes its excellent response time to the fact that it can process the user’s action locally, without having to make remote requests across a network. The logic required to handle the request is already present on the user’s machine. Some actions, such as reading or writing files, do take a longer time to process. 12 THE AJAX ARCHITECTURE SHIFT Server responsibilities Query database Display UI Filter query results Handle user input Calculate order cost Write bill of materials Determine ship date Client responsibilities Figure 1-6 A sample thick-client architecture A well-designed thick-client application will perform these time-consuming tasks asynchronously. The user is able to proceed with other actions while the long-running operation continues in the background. On the other hand, there are disadvantages to thick-client architecture as well. In general, it is difficult to make updates or changes to thick-client desktop applications. The user is usually required to shut down the application, completely uninstall it from his machine, reinstall the new version, then finally restart the newly upgraded application and pick up where he left off. If changes have been made to the server component as well, then it is likely that any user who has not yet upgraded his client will not be able to use the application. Coordinating simultaneous upgrades of server and client installations across many users can be a nightmare for IT departments. There are some new technologies that are designed to ease the deployment of thick-client programs, like Java Web Start and .NET ClickOnce, but these, too, have limitations because they require other programs to be installed on the client (in this case, the Java 2 Runtime and the .NET Framework, respectively). THIN-CLIENT ARCHITECTURE Thin-client applications behave in exactly the opposite way from thick-client applications. The burden of processing falls mainly on the server, as illustrated in Figure 1-7. The job of the client module is simply to accept input from the user and display output 13 CHAPTER 1 INTRODUCTION TO AJAX SECURITY back to him. The dumb terminals and mainframe computers of the mid-twentieth century worked this way, as did early Web applications. The Web server processed all the business logic of the application, maintained any state required, constructed complete response messages for incoming requests, and sent them back to the user. The browser’s only role was to send requests to the Web server and render the returned HTML response so that a user could view it. The thin-client architecture solved the update problem that had plagued the thickclient developers. A Web browser acts as a universal client and doesn’t know or care what happens on the server side. The application can be modified on the server side every day, or ten times a day, and the users will just automatically pick up the changes. No reinstallations or reboots are required. It can even be changed while users are actively using it. This is a huge benefit to IT departments, who now do not need to coordinate extensive upgrade procedures for hundreds or thousands of users. Another great advantage of thin-client programs is found in the name itself: they’re thin. They don’t take up much space on the user’s machine. They don’t use much memory when they run. Most Web applications have a zero-footprint install, meaning they don’t require any disk space on the client machine at all. Server responsibilities Query database Filter query results Determine ship date Calculate order cost Write bill of materials Display UI Client responsibilities Figure 1-7 A sample thin-client architecture Handle user input 14 THE AJAX ARCHITECTURE SHIFT Users were thrilled with the advantages that thin-client Web applications provided, but eventually the novelty of the Web started to wear off. Users began to miss the robust user interfaces that they had come to expect in their desktop applications. Familiar methods of interaction, like dragging and dropping icons, were missing. Even worse, Web applications were simply not as responsive as desktop programs. Every click of the mouse meant a delay while the request was sent, processed at a Web server possibly thousands of miles away, and returned as HTML, which the browser then used to completely replace the existing page. No matter how powerful the processors of the Web servers were, or how much memory they had, or how much bandwidth the network had, there really was no getting around the fact that using a Web browser as a dumb terminal did not provide a robust user experience. The introduction of JavaScript and DHTML helped bring back some of the thickclient style user interface elements; but the functionality of the application was still limited by the fact that the pages could not be asynchronously updated with new data from the server. Complete page postbacks were still required to fetch new data. This made it impractical to use DHTML for applications like map and direction applications, because too much data—potentially gigabytes worth—needed to be downloaded to the client. This also made it impossible to use DHTML for applications that need to be continuously updated with fresh data, like stock tickers. It was not until the invention of XHR and Ajax that applications like these could be developed. AJAX:THE GOLDILOCKS OF ARCHITECTURE So, where does Ajax fit into the architecture scheme? Is it a thick-client architecture or a thin-client architecture? Ajax applications function in a Web browser and are not installed on the user’s machine, which are traits of thin-client architectures. However, they also perform a significant amount of application logic processing on the client machine, which is a trait of thick-client architectures. They make calls to servers to retrieve specific pieces of data, much like rich-client applications call database servers or file sharing servers. The answer is that Ajax applications are really neither thick- nor thin-client applications. They are something new; they are evenly-balanced applications (see Figure 1-8). In many ways, the Ajax framework is the best of both worlds: It has the rich user interface found in good desktop applications; and it has the zero-footprint install and ease of maintenance found in Web applications. For these reasons, many software industry analysts predict that Ajax will become a widely-adopted major technology. In terms of security, however, Ajax is actually the worst of both worlds. It has the inherent security vulnerabilities of both architectures. 15 CHAPTER 1 INTRODUCTION TO AJAX SECURITY Server responsibilities Query database Filter query results Determine ship date Write bill of materials Display UI Handle user input Calculate order cost Client responsibilities Figure 1-8 A sample Ajax architecture: evenly balanced between the client and server A SECURITY PERSPECTIVE:THICK-CLIENT APPLICATIONS The major security concern with thick-client applications is that so much of the application logic resides on the user’s machine—outside the effective control of the owner. Most software programs contain proprietary information to some extent. The ability of an application to perform a task differently and better than its competitors is what makes it worth buying. The creators of the programs, therefore, usually make an effort to keep their proprietary information a secret. The problem with installing secrets (in this case, the logic of the application) on a remote machine is that a determined user can make sure they don’t remain secrets very long. Armed with decompilers and debuggers, the user can turn the installed application back into the source code from which it originated and then probe the source for any security weaknesses. He can also potentially change the program, perhaps in order to crack its licensing scheme. In short, the client machine is an uncontrollable, hostile environment and a poor location in which to store secret information. The security risks of thick-client applications are summarized in Table 1-2. 16 THE AJAX ARCHITECTURE SHIFT Table 1-2 Security risks of thick-client applications Risk Applicable to thick-client applications? Application logic is accessible on the client X Messages between client and server are easily intercepted and understood The application is generally accessible to anonymous public users A SECURITY PERSPECTIVE:THIN-CLIENT APPLICATIONS Thin-client programs have a different set of security concerns (see Table 1-3). Most, if not all, of the valuable business logic of the application remains hidden from the user on the server side. Attackers cannot simply decompile the application to learn its inner workings. If the Web server is properly configured, attackers have no way to directly retrieve this programming logic. When a hacker attempts to break into a Web site, she has to perform a lot of reconnaissance work to try to gain information about the application. She will perform attacks that are designed not to gain unauthorized access to the server or to steal users’ personal data, but simply to learn a little more about the technologies being used. The hacker may examine the raw HTTP responses from the server to determine what types and version numbers of operating systems and Web servers are being used. She may examine the HTML that is being returned to look for hidden comments. Often, programmers insert information (like authentication credentials used for testing) into HTML comments without realizing that the information can easily be read by an end user. Another trick attackers use is to intentionally generate an error message in the application, which can potentially reveal which databases or application servers are being used. Because all this effort is required to reveal fragments of the logic of the thin-client application and thick-client applications can be easily decompiled and analyzed, it seems that thin-client applications are inherently more secure, right? Not exactly. Every roundtrip between client and server provides an opportunity for an attacker to intercept or tamper with the message being sent. While this is true for all architectures, thin-client programs (especially Web applications) tend to make many more round-trips than thick-client programs. Furthermore, Web applications communicate in HTTP, a wellknown, text-based protocol. If an attacker were to intercept an HTTP message, he could probably understand the contents. Thick-client programs often communicate in binary protocols, which are much more difficult for a third-party to interpret. Before, we ran 17 CHAPTER 1 INTRODUCTION TO AJAX SECURITY into security problems by leaving secrets on the user’s machine, outside of our control. Now, we run into security problems by sending secrets back and forth between the client and the server and pretending no one else can see them. Another important security consideration for Web applications is that they are generally freely accessible to any anonymous person who wants to use them. You don’t need an installation disk to use a Web site; you just need to know its URL. True, some Web sites do require users to be authenticated. You cannot gain access to classified military secrets just by pointing your browser to the U.S. Department of Defense (DoD) Web site. If there were such secrets available on the DoD site, certainly the site administrator would issue accounts only to those users permitted to view them. However, even in such a case, a hacker at least has a starting point from which to mount an attack. Compare this situation to attacking a thick-client application. In the thick-client case, even if the attacker manages to obtain the client portion of the application, it may be that the server portion of the application is only accessible on a certain internal network disconnected from the rest of the outside world. Our hacker may have to physically break into a particular office building in order to mount an attack against the server. That is orders of magnitude more dangerous then being able to crack it while sitting in a basement 1,000 miles away eating pizza and drinking Red Bull. Table 1-3 Security risks of thin-client applications Risk Applicable to thin-client applications? Application logic is accessible on the client Messages between client and server are easily X intercepted and understood The application is generally accessible to X anonymous public users A SECURITY PERSPECTIVE: AJAX APPLICATIONS Unfortunately, while Ajax incorporates the best capabilities of both thick-client and thin-client architectures, it is also vulnerable to the same attacks that affect both types of applications. Earlier, we described thick-client applications as insecure because they could be decompiled and analyzed by an attacker. The same problem exists with Ajax applications, and, in fact, even more so, because in most cases the attacker does not even need to go to the effort of decompiling the program. JavaScript is what is known as an 18 THE AJAX ARCHITECTURE SHIFT interpreted language, rather than a compiled language. When a developer adds clientside JavaScript to his Web application, he actually adds the source code of the script to the Web page. When a Web browser executes the JavaScript embedded in that page, it is directly reading and interpreting that source code. If a user wanted to see that source code for himself, all he would have to do is to click the View Page Source command in his browser. Furthermore, Ajax Web applications still use HTTP messages, which are easy to intercept, to communicate between the client and the server just like traditional Web applications. And, they are still generally accessible to any anonymous user. So, Ajax applications are subject to the security risks of both thick- and thin-client applications (see Table 1-4). Table 1-4 Security risks of Ajax applications Risk Application logic is accessible on the client Messages between client and server are easily intercepted and understood The application is generally accessible to anonymous public users Applicable to Ajax applications? X X X A PERFECT STORM OF VULNERABILITIES The Ajax architecture shift has security ramifications beyond just incorporating the inherent dangers of both thin- and thick-client designs. It has actually created a perfect storm of potential vulnerabilities by impacting application security in three major ways: • Ajax applications are more complex. • Ajax applications are more transparent. • Ajax applications are larger. INCREASED COMPLEXITY,TRANSPARENCY, AND SIZE The increased complexity of Ajax applications comes from the fact that two completely separate systems—the Web server and the client’s browser—now have to work together 19 CHAPTER 1 INTRODUCTION TO AJAX SECURITY in unison (and asynchronously) in order to allow the application to function properly. There are extra considerations that need to be taken into account when designing an asynchronous system. Essentially you are creating a multithreaded application instead of a single-threaded one. The primary thread continues to handle user actions while a background thread processes the actions. This multithreaded aspect makes the application harder to design and opens the door to all kinds of synchronization problems, including race conditions. Not only are these problems some of the hardest to reproduce and fix, but they can also cause serious security vulnerabilities. A race condition in a product order form might allow an attacker to alter the contents of her order without changing the corresponding order cost. For example, she might add a new plasma HDTV to her shopping cart and quickly submit the order before the order cost was updated to reflect the $2,500 addition. When we say that Ajax applications are more transparent, what we mean is that more of the internal workings of the applications are exposed to the client. Traditional Web applications function as a sort of black box. Input goes in and output comes out, but no one outside of the development team knows how or why. The application logic is handled almost completely by the server. On the other hand, Ajax applications need to execute significant portions of their logic on the client. This means that code needs to be downloaded to the client machine, and any code downloaded to a client machine is susceptible to reverse engineering. Furthermore, as we just mentioned in the previous section, the most commonly used client-side languages (including JavaScript) are interpreted languages rather than compiled languages. In other words, the client-side portion of the application is sent in raw source code form to the client, where anyone can read it. Additionally, in order for Ajax client code to communicate effectively with the corresponding server portion of the application, the server code needs to provide what is essentially an application programming interface (API) to allow clients to access it. The very existence of a server API increases the transparency of the server-side code. As the API becomes more granular (to improve the performance and responsiveness of the application), the transparency also increases. In short, the more “Ajax-y” the application, the more its inner workings are exposed. This is a problem because the server methods are accessible not just by the client-side code that the developers wrote, but by any outside party as well. An attacker may choose to call your server-side code in a completely different manner than you originally intended. As an example, look at the following block of client-side JavaScript from an online music store. function purchaseSong(username, password, songId) { // first authenticate the user if (checkCredentials(username, password) == false) { 20 A PERFECT STORM OF VULNERABILITIES alert('The username or password is incorrect.'); return; } // get the price of the song var songPrice = getSongPrice(songId); // make sure the user has enough money in his account if (getAccountBalance(username) < songPrice) { alert('You do not have enough money in your account.'); return; } // debit the user's account debitAccount(username, songPrice); // start downloading the song to the client machine downloadSong(songId); } In this example, the server API has exposed five methods: 1. checkCredentials 2. getSongPrice 3. getAccountBalance 4. debitAccount 5. downloadSong The application programmers intended these methods to be called by the client in this exact order. First, the application would ensure that the user was logged in. Next, it would ensure that she had enough money in her account to purchase the song she requested. If so, then her account would be debited by the appropriate amount, and the song would be downloaded to her machine. This code will execute flawlessly on a legitimate user’s machine. However, a malicious user could twist this code in several nasty ways. He could • Omit the authentication, balance checking, and account debiting steps and simply call the downloadSong method directly. This gives him all the free music he wants! • Change the price of the song by modifying the value of the songPrice variable. While it is true that he can already get songs for free simply by skipping over the 21 CHAPTER 1 INTRODUCTION TO AJAX SECURITY debitAccount function, he might check to see if the server accepts negative values for the songPrice parameter. If this worked, the store would actually be paying the hacker to take the music. • Obtain the current balance of any user’s account. Because the getAccountBalance function does not require a corresponding password parameter for the username parameter, that information is available just by knowing the username. Worse, the debitAccount function works the same way. It would be possible to completely wipe out all of the money in any user’s account. The existence of a server API also increases the attack surface of the application. An application’s attack surface is defined as all of the areas of the application that an attacker could potentially penetrate. The most commonly attacked portions of any Web application are its inputs. For traditional Web applications, these inputs include any form inputs, the query string, the HTTP request cookies, and headers, among others. Ajax applications use of all of these inputs, and they add the server APIs. The addition of the API methods represents a potentially huge increase in the number of inputs that must be defended. In fact, not only should each method in an API be considered part of the application’s attack surface, but so should each parameter of each method in an API. It can be very easy for a programmer to forget to apply proper validation techniques to individual parameters of server methods, especially because a parameter may not be vulnerable when accessed through the client-side code. The client-side code may constrain the user to send only certain parameter values: 5-digit postal codes for example, or integers between 0 and 100. But as we saw earlier, attackers are not limited by the rules imposed on the client-side code. They can bypass the intended client-side code and call the server-side functions directly—and in unexpected ways. They might send 6 digits for the postal code field or alphabetic characters instead of integers. If the parameter value was being used as part of a SQL query filter in the server code, it is possible that an attacker might be able to inject SQL code of her choosing into the parameter. The malicious SQL code would then be executed on the server. This is a very common and dangerous attack known as SQL Injection, and it can result in the entire backend database being stolen or destroyed. SOCIOLOGICAL ISSUES Beyond just the technical issues involved with making Ajax a perfect storm for security vulnerabilities, there are also sociological issues that contribute to the problem. Economics dictate that supply of a service will grow to fill demand for that service, even at the expense of overall quality. The demand for Ajax programmers has grown at an 22 A PERFECT STORM OF VULNERABILITIES incredible rate, fueled, at least in part, by the billions of dollars being poured into Web 2.0 site acquisitions. Unfortunately, even though the individual technologies that comprise Ajax have been around for years, their combined, cooperative use (essentially what we refer to as Ajax programming) is relatively new. There has not been much time or opportunity for individuals to learn the intricacies of Ajax development. Because Ajax is such a young technology, most technical resources are targeted at beginners. Also, virtually no one “rolls their own” Ajax framework. Instead, most people use one of the publicly-available third-party frameworks, such as Prototype. There are definitely benefits to this approach—no one likes to spend time reinventing the wheel—but there are also drawbacks. The whole point of using a predeveloped framework is that it simplifies development by shielding the programmer from implementation details. Hence, using a framework actually (or at least implicitly) discourages developers from learning about why their application works the way it does. These factors add up to an equation as follows: Sky-high demand + Tight deadlines + Limited opportunity for training + Easy access to predeveloped frameworks = A glut of programmers who know that an application works, but not why This is a disturbing conclusion, because it is impossible to accurately assess security risks without understanding the internal plumbing of the application. For example, many programmers don’t realize that attackers can change the intended behavior of the clientside code, as we described in the previous section. AJAX APPLICATIONS: ATTRACTIVE AND STRATEGIC TARGETS We have established that Ajax applications are easier to attack than either thick-client applications or traditional Web applications, but why attack them in the first place? What is there to gain? When you stop and think about it, Web sites can be the gateway to every major aspect of a company’s business, and accordingly, they often access all kinds of services to retrieve valuable information. 23 CHAPTER 1 INTRODUCTION TO AJAX SECURITY Consider an e-commerce Web site. Such a site must have access to a database of customer records in order to be able to identify and track its users. This database will typically contain customer names, addresses, telephones numbers, and email addresses as well as usernames and passwords. The Web site must also contain an orders database so that the site can create new orders, track existing orders, and display purchase histories. Finally, the Web site needs to be able to communicate with a financial system in order to properly bill customers. As a result, the Web site may have access to stored account numbers, credit card accounts, billing addresses, and possibly routing numbers. The value of the financial data to a hacker is obvious, but the customer records can be valuable as well. Email addresses and physical mailing addresses can be harvested and sold to spammers or junk mail list vendors. Sometimes the hacker’s end goal is not to steal the application’s data directly, but to simply gain unauthorized access to use the application. Instead of retrieving the entire database, a hacker might be content to simply take control of a single user’s account. He could then use his victim’s credentials to purchase items for himself, essentially committing identity theft. Sometimes the attacker has no more sophisticated goal than to embarrass you by defacing your site or to shut you down by creating a denial of service. This may be the aim of a bored teenager looking to impress his friends, or it may be a competitor or blackmailer looking to inflict serious financial damage. This is by no means a complete list of hackers’ goals, but it should give you an idea of the seriousness of the threat. If your application were to be compromised, there would be direct monetary consequences (blackmail, system downtime), loss of customer trust (stolen financial and personal information), as well as legal compliance issues like California Senate Bill 1386 and the Graham-Leach-Bliley Act. CONCLUSIONS Ajax is an incredible technology that truly has the potential to revolutionize the way we use the Internet. If and when the promise of Ajax is fulfilled, we could experience a new boom in the quality of interactivity of Web applications. But, it would be a shame for this boom to be mirrored by an increase in the number of Web applications being hacked. Ajax applications must not be treated simply as standard Web applications with extra bells and whistles. The evenly-balanced nature of Ajax applications represents a fundamental shift in application architecture, and the security consequences of this shift must be respected. Unless properly designed and implemented, Ajax applications will be exploited by hackers, and they will be exploited more frequently and more severely than traditional Web applications. To prove this point, the next chapter, “The Heist,” will chronicle the penetration of a poorly designed and implemented sample Ajax application by an attacker. 24 2The Heist Myth: Hackers rarely attack enterprises through their Ajax applications. Enter the authors’ rebuttal witness: Eve. EVE You wouldn’t even remember her if you saw her again. It’s not that the 20-something woman in the corner isn’t a remarkable person—she is. But she’s purposely dressed lowkey, hasn’t said more than ten words to anyone, and hasn’t done anything to draw any attention to herself. Besides, this Caribou Coffee is located at 10th Street and Piedmont, right in the heart of trendy Midtown Atlanta, where there are far more interesting things to look at than a bespectacled woman typing on a ThinkPad at a corner table. She purchased coffee and a bagel when she arrived and a refill an hour later. Obviously, she paid in cash; no sense leaving a giant electronic flag placing her at this location at a specific time. Her purchases are just enough to make the cashier happy that she isn’t a freeloader there to mooch the free Wi-Fi Internet access. Wireless signals go right through walls, so she could have done this from her car out in the parking lot. But it would look rather suspicious to anyone if she was sitting in a Jetta in a crowded parking lot with a laptop in her hands—much better to come inside and just blend in. Even better, she notices some blonde kid in a black t-shirt sitting in the middle of the shop. He types away on a stock Dell laptop whose lid is covered with stickers that say, “FreeBSD 4 Life,” “2600,” and “Free Kevin!” She chuckles under her breath; script kiddies always 25 CHAPTER 2 THE HEIST choose causes as lame as their cheap computer equipment. Even assuming that what she does tonight ever gets traced back to this coffee shop (which she doubts), the hacker wannabe in a Metallica t-shirt is the one people will remember. No one ever suspects Eve. And that’s the way she likes it. HACKING HIGHTECHVACATIONS.NET Her target today is a travel Web site, HighTechVacations.net. She read about the site in a news story on Ajaxian, a popular Ajax news site. Eve likes Web applications. The entire World Wide Web is her hunting ground. If she strikes out trying to hack one target Web site, she is just a Google search away from thousands more. Eve especially likes Ajax applications. There are all sorts of security ramifications associated with creating responsive applications that have powerful client-side features. Better yet, the technology is new enough that people are making fairly basic mistakes, and no one seems to be providing good security practices. To top it all off, new bright-eyed Web developers are flocking to Ajax every day and are overwhelming the space with insecure applications. Eve chuckles. She loves a target-rich environment! Eve approaches HighTechVacations.net like any other target. She makes sure all her Web traffic is being recorded through an HTTP proxy on her local machine and begins browsing around the site. She creates an account, uses the search feature, enters data in the form to submit feedback, and begins booking a flight from Atlanta to Las Vegas. She notices that the site switches to SSL. She examines the SSL certificate and smiles: It is self-signed. Not only is this a big mistake when it comes to deploying secure Web sites, it’s also a sign of sloppy administrators or an IT department in a cash crunch. Either way, it’s a good sign for Eve. HACKING THE COUPON SYSTEM Eve continues using the site and ends up in the checkout phase when she notices something interesting: a Coupon Code field on the form. She types in FREE and tabs to the next field on the form. Her browser immediately displays an error message telling Eve that her coupon code is not valid. That’s odd. How did the Web site calculate that it wasn’t a valid coupon code so quickly? Perhaps they used Ajax to send a request back to the server? Eve decides to look under the hood at the source code to see what’s happening. She rightclicks her mouse to view the source and is presented with the message in Figure 2-1. Eve is stunned. HighTechVacations.net actually thinks they can prevent her from looking at the HTML source? That is ridiculous. Her browser has to render the HTML, so obviously the HTML cannot be hidden. A little bit of JavaScript that traps her right-click event and suppresses the context menu isn’t going to stop Eve! She opens the Firebug 26 HACKING HIGHTECHVACATIONS.NET extension for Firefox. This handy JavaScript debugger shows Eve all the JavaScript code referenced on the current page, as shown in Figure 2-2. Figure 2-1 The checkout page on HighTechVacations.net prevents right mouse clicks. There’s a problem. This JavaScript is obfuscated. All the spaces have been removed, and some of the variables and function names have been purposely shortened to make it harder for a human to understand. Eve knows that this JavaScript code, while difficult for her to read, is perfectly readable to the JavaScript interpreter inside her Web browser. Eve runs a tool of her own creation, the JavaScript Reverser. This program takes JavaScript (obfuscated or not) and parses it just like the JavaScript interpreter in the browser would. It tells her all the variables and function names, where and how often they are used, which functions call which other functions, and what arguments they are called with. In addition, the JavaScript Reverser also inserts white space into the code to make it much easier for a human to read. Eve is anxious to see what’s in this JavaScript because the developer has taken so many steps to prevent someone from looking at the code. Figure 2-3 provides the code Eve’s JavaScript Reverser generates. 27 CHAPTER 2 THE HEIST Figure 2-2 Firebug, a JavaScript debugger, shows the obfuscated code for HighTechVacations.net. Figure 2-3 The JavaScript Reverser analyzes JavaScript from HighTechVacations.net to aid Eve in understanding what it does. 28 HACKING HIGHTECHVACATIONS.NET Eve quickly locates a function called addEvent, which attaches JavaScript event listeners in a browser-independent way. She searches for all places addEvent is used and sees that it’s used to attach the function checkCoupon to the onblur event for the coupon code text box. This is the function that was called when Eve tabbed out of the coupon field in the form and somehow determined that FREE was not a valid coupon code. The checkCoupon function simply extracts the coupon code entered into the text box and calls isValidCoupon. Here is a snippet of un-obfuscated code around the isValidCoupon function: var coupons = ["oSMR0.]1/381Lpnk", "oSMR0._6/381LPNK", "oSWRN3U6/381LPNK", "oSWRN8U2/561O.WKE", "oSWRN2[.0:8/O15TEG", "oSWRN3Y.1:8/O15TEG", "oSWRN4_.258/O15TEG", "tQOWC2U2RY5DkB[X", "tQOWC3U2RY5DkB[X", "tQOWC3UCTX5DkB[X", "tQOWC4UCTX5DkB[X", "uJX6,GzFD", "uJX7,GzFD", "uJX8,GzFD"]; function crypt(s) { var ret = ''; for(var i = 0; i < s.length; i++) { var x = 1; if( (i % 2) == 0) { x += 7; } if( (i % 3) ==0) { x *= 5; } if( (i % 4) == 0) { x -= 9; } ret += String.fromCharCode(s.charCodeAt(i) + x); } return ret; } function isValidCoupon(coupon) { 29 CHAPTER 2 THE HEIST coupon = coupon.toUpperCase(); for(var i = 0; i < coupons.length; i++) { if(crypt(coupon) == coupons[i]) return true; } return false; } The coupon code Eve enters is passed to isValidCoupon where it is uppercased, encrypted, and compared against a list of encrypted values. Eve looks the crypt function and barely contains a laugh. The encryption is just some basic math operations that use a character’s position in the string to calculate a number. This number is added to the ASCII code of the plaintext character to get the ASCII code of the encrypted character. This “encryption” algorithm is a textbook example of a trivial encryption algorithm, an algorithm that can be easily reversed and broken (for example, Pig Latin would be considered a trivial encryption of English). Decrypting an encrypted coupon code is as simple as subtracting the number from the ASCII code for an encrypted character. Eve quickly copies the coupons array and crypt function into a new HTML file on her local machine and modifies the crypt function into a decrypt function. Her page looks like this: Eve opens this HTML page in her Web browser and gets a series of pop ups producing all the valid coupon codes available for booking flights on HighTechVacations.net. The full list is: • PREM1—500.00—OFF • PREM1—750.00—OFF • PROMO2—50.00—OFF • PROMO7—100.00—OFF • PROMO13—150.00—OFF • PROMO14—200.00—OFF • PROMO21—250.00—OFF • PROMO37—300.00—OFF • UPGRD1—1ST—CLASS • UPGRD2—1ST—CLASS • UPGRD2—BUS—CLASS • UPGRD3—BUS—CLASS • VIP1—FREE 31 CHAPTER 2 THE HEIST • VIP2—FREE • VIP3—FREE Eve makes a note of all of these codes. She can use them herself or sell the information to other people on the Internet. Either way, Eve knows she won’t be paying for her trip to Las Vegas this year! ATTACKING CLIENT-SIDE DATA BINDING Still hungry for more valuable data, Eve decides to examine the search feature of HighTechVacations.net. She makes another search for a flight from Atlanta to Las Vegas. She notices that the search page does not refresh or move to another URL. Obviously, the search feature is using Ajax to talk to a Web service of some kind and dynamically load the results of her search. Eve double-checks to make sure all of her Web traffic is funneled through an HTTP proxy she is running on her machine, which will allow her to see the Ajax requests and responses. Eve saves a copy of all traffic her HTTP proxy has captured so far and restarts it. She flips over to her Web browser, and performs a search for flights leaving Hartsfield-Jackson International Airport in Atlanta to McCarran International Airport in Las Vegas on July 27. After a slight delay Eve gets back a series of flights. She flips over to the proxy and examines the Ajax request and response, as shown in Figure 2-4. Eve sees that HighTechVacations.net is using JavaScript Object Notation (JSON) as the data representation layer, which is a fairly common practice for Ajax applications. A quick Google search tells Eve that ATL and LAS are the airport codes for Atlanta and Las Vegas. The rest of the JSON array is easy to figure out: 2007-07-27 is a date and the 7 is how many days Eve wanted to stay in Las Vegas. Eve now understands the format of the requests to the flight search Web service. Eve knows that the departure airport, destination airport, and flight are all most likely passed to a database of some kind to find matching flights. Eve decides to try a simple probe to see if this backend database might be susceptible to a SQL Injection attack. She configures her proxy with some find-andreplace rules. Whenever the proxy sees ATL, LAS, or 2007-07-27 in an outgoing HTTP request, the proxy will replace those values with ' OR before sending the request to HighTechVacations.net. Eve’s ' OR probe in each value might create a syntax error in the database query and give her a database error message. Detailed error messages are Eve’s best friends! 32 HACKING HIGHTECHVACATIONS.NET Figure 2-4 Eve’s flight search request made with Ajax and the response Eve brings her Web browser back up and searches for flights from Atlanta to Las Vegas yet again. She waits…and waits…and nothing happens. That’s odd. Eve checks her HTTP proxy, shown in Figure 2-5. So Eve’s request with SQL Injection probes was included in the request, and the server responded with a nice, detailed error message. The JavaScript callback function that handles the Ajax response with the flight information apparently suppresses errors returned by the server. Too bad the raw database error message was already sent over the wire where Eve can see it! The error message also tells her that the database server is Microsoft’s SQL Server. Eve knows she has a textbook case of verbose SQL Injection here, but Eve suspects she also has a case of client-side data transformation. HighTechVacations.net’s Web server takes the flight results from the database query and sends them directly to the client, which formats the data and displays it to the user. With server-side data transformation, the database results are collected and formatted on the server instead of the client. This means extra data—or incorrectly formatted data—that’s 33 CHAPTER 2 THE HEIST returned from the database is discarded by the server when it binds that into a presentational form, preventing Eve from seeing it. With client-side data transformation, which is usually found only in Ajax applications, Eve can piggyback malicious SQL queries and capture the raw database results as they are sent to the client-side JavaScript for formatting. Figure 2-5 Eve’s probes caused an ODBC error. Client-side JavaScript suppresses the error, and it does not appear in her Web browser. Eve fires up another tool, her HTTP editor. This tool allows Eve to craft raw HTTP requests to the Web server instead of using find-and-replace rules in the proxy to inject malicious data. With a little trial and error, Eve determines that she can piggyback a SQL command on top of the date parameter inside of the JSON in her request. Because Eve is attacking MS SQL Server, she sends a query against the SYSOBJECTS table, shown in Figure 2-6, to retrieve a list of all the user-defined tables in HighTechVacations.net’s database. 34 HACKING HIGHTECHVACATIONS.NET Figure 2-6 Eve retrieves a list of all the user-defined tables in the Web site’s database with just a single query. There are many interesting tables here for Eve, including Specials, Orders, Billing, and Users. Eve decides to select everything out of the Users table, as shown in Figure 2-7. Awesome! Eve just retrieved information about all of the users with a single request! HighTechVacations.net was susceptible to SQL Injection, but the fact that they used client-side transformation instead of server-side transformation means that Eve can steal their entire database with just a few queries instead of waiting a long time using an automated SQL Injection tool like Absinthe. Eve is very happy that she harvested a list of usernames and passwords. People often use the same username and password on other Web sites. Eve can leverage the results from this hack into new attacks. By exploiting HighTechVacations.net, Eve might be able to break into other totally unrelated Web sites. Who knows, before the night is over Eve could be accessing someone’s bank accounts, student loans, mortgages, or 401(k)s. She takes a few minutes to pull the usernames and encrypted passwords from the results. Eve 35 CHAPTER 2 THE HEIST is not sure how the passwords are encrypted, but each password is exactly 32 hexadecimal digits long. They are most likely MD5 hashes of the actual passwords. Eve fires up John the Ripper, a password cracking utility, and starts cracking the list of passwords before grabbing the Billing and JOIN_Billing_Users tables. These tables give her billing information, including credit card numbers, expiration dates, and billing addresses for all the users on HighTechVacations.net. Figure 2-7 Eve retrieves every column of every row from the Users table with a single query. ATTACKING THE AJAX API Eve decides to take a closer look at the pages she has seen so far. Eve checks and notices that every Web page contains a reference to common.js. However, not every Web page uses all the functions defined inside common.js. For example, common.js contains the isCouponValid function even though only the checkout pages use it. Eve knows it’s possible there are other functions in common.js used by Web pages that Eve hasn’t seen yet. 36 HACKING HIGHTECHVACATIONS.NET There could even be administrative functions that visitors aren’t supposed to use! Eve looks through the list of variables and functions found by her JavaScript Reverser and almost skips right past it. Nestled right in the middle of a list of boring Ajax functions she sees something odd: a function named AjaxCalls.admin.addUser, shown toward the middle of Figure 2-8. Figure 2-8 A reference in common.js to an unused administrator function,AjaxCalls.admin.addUser. The function itself doesn’t tell Eve very much. It is a wrapper that calls a Web service to do all the heavy lifting. However, the name seems to imply some kind of administrative function. Eve quickly searches all the responses captured by her HTTP proxy. There are no references to the addUser function on any page she has visited so far. Eve is intrigued. Why is this function in common.js? Is it a mistake? Once again, Eve fires up her HTTP editor. She knows the URL for the Web service that addUser contacts and she knows that she needs to use a POST when sending requests, but 37 CHAPTER 2 THE HEIST that’s about it. All the other Web services seem to use JSON, so Eve sends a POST request to /ajaxcalls/addUser.aspx with an empty JSON array as shown in Figure 2-9. Figure 2-9 The addUser.aspx Web service responds with an error message to improperly formatted requests. Interesting. The Web site responded with an error message telling Eve that her request was missing some parameters. Eve fills in one bogus parameter and resubmits the request. Figure 2-10 shows this transaction. Eve creeps to the edge of her seat. Her bogus shot in the dark actually accomplished something. The Web service didn’t seem to add a user, but it told her she is now only missing three items instead of four. Eve stops and thinks. She knows she needs to pass this Web service four parameters in JSON. She can make an educated guess as to what kind of data is needed: probably an account name, a real name, a password, and some kind of flag. She knows that flags are commonly Boolean values but she is unsure what format she should use. Eve quickly puts together a request with plausible values and sends it, as shown in Figure 2-11. 38 HACKING HIGHTECHVACATIONS.NET Figure 2-10 Eve’s dummy parameter has solicited a different error message from the addUser Web service. Figure 2-11 The Web service rejects Eve’s request because of an invalid debugflag value. 39 CHAPTER 2 THE HEIST Uh-oh. This is what Eve was worried about. She is sending the parameters in the correct form but it looks like the last one, debugflag, is wrong. Flags are either on or off. Eve thought that sending “true” would work but it doesn’t. Eve tries various other values: “true” with quotes, true uppercased, false, but all fail. On a whim, Eve tries a “1” for the debugflag value. Some programming languages like C don’t have a native true or false, but instead use a “1” or a “0” as the respective values. The transaction is shown in Figure 2-12. Figure 2-12 Eve guesses “1” for the value of debugflag and her request is accepted. Eve can’t believe her eyes. It worked! She’s not totally sure what kind of account she just created, or where that account is, but she just created an account called eve6. Eve points her HTTP editor back at the flight search Web service and performs another SQL Injection attack to dump the list of users again. Sure enough, there is now an account for eve6 in the list. Eve still does not know what the debugflag does or where it is stored. She could dig deeper in the database looking for it, but instead decides to try out her new account. Eve opens a new tab in her browser and logs in under her new eve6 account. 40 HACKING HIGHTECHVACATIONS.NET Figure 2-13 shows the HighTechVacation.net Web site while being accessed using the eve6 account. Figure 2-13 HighTechVacations.net presents a different interface to debug account users. Everything is different! Eve sees data about the particular Web server she is using, the server load, and information about her request. What interests Eve the most is the Debug menu bar. While there are many options to explore here, Eve immediately focuses on the Return to Admin link. After all, she didn’t get here from an administration page, so what happens if she tries to go back to one? Eve clicks the link and receives the Web page shown in Figure 2-14. Wow! Eve seems to have caused some kind of null object exception. Plus, she now knows the location of the administrator area. Eve often uses tools like Nikto to bruteforce common directories like admin and logs but she doesn’t have /SiteConfig/ on her 41 CHAPTER 2 THE HEIST list of directories to guess, and so she would have missed this admin portal. It is odd that some parts of the Web site seem to think the eve6 account is an administrator or QA tester, while others deny access. The null object exception might have been caused when the backend application tried to pull information about eve6 that wasn’t there because eve6 isn’t actually an administrator. Apparently, the developers on HighTechVacations.net made the mistake of thinking that administrative Web services like addUser could only be accessed from the administrative portal, and so they only perform authentication and authorization checks when a user tries to access to the portal. By directly talking to addUser or other Web services, Eve is able to perform all the actions of an administrator without actually using the administrative portal. Figure 2-14 The administrator area accessible from the debug version of HighTechVacations.net. A THEFT IN THE NIGHT Eve yawns, sips the last of her coffee, and stretches. Her hack has been a complete success so far. She has cracked all the promotional codes for free airline tickets. She has a list of all the usernames and is currently cracking their passwords. She has a copy of the credit card data for anyone who has ever booked a flight with HighTechVacations.net. She has created a backdoor account with (slightly unstable) administrator or QA privileges. And finally, she has located the login for an administrative portal that could possibly give her access to more sites besides HighTechVacations.net. 42 A THEFT IN THE NIGHT There are still more possibilities for her to explore if she wants to. For example, she noticed that when she booked a flight, a series of Web services were called: startTrans, holdSeat, checkMilesClub, debitACH, pushItinerary, pushConfirmEmail, and finally commitTrans. What happens if Eve calls these Web services out of order? Will she still get billed if she skips the debitACH function? Can she perform a Denial of Service attack by starting thousands of database transactions and never committing them? Can she use pushConfirmEmail to send large amounts of spam or maybe launch a phishing scheme? These are possibilities for another day; she already has all the passwords anyway. Better to sell some to spamming services and move on. What about that administration portal? Eve thinks about that half-completed Perl script she wrote to brute-force Web-based login forms. Maybe this is an excuse to finish that project. Eve looks at her watch. It’s almost 9 p.m. By the time she gets home, some of Eve’s business associates in the Ukraine should be just about getting in from a late night of clubbing. Eve smiles. She certainly has some data they might be interested in, and they always pay top dollar. It’s all a matter of negotiation. Eve powers down her ThinkPad, packs her backpack, and drops her coffee in the trash can by the door on her way out. She hasn’t even driven a mile before a new customer sits down at her table and pulls out a laptop. The unremarkable woman at the corner table is just a fading memory in the minds of the customers and coffee jockeys at Caribou. No one ever remembers Eve. And that’s the way she likes it. 43 This page intentionally left blank 3Web Attacks Myth: Ajax applications usually fall victim to new, Ajax-specific attack methods. While the unique architecture of Ajax applications does allow some interesting new attack possibilities, traditional Web security problems are still the primary sources of vulnerabilities or avenues of attack for Ajax applications. Hackers are able to employ proven methods and existing attack techniques to compromise Ajax applications. In fact, Ajax makes many existing Web security vulnerabilities more easily detectable, and therefore more dangerous. Enhanced security for Ajax applications requires a grasp of the fundamentals of existing Web application attack methods and the root vulnerabilities they seek to exploit. In this chapter, we examine some, but by no means all, of the most common Web application attacks. We describe, in detail, the methodologies used to perform the attacks and the potential impact a successful attack might have on your application and your users. THE BASIC ATTACK CATEGORIES Web application attacks typically fall into two high-level categories: resource enumeration and parameter manipulation. A third category encompasses cross-site request forgeries, phishing scams, and denial of service attacks. We will examine each category in detail. 45 CHAPTER 3 WEB ATTACKS RESOURCE ENUMERATION Put simply, resource enumeration is the act of guessing to find content that may be present on the server but is not publicly advertised. By this we mean content that exists on a Web server and can be retrieved if the user requests the correct URL, but that has no links to it anywhere in the Web application. This is commonly called unlinked content because you cannot get to it by following a hyperlink. As an example, consider a file called readme.txt in the directory myapp. There are no hyperlinks anywhere on the somesite.com Web site to readme.txt, but if a user requests the URL http://somesite.com/myapp/readme.txt, the user will receive the contents of readme.txt. The simplest form of resource enumeration attack is simply making educated guesses for commonly named files or directories. This is called blind resource enumeration because there was nothing on the site that led the attacker to try a particular filename or directory; he simply tries every commonly used filename or directory name to see if any of the requests return some content. Checking for readme.txt, as in the above example, is a good start. Many applications have some kind of information file, such as readme.txt, install.txt, whatsnew.txt, or faq.txt. Requesting this file in different directories on the application is also usually a good idea. Other common file names hackers guess for include: • test.txt • test.html • test.php • backup.zip • upload.zip • passwords.txt • users.txt Attackers will also try common directory names like: • admin • stats • test • upload • temp • include • logs 46 THE BASIC ATTACK CATEGORIES A complete list of files or directories attackers guess would be hundreds of lines long and is beyond the scope of this book. Open source Web application vulnerability scanners like Nikto (http://www.cirt.net/code/nikto.shtml) do contain such lists. Even without a full list of everything attackers try, hopefully you are seeing a pattern. An attacker is looking for things in the Web site’s directory that are not supposed to be there—and that the administrator forgot to remove. Some of these unlinked resources can contain especially damaging information. For example, a file like backup.zip might contain the entire contents of a Web site including the raw dynamic PHP, ASPX, or JSP files. This would reveal the source code for the entire application! A file like passwords.txt might contain sensitive account information. Never underestimate how much damage an unlinked resource can cause. The Web page test.html might contain links to an older, insecure part of the Web application. The directory /logs/ may reveal Web requests to a hidden administrative portal on the Web site. A readme.txt file might reveal versions of installed software or default passwords for a custom application. Figure 3-1 shows an attacker downloading an unlinked FTP log file, which reveals internal IP addresses and the drive and directory structure of the Web server’s file system. Figure 3-1 Accessing unlinked content by guessing for common filenames 47 CHAPTER 3 WEB ATTACKS Blind enumeration is effective because it preys upon the fact that Web developers tend to follow conventions, whether purposefully or unconsciously. As a whole, developers tend to do things the same as other developers. This is the reason developers use the variables foo and bar when they are in a rush. It’s why so many people have a test page somewhere in their application—and that test page is most likely called test. It’s why so many applications have an includes or scripts or data directory. Attackers can leverage these common conventions to make reasonable guesses about the name or location of unlinked content. Blind resource enumeration is purely a percentages game. A more advanced form of resource enumeration is knowledge-based resource enumeration. This form of resource enumeration still involves guessing for unlinked resources, but the attacker makes more educated guesses based on known Web pages or directories on the site. A good example of this type of resource enumeration is searching for backup files. Sure, an attacker might get lucky and find a file called backup.zip, but a more effective technique is to look for backed-up versions of known files. For example, let’s say the page checkout.php exists on an e-commerce site. An attacker would request files such as: • checkout.bak • checkout.old • checkout.tmp • checkout.php.old • checkout.php.2 • Copy of checkout.php If the Web site has not been configured to properly serve files that end in old or tmp it will not pass the file to a handler such as a PHP interpreter, and will simply serve the raw file contents. Figure 3-2 shows an attacker retrieving the complete source code for the page rootlogin.asp using knowledge-based resource enumeration. Besides trying to guess filenames, extensions, or directories, knowledge-based resource enumeration can be used with parameter values as well. Suppose a news site has a single page called Story.aspx, and every hyperlink to Story.aspx has a parameter named id in the query string of the URL. Enter an attacker who uses a Web crawler to catalog the entire Web site. She notices that the ID parameter is always a positive four digit number between 1000 and 2990. She also notices that while there are possible 1990 URLs to Story.aspx with the parameter id that could fall into this range, there are only around 1600 new stories. In other words, there are gaps in the range of story ids where an id value could exist, but for which there aren’t linked stories. This sounds suspiciously like 48 THE BASIC ATTACK CATEGORIES there is unlinked content on the Web server. The attacker writes a program to request all the story ids that fit in the range, but for which there is no hyperlink. Sure enough, the attacker finds 30 ids that, when requested, return a news story that isn’t publicly known. Perhaps these were shelved as bad stories, or were too risqué, or are news stories that haven’t yet been published. Figure 3-2 Using knowledge-based resource enumeration to discover backup versions of known files A real life example of this comes from a penetration test that we performed not long ago for a large publicly traded company. There was a section of the company’s Web site on which all the press releases were found. All the URLs were of the form http://somesite.com/press/YYYY/MM/DD/release.pdf, where YYYY was the four-digit year, MM was a two-digit month, and DD was a two-digit day. We began brute forcing all possible dates for the current year to find unlinked press releases. We got a response containing a press release that was dated four days in the future. It was a press release about the company’s quarterly earnings report, which wasn’t yet public and was not supposed to be released for four more days. Had we been criminals, we could have used this 49 CHAPTER 3 WEB ATTACKS knowledge to perform insider trading and make stock trades that would generate a substantial amount of money (the earnings statement was definitely unfavorable). Resource enumeration is a great technique that attackers use to find unlinked resources. You should think of hackers conducting resource enumeration as explorers in a dark cave. They can’t actually see any buried treasure, but as they feel their way around the cave, they just might stumble upon something. While developers are encouraged to back up their code and pepper it with comments, the archives should be stored securely offline. Keeping a trim and clean Web root will help keep those hackers probing in the dark. PARAMETER MANIPULATION Hackers commonly manipulate data sent between a browser and a Web application to make the application do something outside of its original design, often to the hacker’s advantage. This is known as Parameter Manipulation because the attack is manipulating the input of the application to make it behave differently. Parameter manipulation attacks are meant to hit edge cases in a program that the developer did not plan on and that cause the application to behave inappropriately. Consider a Web application in which people sign up to have coupons delivered to their home addresses. What happens if a hacker sends the value -1 for the ZIP code? Did the developer check if the ZIP code is in the correct format? Will -1 cause an exception to be thrown? Will an error message with a stack trace be returned? What if the hacker enters ~!@#$%^&*()_+ into the textbox for the state? The above examples are generic probes of unexpected characters designed to cause a failure and (hopefully) reveal important information in the error messages. While this is certainly effective, it really is just a more active way to gather information. The goal of most parameter manipulation attacks, however, is initiating actions—specifically actions the attacker wants to happen. Sure, an attacker can make a database query crash and reveal sensitive data, but can the attacker issue his own SQL commands to the database? Can the attacker get the Web application to read any file on the Web server? Parameter manipulation attacks seek to inject malicious code into the server logic, where the code is then executed or stored. To explain this concept a little more clearly, let’s look at a noncomputing real-world example. Imagine you have a well-meaning but clueless roommate making out a to-do list for the weekend. His list looks like this: 1. Pay bills 2. Walk the dog 3. Go to the grocery store for milk 50 THE BASIC ATTACK CATEGORIES He asks you if you want anything from the grocery store and hands you his list so that you can add your grocery items. With a mischievous grin, you take the list, add cookies to the shopping list, and then add a completely new fourth item: 1. Pay bills 2. Walk the dog 3. Go to the grocery store for milk and cookies 4. Wash roommate’s car You hand the list back and try to contain a laugh as he sits down at the table to begin paying bills. Later in the day, you sit down to watch the game and enjoy some wellearned milk and cookies while your roommate hoses off your car in the driveway. In this case, you have attacked your roommate (or at least taken advantage of his cluelessness) by “injecting” a command of your own choosing into his to-do list. He then processed that command just as if it were one he had written down himself. While your roommate was expecting you to provide only data (i.e., cookies), you instead provided both data and commands (cookies; 4. Wash roommate’s car). This is exactly the same methodology that parameter manipulation attacks on Web applications use. Where a Web application will expect a user to provide data, an attacker will provide both data and command code in order to try to get the server to execute that code. The canonical example of this type of attack is SQL Injection. SQL Injection SQL Injection is a parameter manipulation attack in which malicious SQL code is piggybacked onto SQL commands executed in the dynamic logic layer of a Web application. The most common target for this attack is a database query that executes in response to a search initiated by a user action. In our sample DVD store application (see Figure 3-3), each image of a DVD is actually a hyperlink to a product details page. The hyperlink contains the product ID of the selected DVD as a query parameter, so if a user clicked on the image of the Hackers DVD (which has a product ID of 1), the browser would request the page /product_detail.asp?id=1. The product details page would then query the database to retrieve reviews and other product information for the selected movie. 51 CHAPTER 3 WEB ATTACKS Figure 3-3 A database-driven DVD store The code that product_detail.asp executes looks like this: Dim selectedProduct ' set selectedProduct to the value of the "id" query parameter … ' create the SQL query command Dim selectQuery selectQuery = "SELECT product_description FROM tbl_Products " + "WHERE product_id = " + selectedProduct ' now execute the query … This looks very straightforward; experienced Web developers have probably seen code like this a hundred times. Assuming that the customer uses the application as intended by clicking the movie image links, the server will execute a SQL query as follows: SELECT product_description FROM tbl_Products WHERE product_id = 1 52 THE BASIC ATTACK CATEGORIES Again, this is very straightforward and will work as intended; the page code will retrieve and display the product description for the Hackers DVD (see Figure 3-4). Figure 3-4 The product details screen for Hackers Now let’s see what happens if we intentionally misuse the application. There is nothing to prevent us from browsing to product_detail.asp directly and entering any value we like for the id parameter. Let’s try /product_detail.asp?id=’ (see Figure 3-5). Well, this is certainly a change from the previous response! The database query failed—and threw back a very detailed error message to the user. We will get to the details of the error message in a minute, but first let’s figure out why the query failed. Because we sent the value ' for the product ID, the query that the server tried to execute looked like this: SELECT product_description FROM tbl_Products WHERE product_id = ' 53 CHAPTER 3 WEB ATTACKS Figure 3-5 The injection attack causes a detailed error to be displayed to the user. Unfortunately, this is not valid SQL because there are a mismatched number of apostrophes in the command. The command failed, and the error message bubbled all the way back up the call stack to be displayed to the user. At this point, we know that we have struck gold. We know that the back end database is a Microsoft SQL Server database, because the error message includes a reference to the ODBC SQL Server driver. Better still, we know that we can force the server to execute any SQL command we want by sending the command as the id parameter of the product_detail.asp page. One of our primary objectives, as we continue, is to extract as much data from the database as possible. The first step in this process is to find out exactly what tables are in the database. Because we know that the database is a SQL Server database, we know that the database contains a table called sysobjects. Any row in the sysobjects table with an xtype column value of ‘U’ contains information on a user-defined table. We can attempt to extract this information by injecting a UNION SELECT clause into the SQL query. Let’s make a new request to product_details.asp: /product_details.asp?id=1 UNION SELECT name FROM sysobjects WHERE xtype='U' 54 THE BASIC ATTACK CATEGORIES We get another error message from the server (see Figure 3-6), but this time it is bad news for us. It seems our injected UNION SELECT clause did not have exactly the same number of expressions (in this case, selected columns) as the original query. Let’s retry the request, but this time let’s add a second expression to our injection attack. It can be something meaningless like null; it doesn’t need to be an actual column in the table. The point is only to get the number of columns to match up. If we get the same response from the server, we must still have a mismatch in the count of expressions, and we simply keep adding more expressions until we get back a new error. Figure 3-6 The UNION SELECT injection failed because the number of columns did not match. At last—success! The page returns another error message, but this time the contents of the message, shown in Figure 3-7, reveal that tbl_Globals is one of the user-defined tables in the database. 55 CHAPTER 3 WEB ATTACKS Figure 3-7 The injection attack succeeds in pulling a table name from the database. We can now extract every table name, one at a time, by adding a filter to our injection clause. The next attack we send is: /product_details.asp?id=1 UNION SELECT name FROM sysobjects WHERE xtype='U' AND name > 'tbl_Globals' This methodology, then, is repeated until no more tables are retrieved. The same technique can now be used to extract the column names and the individual data elements from the discovered tables until, in the end, we have a complete dump of the database contents. Blind SQL Injection At this point, you’re probably thinking that an easy solution to SQL Injection would be simply to turn off the detailed error messages that get returned from the server. While this is an excellent idea (and we highly recommend doing so) it will not solve the 56 THE BASIC ATTACK CATEGORIES underlying problem, and the vulnerability will still be exploitable by using a variation of SQL Injection called blind SQL Injection. Blind SQL Injection works on the principle of injecting true/false expressions into the database query. For example, we, as attackers, might inject an always-true SQL statement, like AND 1=1, just to see what comes back from the server. If we can determine the difference between the server’s response to a true statement and the server’s response to a false statement, then we can ask the database yes-or-no questions and obtain information in that manner. The first step is to determine what a true response looks like. We send the following request: /product_details.asp?id=1 AND 1=1 Figure 3-8 shows the server’s response. Figure 3-8 The server’s response to the always-true statement 1=1 57 CHAPTER 3 WEB ATTACKS Now let’s see what an always-false response looks like. We send: /product_details.asp?id=1 AND 1=2 This time the server responds as illustrated in Figure 3-9. Figure 3-9 The server’s response to the always-false statement 1=2 We can see that the server has improved its security by returning a HTTP 500 error page instead of a detailed error listing. However, this will not stop us. All we wanted to see was the difference between a true response and a false response, and now we know that. So, now that we can ask the database any true/false question we want, what meaningful questions can we ask? Let’s start the same way we did before, by pulling the names of the user-defined tables from the sysobjects table. We can’t ask for the names directly, because such questions would not be true/false questions. We can, however, ask about individual characters of the response. The first question we ask is: Is the first character of the name of the first user-defined table an A? 58 THE BASIC ATTACK CATEGORIES /product_details.asp?id=1 AND ASCII(SUBSTRING(SELECT TOP 1 name FROM sysobjects WHERE xtype='U'),1,1)) = 65 If this injected query returns the true page, then we know the first character of the name of the first user-defined table is an A, and we can move on to the second character. If the server responds with the false page, we try B for the first character. We can proceed in this manner until we have found all of the characters of all of the user-defined tables. At that point, we can proceed to extract all the columns and data from those tables as well. If this sounds unbelievably tedious to you, that’s because it is unbelievably tedious. However, when intelligent, highly-motivated individuals like hackers are faced with tedious tasks, they often create tools to do the work for them. There are several automated blind SQL Injection tools freely available on the Internet, such as Absinthe. Absinthe can extract all the data from a vulnerable Web site in a matter of seconds. Other SQL Injection Attacks There are other uses (or abuses) of SQL Injection beyond pulling data from the database. SQL Injection is often used to bypass login forms by injecting an always-true statement into the authentication routine. The SQL query is intended to be executed as follows: SELECT * FROM Users WHERE username = username AND password = password But instead, the injected always-true statement makes the intended logic irrelevant: SELECT * FROM Users WHERE username = x AND password = x OR 1=1 Because OR 1=1 will always be true, this query will always return all the rows in the Users table, and the authentication code will assume the user is valid and grant access. The attacker is also not constrained to simply add UNION SELECT or WHERE clauses to the original command. She can also append entirely new commands. Some interesting possibilities include deleting database rows: SELECT * FROM Product WHERE productId = x; DELETE FROM Product Or inserting database rows: SELECT * FROM Product WHERE productId = x; INSERT INTO Users (username,password) VALUES ('msmith','Elvis') 59 CHAPTER 3 WEB ATTACKS Or dropping tables entirely: SELECT * FROM Product WHERE productId = x; DROP TABLE Product Finally, the attacker can attempt to execute any stored procedures that may be present in the database. SQL Server databases are created, by default, with many potentially dangerous stored procedures. Perhaps the worst offender is the procedure xp_cmdshell, which allows the caller to execute arbitrary Windows shell commands: SELECT * FROM Product WHERE productId = x; EXEC master.dbo.xp_cmdshell 'DEL c:\windows\*.*' XPath Injection XPath Injection is very similar to SQL Injection, except that its target is an XML document instead of a SQL database. If your application uses XPath or XQuery to pull data from an XML document, it may be vulnerable to an XPath Injection attack. The same principle of SQL Injection applies to XPath Injection: An attacker injects his own code into the query, and the server executes that code just as if it were part of the originally intended command. The only difference between the two is the command syntax required to exploit the vulnerability. Instead of tables and rows, XML documents store data in tree nodes. If our goal as attackers is to extract all of the data in the document, we have to find a way to break out of the intended node selection and select the root element. We can start by applying some of the same concepts of blind SQL Injection. We will make our attacks against a mirror of the DVD store from the last example that has been modified to use an XML document, instead of a SQL database, for its data store. As before, we ask the server an always-true question and an always-false question in order to determine the difference between the responses. /product_details.asp?id=1' AND '1'='1 You can see that the syntax is virtually identical to the SQL Injection attack. The only difference is that we had to wrap the values in apostrophes. The server responds as shown in Figure 3-10. 60 THE BASIC ATTACK CATEGORIES Figure 3-10 The server’s response to the always-true injected XPath query Now, let’s look at the always-false response (see Figure 3-11). /product_details.asp?id=1' AND '1'='2 We now have our baseline responses. Just as before, we can’t ask for the element names directly, but we can ask about the individual characters. The first question to ask is: Is the first character of the name of the first child node of the document an A? /product_details.asp?id=1' and substring(/descendant:: *[position()=1]/child::node()[position()=1],1,1)='A If you’re getting a sense of déjà vu, it is well deserved: This blind XPath Injection technique is virtually identical to blind SQL Injection. It is also just as tedious as blind SQL Injection. Currently, we do not know of any tools that automate an XPath Injection attack, but there is no technical reason it could not be done. It is probably just a matter 61 CHAPTER 3 WEB ATTACKS of time before some enterprising young hacker creates one. The bottom line is that you should never underestimate the resourcefulness or the determination of an attacker. Figure 3-11 The server’s response to the always-false injected XPath query Advanced Injection Techniques for Ajax In both the SQL Injection and XPath Injection examples given here, the server code was responsible for parsing the query response data and transforming it into HTML to be displayed to the user. Virtually all traditional Web applications work this way. However, Ajax applications can employ a different strategy. Because Ajax applications can make requests to the server for data fragments, it is possible to design an Ajax application in such a way that the server returns raw query results to the client. The client then parses the result data and transforms it into HTML. From a performance point of view, this is a good idea: Data transformation routines such as XSLT are computationally expensive, and it would be better to have the client pay that price. However, this methodology opens up a huge potential security hole. Because the server is returning raw query results to the client, it will be much easier for an 62 THE BASIC ATTACK CATEGORIES attacker to exploit any injection vulnerabilities in the query command logic. The attacker will no longer have to ask thousands of true/false questions; he can simply request the data and it will be given to him. In most cases the entire back end data store can be retrieved with one or two requests. Not only does this make life much easier for the attacker, it also dramatically improves his chances of success, because it’s much less likely that he will be stopped by any kind of intrusion detection system (IDS). This topic will be covered in detail in Chapter 6, “Transparency in Ajax Applications,” but for now we will whet your appetite with sample single-request attacks for XPath and SQL Injection, respectively: /product_details.asp?id=1' | /* /product_details.asp?id=1; SELECT * FROM sysobjects Command Execution In a command execution attack, an attacker attempts to piggyback her own operating system commands on top of input into the Web application. This attack is possible anytime a Web application passes raw, unvalidated user input as an argument to an external program or shell command. A decade ago, Web applications were much more primitive. Web applications regularly called out to other external programs running on the same server in order to take advantage of those programs’ existing functionality. This typically occurred through the Common Gateway Interface (CGI). The canonical example of command execution is the CGI program finger.cgi. Finger is a UNIX command that returns various bits of information about a user’s account on the server. Typically finger would return information on whether the user was logged in, the last time he checked his mail, his home directory, and other personal information. Finger.cgi was a CGI program that accepted a username in the query string, passed this to a command shell that executed the finger command with the user supplied input as a parameter, and then nicely formatted the results of finger into an HTML response. Figure 3-12 shows an example of the output of finger.cgi. To understand how command execution is possible, we need to look at the vulnerability in the actual Perl code of finger.cgi, which is shown below. $name = $ENV{'QUERY_STRING'}; $name = substr $name, 7; print "
"; print `/usr/bin/finger $name`; print "
"; 63 CHAPTER 3 WEB ATTACKS Figure 3-12 HTML-formatted output of the UNIX finger command using a CGI Web interface This Perl code simply extracts the name passed to finger.cgi from the query string and calls the finger program (/usr/bin/finger) passing the name as an argument. Finger.cgi itself is extremely simple. It delegates all the heavy lifting to the finger program, takes the output from /usr/bin/finger, and returns the results to the user formatted inside HTML PRE tags. Everything inside the grave accent marks (`) in Perl is passed to a command prompt and executed. So, if the user supplies the name root, the command that is executed is /usr/bin/finger root. What if the attacker tries something the programmer didn’t expect? What if the attacker supplies the name root;ls? In this instance, the shell executes the command /usr/bin/finger root;ls. The semicolon delimits UNIX commands; so, in fact, two commands will run—and both of their outputs will be returned to the user. In this case finger will run as expected and the ls command (similar to the Windows dir command, which shows the files in the current directory) will both execute. The attacker can see that her command executed because the output from the injected ls command is displayed inside the HTML alongside the normal finger response. Simply by appending a semicolon followed by a UNIX command, the attacker has gained the ability to execute arbitrary commands on the remote Web server. Finger.cgi is acting exactly like an SSH, 64 THE BASIC ATTACK CATEGORIES remote desktop, or telnet connection because it allows users to execute commands on a remote system. While Web applications have come a long way in 10 years and the finger vulnerability has been patched for some time, command execution vulnerabilities still exist and are especially common in home-grown applications. “Contact Us” or “Leave a Comment” Web pages like the example shown in Figure 3-13 are often vulnerable to command injection. These programs typically shell out to an external mail-sending program to do the heavy lifting of actually sending the email. Figure 3-13 Comment forms typically use some kind of external mail program to deliver the comments to the appropriate recipient. Command execution is extremely dangerous because it allows an attacker to remotely execute programs on a Web server. Specifically, if command execution is successful, attackers can get the Web application or Web server to run commands on their behalf. It’s commonly believed that the user account privileges of the Web server don’t allow access to important proprietary information, and thus, securing against command 65 CHAPTER 3 WEB ATTACKS execution vulnerabilities is not very important. This is simply false. Modern Web sites can touch nearly every major part of a business. The Web server’s user account has to have access to certain files or databases. Even if the Web server’s user account cannot directly access the database, the Web server has to be able to access the source code or programs that do connect to the database. Otherwise it couldn’t function. Once an attacker gains access, it is an easy step to dump more highly-privileged usernames, passwords, or database connection strings from these files using the Web server’s permissions. Because Web applications wield a great deal of power and have significant permissions, the ramifications of command execution injection prove serious and far-reaching. File Extraction/File Enumeration File extraction and file enumeration are parameter manipulation attack techniques where a hacker attempts to read the contents of files on the Web server. An example should help illustrate how this vulnerability occurs and is exploited. Consider a Web site http://somesite.com. On this site there is a single page called file.php. Every Web page on the Web site is served using file.php, with the specific file to use passed as a parameter in the URL’s query string. For example, the URL http://somesite.com/file.php?file=main.html serves the main page, and the URL http://somesite.com/file.php?file=faq.html serves the frequently asked questions page. The attacker hypothesizes that the source code for file.php looks something like the pseudocode listed below: $filename = filename in query string open $filename readInFile(); applyFormatting(); printFormattedFileToUser(); At this point, the attacker requests http://somesite.com/faq.html directly and notices that it looks very similar to http://somesite.com/file.php?file=faq.html, except there are some styling and formatting differences. This confirms to the attacker that the Web page file.php is simply reading in the contents of a file that was specified in the file parameter of the query string and applying some simple formatting before returning it to the user. Now imagine what might happen if the attacker attempts to break out of the list of allowed Web pages like main.html and faq.html. What if he requests the URL http://somesite.com/file.php?file=..\..\..\..\boot.ini? In this case, the attacker intends file.php to retrieve the contents of the file..\..\..\..\boot.ini. For those unfamiliar with the syntax, 66 THE BASIC ATTACK CATEGORIES when .. is used in a file path, it means to navigate to the parent of the current directory. On computers running Microsoft Windows and IIS, Web pages are stored in the directory C:\Inetpub\wwwroot\. This means that when file.php attempts to open the file ..\..\..\..\boot.ini, file.php is, in fact, attempting to open the file C:\Inetpub\ wwwroot\..\..\..\..\boot.ini, which is equivalent to C:\..\..\boot.ini, which is equivalent to C:\boot.ini. Boot.ini exists on all modern versions of Windows and contains information about the machine’s configuration. File.php would open C:\boot.ini, attempt to format it, and return the contents to the attacker. By using the .. sequence, an attacker can force the Web application to open any file on the Web server that the application has permissions to read. You should note that the attacker only needed to go “up” two directories (both wwwroot and Inetpub) to reach the location where boot.ini is stored. However, the attacker had no idea where exactly the Web root was stored on the Web server. For example, if the Web root was C:\Documents and Settings\Billy.Hoffman\My Documents\Web sites\wwwroot the attacker would have needed to set the file parameter ..\..\..\..\..\boot.ini to properly navigate all the way to C:\boot.ini. Luckily for the attacker, if they send more .. sequences than are needed, the operating system just ignores them, as was the case with our original example. You should also note that this attack is applicable not just to Windows but also Linux, Solaris, Mac OSX, and other operating systems. All of these operating systems have well-known files that are in fixed positions. An attacker can figure out what operating system is being used and try to retrieve the appropriate file, or simply try to retrieve them all, to see if the file extraction vulnerability is real or not. Cross-Site Scripting (XSS) Cross-Site Scripting (XSS) works similarly to SQL Injection, command execution, and all the other parameter manipulation attacks we’ve discussed in this chapter so far, but with a subtle twist. In all of the other attacks, the attacker’s goal was to get her injected code executed on a victim Web server. In an XSS attack, the attacker’s goal is to get her injected code executed on a victim Web client (i.e. another user’s Web browser). XSS vulnerabilities occur when unfiltered user input is displayed on a Web page. There are many common instances of this, including: • Search results. Searching for Hemingway in an online bookstore may direct the user to a result page that displays the text, Your search for “Hemingway” returned 82 results. • Wikis/social networks/message boards/forums. The primary purpose of these sites is to accept content from users and display it to other visitors. 67 CHAPTER 3 WEB ATTACKS • Personalization features. Say you set up an account with a Web site and give your first name as Ken. The next time you return to that site, the home page displays the text, Welcome back, Ken. Let’s take a closer look at the example bookstore mentioned above. As you can see in Figure 3-14, the user has searched the site for all books containing the term “Faulkner”. Figure 3-14 A normal search result from an online bookstore The HTML returned from the server looks like this: Search
Search for books: Your search for Faulkner returned 12 results. … Because the page is echoing back the user’s search term, it is possible that we, acting as attackers, might be able to inject our own HTML or JavaScript into the page. Just like an 68 THE BASIC ATTACK CATEGORIES attacker performing a SQL Injection attack attempts to insert SQL commands into the SQL query data, an attacker performing an XSS attack attempts to insert HTML or script into the HTML data. Let’s make another search, but this time let’s search for the term . Figure 3-15 The search page is vulnerable to a Cross-Site Scripting attack. Just as we suspected—the page rendered the injected HTML and JavaScript as given, and popped up an alert dialog. This is the HTML returned from the server: Search Search for books: Your search for returned 12 results. … 69 CHAPTER 3 WEB ATTACKS When a browser receives HTML from a Web server—or in the case of an Ajax or DHTML application, when the page DOM is modified to include new HTML—the browser is simply going to do its job and render that HTML. If the HTML contains script content, and the browser is configured to execute script, then the browser will execute that script. The browser has no way of knowing that the script code has been injected into the page by an attacker and was not part of the page contents intended by the programmer. Because the injection is used as an example of an XSS attack so frequently, some people mistakenly think that XSS is not a serious issue. “All you can do with Cross-Site Scripting is to pop up a dialog on the page,” they say. “Big deal!” Actually, XSS is a very big deal, but not because it can pop up alerts. One of the most common ways to exploit an XSS vulnerability is to steal victims’ cookies. Let’s attack the search page again, this time with the search term: When the browser renders this content, it takes the contents of the current document cookie and sends it off to evilsite.com, blissfully unaware that it has just doomed its user. Because session IDs and authentication tokens are commonly stored in cookies, a successful cookie theft could allow an attacker to effectively impersonate the victim on the vulnerable site. There are other interesting possibilities for the attacker. Because XSS can be used to inject HTML as well as JavaScript, it might be possible for an attacker to add a new login form to the page, one that forwards the credentials back to him. It might be possible to manipulate the stylesheet of the page to move or hide page elements. Consider a bank application that allows the user to transfer funds between two accounts. If an attacker could manipulate the page to switch the target and destination account fields, he could certainly cause some trouble for the bank’s users. In fact, there are an almost infinite number of possibilities for exploiting XSS. XSS exploits have been written to perform port scans of the victim’s machine and to create automatic vulnerability detection scanners that transmit their findings back to the attacker. The Samy Web worm that took down MySpace used XSS to execute and propagate its payload. An attacker is limited only by the capabilities of HTML and JavaScript. Because XMLHttpRequest can be accessed through JavaScript, it is even possible to inject a complete Ajax application into a vulnerable Web site—one that could make a silent request or even a silent series of requests. The potential is staggering. Clearly, XSS can do much more than just pop up alert dialogs. 70 THE BASIC ATTACK CATEGORIES While we’ve shown that it is possible for an attacker to do very nasty things with XSS, we haven’t yet shown how it’s possible for him to hurt anyone but himself. After all, it was the attacker’s own browser that rendered the results of his script injection. For XSS to be a real threat, we need a way to target other users. There are two common techniques that attackers use to accomplish this. The first method (known as reflected XSS) is to write the injected content into a URL query parameter and then trick a user into requesting that URL. For our bookstore example, the URL might be http://bookstore.com/search.aspx?searchTerm= . Getting a victim to follow this link usually involves some social engineering— psychological trickery—on the attacker’s part. One way to accomplish this is to send an email to the potential victim with a message along the lines of “Click this link to claim your free prize!” Of course, the link the user follows does not actually earn her a free prize, but instead makes her a victim of identity theft. This attack can be especially effective when used in a mass spam email. The second method that attackers use to target victims is to actually store the malicious script in the vulnerable page. With this method, all viewers of that page would be affected. This is possible whenever a vulnerable page is designed to accept, store, and display user input. A wiki is a good example of this type of page, as is a blog on which readers can post their own comments about the article. This method of XSS (known as stored XSS) is more dangerous than reflected XSS, because it doesn’t require any social engineering. There is no trick that the victim has to fall for; she just has to browse to a vulnerable site. There is actually a third type of XSS known as DOM-based or local XSS. DOM-based XSS is exploited in the same way as reflected XSS: An attacker crafts a malicious URL and tricks a victim into following the link. However, DOM-based XSS differs from other methods of XSS in that the existing client-side script of the page executes the XSS payload; the server itself does not actually return the payload embedded in the page. To demonstrate this type of an attack, consider the following HTML: Welcome back, … In normal circumstances, the value of the username query parameter would be displayed in a friendly welcome message. However, if a parameter contained JavaScript code, that code would be written into the page DOM and executed. Session Hijacking Because HTTP is a stateless protocol, Web applications often identify users with a session ID so that the applications can identify users across multiple request/response transactions. In a session hijacking attack, hackers will guess or steal another user’s active session ID and use the information to impersonate the victim and commit fraud and other malicious activity. The deli counter provides a real-world example of session hijacking. During the busy lunch rush, the deli will take customers’ orders and payments and then give them a numbered ticket to redeem their order. Suppose a malicious attacker were to duplicate one of the numbered tickets. He could then go up to the counter, present the forged ticket in place of the legitimate deli customer, and receive the lunch order without paying for it— in effect hijacking the customer’s lunch. The deli counter example proves useful because the numbered ticket serves the same purpose as the session ID. Once the ticket or session ID has been assigned, no more questions are asked regarding identity, method of payment, and so on. Guessing or stealing someone’s session ID enables attackers to commit identity fraud and theft. Session hijacking takes several forms, including: • Brute forcing. Hackers will attempt to guess the format for session IDs simply by testing different permutations repeatedly until they find one that works. • Fuzzing. If attackers suspect session IDs fall within certain numerical values, they will test number ranges until they find a successful match. This approach is essentially an “educated” form of brute force. • Listening to traffic. Some hackers will review transcripts of the requests and responses between a Web application server and a user to see if they can identify the session ID. The growth of wireless networks and access points has made this form of eavesdropping much easier and more commonplace. 72 THE BASIC ATTACK CATEGORIES Authorization bypass is another form of session hijacking. For example, take the sample Web site Simon’s Sprockets. This site stores a persistent cookie on the client machine to identify returning users. After a user has created an account on the site, they see a friendly Welcome Back message whenever they visit (see Figure 3-16). Returning users can also see a list of orders they have made in the past and place new orders quickly, without re-entering their shipping and billing information. Figure 3-16 Simon’s Sprockets displays a friendly message to returning users. This sounds like a real convenience for Simon’s users. It also sounds like it could be a huge security hole. To find out if this is indeed the case, we first need to see exactly what data Simon is storing in the users’ cookies. Depending on your browser and operating system, you may be able to open the file containing the cookie and directly view the contents. There are also some browser plug-in utilities that will allow you to view the contents. Using any of these methods, we can see that the cookie contains the following information: FirstName=Annette LastName=Strean AccountNumber=3295 MemberSince=07-30-2006 LastVisit=01-05-2007 Most of this data seems harmless enough, but the AccountNumber field should raise a red flag. This is almost certainly the field that the application is using to uniquely identify the user. It might be possible to access another user’s account by changing the value of the cookie. Furthermore, the value of the field appears to be just a relatively small integer value. It seems likely that Simon’s Sprockets is using an incrementing integer for its account number; that is, if Annette has account number 3295, then the next person to open an account after her would be number 3296. Let’s test our theory by changing the cookie value and re-requesting the page. 73 CHAPTER 3 WEB ATTACKS Figure 3-17 Simon’s Sprockets gives an attacker easy access to other users’ accounts. Our theory appears to be confirmed. With a simple change to an easily-guessed cookie value, we can now read the order history of our unfortunate victim, Tony. Even worse, because returning users can place new orders without having to re-enter their billing information, we can buy sprockets for ourselves with Tony’s money. In one real-world example of this vulnerability, a mail order drug company’s Web site used sequential identifiers stored in cookies to remember repeat customers. This allowed hackers to easily view other customers’ names, addresses, and order histories—a serious privacy violation. An incrementing integer is not the only poor choice for a unique identifier. An email address is equally dangerous, but in a subtly different way. When an incrementing integer is used, an attacker will be able to access the accounts of many random strangers. With an email address, an attacker can actually target specific people. This might help if some social engineering is involved in the exploit. For example, if the Web site did not allow the user to change her shipping address without reauthenticating, an attacker might time the attack so that the shipment would arrive while his known target is away on vacation. The best choice for a unique identifier is a large, randomly-generated number like a Universally Unique Identifier (UUID). A UUID is a 16-byte integer, which allows for approximately 3.4 x 1038 unique values. There are more possible combinations of UUIDs than there are atoms in your body. In fact, there are way more! We assume that an average human has a mass of 70 kilograms, of which 65% is oxygen, 19% carbon, 10% hydrogen, and 3% nitrogen. After consulting the periodic table of elements, we can calculate that an average person contains 7 x 1027 atoms. That’s still a billion times less than the number of possible UUIDs. The odds of guessing a randomly generated UUID are slim. Also, there is no way to obtain a particular individual’s UUID from an outside source, unlike the relative availability of email addresses (or social security numbers, or driver’s license numbers). 74 OTHER ATTACKS OTHER ATTACKS We’ve already looked at resource enumeration and parameter manipulation attacks and how they exploit vulnerabilities of Web sites, and hence Ajax applications. In this section we look at three additional attacks that don’t fall so neatly into a category: • Cross-Site Request Forgery • Phishing • Denial of Service CROSS-SITE REQUEST FORGERY (CSRF) Cross-Site Request Forgery (CSRF) is a form of attack in which the victim’s browser is directed to make fraudulent requests to a Web page using the victim’s credentials. In some ways, CSRF is similar to XSS: The attack is made on the user by manipulating his Web browser to perform a malicious action. However, the difference between the two is an issue of misplaced trust. XSS attacks take advantage of the user’s trust of the Web site he is visiting—the confidence that all of the content on that site was intentionally placed there by the site creators and is benign. On the other hand, CSRF attacks take advantage of the Web site’s trust of its users, the confidence that all of the requests the site receives are intentionally and explicitly sent by the legitimate site users. Consider a bank Web site that allows its users to make account transfers. Once a user has logged in and received an authentication cookie, he needs only to request the URL http://www.bank.com/manageaccount.php?transferTo=1234&amount=1000 in order to transfer $1000 to account number 1234. The entire security of this design rests on the belief that a request to manageaccount.php containing a valid authentication cookie must have been explicitly made by the legitimate user. However, this trust is easily exploited. If an attacker can trick an already-authenticated user into visiting a malicious page that contains an image link like , the user’s browser will automatically request that URL, thus making an account transfer without the user’s knowledge or consent. There are many other methods besides image links that the attacker can use for a CSRF attack. Some of these include This attack method could be used with any HTML tag that includes an attribute with a URL, such as: or: Once again, our validation has proved to be less than valid. Certainly, we could find a way to modify our search condition so that it flagged the presence of javascript: in the request, but then some hacker would find some other method of bypassing the blacklist. Perhaps a URL-encoded request such as %3Cscript%3E would execute the attack and bypass the filter. An attacker could use which does not even contain the word “script.” We could keep playing this back-andforth ping-pong game with the hacker forever. We patch a hole, he finds a new one. We patch that hole, he finds another new one. This is the fundamental flaw with blacklist validation. Blacklisting is only effective at blocking the known threats of today. It really makes no effort to anticipate any possible new threats (or 0-day attacks) of tomorrow (see Figure 4-6). Blacklist validation filter User does not. By telling the filter what input is valid, as opposed to what input is invalid, we can block virtually every kind of command injection attack in one fell swoop. The only caveat to this is that you must be very exact when describing the valid format to the whitelist filter. Will Jeans Kevin Katie Swimsuit Suit and tie Brian Figure 4-8 Will White(list) bouncer only allows appropriately dressed customers into the club. This process can be trickier than it initially seems. Let’s continue the preceding example and come up with an appropriate whitelist validation pattern for an email address. We know that all email addresses must contain an @ symbol, so we could just check for that, but this would also allow values like: • jason@simonssprockets.foobar (invalid domain name) • ryan!@$imon$$procket$.com (includes illegal punctuation) • jeff@pm@simonssprockets.com (multiple @ symbols) • #e23^5Jlp,+@9Qz!w?F (just random garbage text) 107 CHAPTER 4 AJAX ATTACK SURFACE We need to refine the pattern to remove some of these invalid cases. Let’s say that our value must start with alphanumeric text, then include exactly one @ symbol, then more alphanumeric text, and finally end with a valid top level domain like .com or .net. This rule solves all four of the earlier problem examples, but creates new problems because we will now block valid email addresses like these: • jason.smith@simonssprockets.com (includes period in the name field) • ryan@simons-sprockets.com (includes dash in the domain field) Being overly restrictive with whitelist filters is just as bad as being overly permissive. The overly restrictive pattern is better from an application security perspective—it’s less likely that an attacker will be able to find a flaw in such a filter—but it is much worse from a usability perspective. If a legitimate user’s real email address is rejected, that user probably won’t be able to use the site and will just take his business elsewhere. After some trial and error, we arrive at this rule for email addresses: • The name portion of the address must contain alphanumeric characters and option- ally can contain dashes or periods. Any dash or period must be followed by an alphanumeric character. • An @ symbol must follow the name portion. • The domain portion of the address must follow the @ symbol. This section must contain at least one, but no more than three, blocks of text that contain alphanumeric characters and optional dashes and end with a period. Any dash must be followed by an alphanumeric character. • The address must end with one of the valid top level domains, such as .com, .net, or .org. Whew! This turned out to be a pretty complicated rule for something as seemingly simple as an email address3. We’re not going to be able to validate input against this rule with basic string comparison functions like strstr. We’re going to need some bigger guns for a rule like this, and luckily we have some heavy artillery in the form of regular expressions. 3 RFC822 and others provides more detailed information on what characters are allowed in different parts of an email address 108 PROPER INPUT VALIDATION REGULAR EXPRESSIONS Regular expressions (also commonly called regexes or RegExs) are essentially a descriptive language used to determine whether a given input string matches a particular format. For example, we could check whether a string contained only numbers; or whether it contained only numbers and letters; or whether it contained exactly three numbers, then a period, then one to three letters. Almost any format rule, no matter how complex, can be represented as a regular expression. Regex is a perfect tool for input validation. A complete discussion of regular expression syntax could (and does) fill a whole book in itself, and any attempt we could make here would be inadequate. ADDITIONAL THOUGHTS ON INPUT VALIDATION There are a few more issues that you should take into consideration when validating user input. First, we should not only validate the input for an appropriate format, but also for an appropriate length. While one thousand as followed by @i-hacked-you.com may follow our format rules perfectly, the sheer size of this input indicates that it is not a valid value. A submitted value like this is probably an attempt to probe for a potential buffer overflow vulnerability in the application. Whether or not the site is vulnerable to such an attack, you should not just accept arbitrarily large input from the user. Always specify a maximum (and, if appropriate, a minimum) length for each input. This rule can be enforced through a regular expression as well, or simply checked with the appropriate string-length function for your language of choice. There are also situations where the input validation rule, as dictated by the business logic requirements of the application, may allow some attacks to get through. For example, let’s say that our example wiki site allowed users to submit article updates that contain HTML. If we create a whitelist filter for this input that allows all valid HTML, we would also be allowing Cross-Site Scripting attacks to pass through. In this case, we would strongly recommend only allowing a small, safe subset of the complete HTML specification. An even better solution would be to define a new metalanguage for markup, like using double sets of square brackets to indicate hyperlinks. Mediawiki, which powers Wikipedia (www.wikipedia.org), uses this strategy with excellent results. APOSTROPHES One question that often comes up is the question of apostrophes. It is often desirable to allow users to enter apostrophes in name or street address values. But, if we allow apostrophes in users’ input, how can we prevent injection attacks? 109 CHAPTER 4 AJAX ATTACK SURFACE The solution is to continue to refine the whitelist pattern. O'Brien may be a valid value for a user’s last name, but ' SELECT * FROM tblCreditCards is probably not. Consider limiting the number of words (or in terms of regular expressions, the number of groups of alphanumeric characters delimited by whitespace characters). Consider limiting the number of apostrophes allowed; it is unlikely that any user would have more than one apostrophe in her name. As an extra protective measure, it can be worthwhile to employ not only a whitelist filter, but also a blacklist filter when validating input. We did say that blacklist filters are inadequate, and this is true, but that does not imply that they are not useful. You should not rely solely on a blacklist to filter input; but a blacklist used in combination with a whitelist can be very powerful. Use the whitelist to ensure that the input matches your designated format, and use the blacklist to exclude additional known problems. Returning to our Club Cheetah metaphor, we might keep Will White on to ensure that all patrons are appropriately dressed, but we might also rehire Biff Black to keep out known troublemakers, regardless of whether or not they’re wearing a suit and tie (see Figure 4-9). Kevin Jeans Known troublemakers: Will Biff Erik Brian Kim Brian Suit and tie Cocktail dress Tracy Figure 4-9 Employing both Will White(list) and Biff Black(list) gives maximum security. 110 VALIDATING RICH USER INPUT Finally, always be sure to perform validation not only on the client side, but also the server side. As we’ve said before, any code that executes on the client side is outside the realm of control of the application programmers. A user can choose to skip execution of some or all of the client-side code through judicious use of script debuggers or HTTP proxies. If your application only performs validation through client-side JavaScript, hackers will be able to completely bypass your filters and attack you any way they want to. VALIDATING RICH USER INPUT By this point, we have thoroughly discussed proper input validation to ensure that usersupplied data is in the proper format and value range. However, things become much more complicated when validating rich input like RSS feeds, JavaScript widgets, or HTML. After all, a simple regular expression like /^(\d{5}-\d{4})|(\d{5})$/ will validate a U.S. ZIP code, but there isn’t a whitelist regular expression to match safe HTML. The process is especially difficult for mashups and aggregate sites, because they typically consume large amounts of rich content like news feeds, Flash games, JavaScript widgets, and Cascading Style Sheets—all from multiple sources. Validating rich input typically involves two steps. The first step is to confirm that the rich input is in the correct structure. Once you have confirmed this, the next step is to confirm that the data inside of this structure is legitimate. With malformed structure, rich inputs can cause Denial of Service attacks or buffer overflows just as discussed with relation to uploaded files. Even if the structure is valid (for example, an RSS feed is composed of well-formed XML), the contents of that structure could be malicious. For example, the RSS feed could contain JavaScript used to perform massive Cross-Site Scripting attacks.4 In Ajax applications, the most common types of rich input are markup languages and JavaScript code. VALIDATING MARKUP LANGUAGES We will use RSS feeds as a case study to discuss how to properly validate various types of text markup such as HTML or XML. RSS feeds are input, and just like any other kind of input they must be validated. Figure 4-10 summarizes the approach developers should take when validating an RSS feed from an unknown source. First, validate the structure of the input. If any attributes of tags are unknown or out of place, they should 4 Security researcher Robert Auger gave a well-received and comprehensive presentation at Black Hat 2006 about using RSS as a vehicle for injecting malicious content. 111 CHAPTER 4 AJAX ATTACK SURFACE be discarded. Once the structure has been confirmed, we examine the content inside the structure and validate it with whitelisting, in much the same way we validate simple data like telephone numbers. The first step is to validate the structure of the RSS feed. RSS feeds are XML documents. Specifically, RSS feeds have a particular structure that defines which nodes or attributes are required; which nodes or attributes are optional; which nodes can be nested inside of other nodes; and so on. For example, according to the RSS 2.0 standard, the root tag must be , and that tag must have an XML node attribute specifying the version.5 There can only be one tag inside of the tag, and tags cannot be nested inside one another. The full RSS standard is beyond the scope of this book. Developers should use an XML parser when retrieving RSS feeds to confirm that the RSS feed is of the appropriate structure. Whitelisting should be used when validating the structure. For example, the tag is currently the only valid child tag for the tag. When walking child nodes of , if the validation routine comes across any node that is not a node, it should discard that unknown node and all of its children. RSS ? Validate XML Structure using whitelisting Whitelist input validation of individual data types Apply appropriate outbound filtering HTML Figure 4-10 When validating rich input like an RSS feed from an unknown source, developers should validate the rich input’s structure before performing validation on each element of the structure. Another, simpler alternative is to use a validating XML parser, if one is available, for the server-side programming language being used. Validating XML parsers will automatically compare the XML document in question against a given XML schema and determine whether the document is valid. Once we have validated the RSS feed’s XML structure, we turn to validating the individual items. We will focus on just a few parts of the tag of the RSS feed, but this approach should be applied to all elements of the feed. Table 4-2 contains information about different data elements inside of the tag for an RSS feed. We can see immediately that some of these elements can be validated easily. For example, the link element should only contain a hyperlink. We should ignore or discard anything that is not a valid URL. However, it is easy to be too loose with our whitelist input validation expression. In this situation, not only should the link element contain a 5 http://cyber.law.harvard.edu/rss/rss.html 112 VALIDATING RICH USER INPUT hyperlink, but it should only contain certain types of hyperlinks. URLs with schemas like javascript:, vbscript:, data:, ssh:, telnet:, mailto:, and others should not be allowed. Do not fall into the trap of using a blacklist here. Instead, you should whitelist the schemas to allow. A good rule of thumb is to allow only http:, https:, and ftp:. Table 4-2 Field names and data types for RSS items Field name Description title Title of item link URL of item description Item synopsis author Email address of author pubdata Date item was published Assumed Data Plain Text Hyperlink Rich Text Plain Text Date? Plain Text? While the steps used to validate an input for hyperlinks are rather straightforward, other elements are not so clear. In many ways this makes validating RSS feeds a good case study in applying input validation when a standard is vague or ambiguous. For example, in the standard, the author element is defined as “Email address of the author of the item.” However, the in the example RSS feed, the author element is given the value lawyer@boyer.net (Lawyer Boyer). Technically, this is not a valid email address. Can the description field contain HTML tags? Which ones? And what date format should the pubdata use? Whenever a specification is vague, it is better to err on the side of caution. Perhaps it makes sense for your application to strip any HTML tags found in the description field and require that the pubdate field only contains alphanumeric characters, dashes, or commas. VALIDATING BINARY FILES This same methodology is applicable to binary data as well. For example, GIF files have a well-known structure. The items inside of the GIF structure are well-known as well. Developers should start by ensuring that the necessary items for a valid GIF file are present (such as the header, palette data, and graphics data). If any other unrecognized structures exist (such as comments, animation information, etc.), or if duplicates of required structures exist, these should be discarded. Another suitable choice would be to discard the entire file and return an error. 113 CHAPTER 4 AJAX ATTACK SURFACE Once we have validated that the necessary structure exists, we validate the data in the structure. This is essentially a series of whitelist input validation tests for data type and range. We treat these exactly like we treat other simple input validation issues like ZIP code validation. With GIF files we would validate that the colors-per-frame value is an unsigned 8 bit integer, that the length and width of the image are unsigned 16 bit integers, and so forth. VALIDATING JAVASCRIPT SOURCE CODE Validating JavaScript is extremely difficult. While it is trivial to validate its structure— simply check that the code is syntactically correct—validating the content is another matter. Validating the content of a block of JavaScript code means that we need to ensure the code does not perform a malicious action. In this section we answer common questions about how to accomplish this. Is this idea even feasible? How easy is it to perform analysis, either manual or automated, on a piece of arbitrary JavaScript code to determine if the JavaScript code is malicious or not? To scope the problem of detecting malicious JavaScript, it is helpful to examine some of the characteristics of malicious JavaScript code. Typically, malicious JavaScript does some combination of the following: • Accessing and manipulating the DOM • Hooking user events such as OnMouseOver and OnKeyDown • Hooking browser events such as OnLoad and OnFocus • Extending or modifying native JavaScript objects • Making HTTP connections to offsite domains • Making HTTP connection to the current domain Unfortunately, these malicious behaviors are exactly the same types of tasks that legitimate JavaScript performs! Normal JavaScript manipulates the DOM for DHTML effects. It hooks user and browser events to respond to various actions. Normal JavaScript modifies and extends native objects for many reasons. It extends native objects to provide commonality between different browsers, such as adding the push function to Array objects. Microsoft’s ASP.NET AJAX extends objects like Array and String so their functions and properties match those offered by the equivalent .NET classes. The Prototype framework also extends native objects to add functionality. Normal JavaScript makes use of a variety of methods to send HTTP requests. Image preloading, Web analytics code, unique visitor tasking, online advertising systems, XMLHttpRequests, and hidden iframes are legitimate scenarios where JavaScript code sends HTTP requests to domains all over 114 VALIDATING RICH USER INPUT the Internet. We cannot conclusively determine if JavaScript code is malicious based entirely on what functions and features the code uses. Instead, we need to examine the context in which these features are used. Is the function handling the onkeyevent recording a user’s keystrokes or simply keeping a current letter count for a text area in a form? Let’s assume that a developer manually examines the JavaScript source code and ensures that it only accesses appropriate DOM objects, doesn’t hook any events, and only requests static images from approved domains. Can the developer now stamp a “safe” seal of approval on the code knowing that they checked everything? The answer is no. It’s possible that the JavaScript code does more than the source code is letting on. JavaScript is a highly dynamic language that can actually modify itself while it is running. Virtually all nonnative functions can be overridden with new versions. JavaScript even allows socalled dynamic code execution, where JavaScript source code stored inside of a string can be passed to the interpreter for execution. The JavaScript could generate this code dynamically or even fetch it from a third-party source. To ensure that a block of JavaScript code is safe, developers would have to find any strings containing JavaScript and check to see whether they are ever executed. But is this even a viable strategy? The real danger with dynamic code execution is that the JavaScript source code is stored in a string. How this string is assembled is left entirely up to the developer. Attackers almost always obfuscate or encrypt the string to prevent someone from noticing extra JavaScript statements. This normally involves start blocks of numbers or gibberish that are stored in a string and decrypted. These are fairly easy to spot. However, consider the following encryption6 and decryption methods. function dehydrate(s) { var r = new Array(); for(var i=0; i < s.length; i++) { for(var j=6; j >=0; j--) { if(s.charCodeAt(i) & (Math.pow(2,j))) { r.push(' '); } else { r.push('\t'); } } } r.push('\n'); return r.join(''); } 6 Malicious JavaScript already contains the encrypted dynamic code and usually doesn’t include the encryption function. We include it here for clarity. 115 CHAPTER 4 AJAX ATTACK SURFACE function hydrate(s) { var r = new Array(); var curr = 0; while(s.charAt(curr) != '\n') { var tmp = 0; for(var i=6; i>=0; i--) { if(s.charAt(curr) == ' ') { tmp = tmp | (Math.pow(2,i)); } curr++; } r.push(String.fromCharCode(tmp)); } return r.join(''); } In the preceding code, the dehydrate function converts a string of characters into a string of whitespace characters. These whitespace characters actually represent the bit stream for the characters in the original string. A space represents a one; a tab represents a zero; and a new line character terminates the bitstream. A single character in the original string is stored as seven whitespace characters, each representing one of the lower seven bits of the original character. We only need to store the lower seven bits of a character, because all of JavaScript’s keywords and language symbols can be represented in 7-bit ASCII. The hydrate function takes the bitstream and converts it back into a string. For example, the code string alert(7) is converted into a string of 57 characters (8 × 7 bits per character + 1 character for the new line to signify the stop of the bit stream). The resulting string of whitespace begins with space, space, tab, tab, tab, tab, space, which represents the bitstream 1100001 = 97, which is the ASCII code for a lowercase letter a. The 7-character whitespace representation for each letter follows inside the dehydrated string. Web browsers ignore whitespace, so any whitespace-encoded data will not get modified or damaged by the Web browser. An attacker could dehydrate a string of malicious code into whitespace and include it inside the code of the dehydrate function itself! The following code illustrates this approach. function hydrate() { //startevil //endevil 116 VALIDATING RICH USER INPUT //grab the entire current HTML document var html = document.body.innerHTML; //find our unique comments var start = html.indexOf("//star" + "tevil"); var end = html.indexOf("//end" + "evil"); //extract out all the whitespace between unique comments var code = html.substring(start+12, end); ... //rest of hydrate function here The third line of the code block appears empty. However, this is actually the single line containing our encrypted bitstream represented as whitespace. This whitespace is bracketed by two comments that contain a unique string. In this example we used startevil and endevil, but any unique string could be used. The whitespace bitstream could even be inserted into a comment block with legitimate comments describing code features to further hide it. Our JavaScript code then grabs a string containing the entire HTML of the current document, including the current running block of JavaScript. Next the code searches for the two unique comments and extracts the whitespace containing the bitstream from between them. This code would then proceed with the rest of the hydrate function to reconstruct the original malicious code string. Whitespace encryption is a very effective way to hide malicious JavaScript in plain sight. Because an attacker has virtually an unlimited number of different ways to encrypt and hide malicious code strings, perhaps developers could focus on trying to detect the calls to execute the JavaScript. The most common mechanism for evaluating strings containing JavaScript code is the eval function. In this context, let’s see how a developer might detect whether arbitrary JavaScript code is using eval to execute hidden or obfuscated source code. At first glance, it seems that a simple regular expression like /eval\s\(/ig will do the trick. Unfortunately, this is not the case. First of all, eval is a function of the window object. It can be referenced as window.eval or eval. Secondly, JavaScript’s array notation can also be used to access eval using window['eval']. More odds stack against a developer trying to craft a regular expression blacklist for eval. As of JavaScript 1.5, all functions themselves have two functions called apply and call. These allow developers to invoke a function and pass arguments to it without using the traditional func(args) format. These functions can also be called using JavaScript’s array notation. The following code shows 12 distinct ways to invoke the eval function, all of which will bypass our regular expression for the sequence eval(. A thirteenth example uses a combination of these approaches for maximum filter evasion. All 13 examples execute on the 4 major Web browsers for Windows at the time of publication (Internet Explorer 7, Firefox, Opera 9.21, and Safari 3.0.2). 117 CHAPTER 4 AJAX ATTACK SURFACE //function to generate malicious string of JavaScript code function evalCode(x) { return "alert('" + x + "')"; } //using call eval.call(window, evalCode(1)); eval['call'](window, evalCode(2)); //using apply eval.apply(window, [evalCode(3)]); eval["apply"](window, [evalCode(4)]); //window prefix, using call window.eval.call(window, evalCode(5)); window.eval['call'](window, evalCode(6)); window['eval'].call(window, evalCode(7)); window['eval']['call'](window, evalCode(8)); //window prefix, using apply window.eval.apply(window, [evalCode(9)]); window.eval['apply'](window, [evalCode(10)]); window['eval'].apply(window, [evalCode(11)]); window['eval']['apply'](window, [evalCode(12)]); //object aliasing to avoid signatures var x = 'eval'; var y = window; y[x]['ca' + String.fromCharCode(108, 108)](this, evalCode(13)); Array notation is especially powerful because it allows an attacker to refer to eval, call, or apply using strings. These strings can be obfuscated and encrypted in various ways. In the above code, Example 13 assembles the string call on the fly. Example 13 also uses object aliasing to remove the string window from the attack. The window object is the global scope object for JavaScript in a browser and references to it can often be replaced with this. Examples 1 through 12 show that there are no easy regular expressions to use blacklisting to detect calls to eval, while Example 13 illustrates that it is impossible to create a regular expression to detect the use of eval. To further drive nails into the coffin of using regular expressions to detect dynamic code execution, eval is not the only way JavaScript will execute code stored inside of a string. It is simply the most common and well-known method of dynamic code execution. The following code shows six more vectors for executing dynamically generated 118 VALIDATING RICH USER INPUT JavaScript code.7 Even worse, all the obfuscation mechanisms, object aliasing, and use of call and apply from our previous example are applicable for the window.location, document.write, and window.execScript vectors. There are further variations on each attack vector. For example, document.write could be used to write out a . var evilCode = "alert('evil');"; window.location.replace("javascript:" + evilCode); setTimeout(evilCode, 10); setInterval(evilCode, 500); new Function(evilCode)(); document.write(" While we’re not willing to say that it is impossible to view the value of a local variable from a separate function outside its scope using XSS, there are currently no known ways to accomplish this. Such an attack would certainly be orders of magnitude more difficult than fetching the global variable value. Only JavaScript variables declared inside a function with the keyword var are declared as locally scoped variables. All implicitly declared variables will be declared in the global scope. So, in our earlier example, when we inadvertently declared a new variable interetsRate, we actually declared that variable at global scope and not local scope. If 134 ASYNCHRONICITY the application is vulnerable to XSS, this value can be stolen easily. Other unpleasant scenarios might include forgetting whether the variable is named password, passwd, pass, or pwd and accidentally declaring a new global variable to hold this sensitive data. SECURITY NOTE To minimize the exposure of variable values to other scripts, developers should use the most restrictive scoping possible for their variables. If a variable is only used locally, developers must declare the variable using the var keyword before using it. Developers should minimize the number of global variables they use. This also prevents so-called variable and function clobbering, which will be discussed in Chapter 7, “Hijacking Ajax Applications.” ASYNCHRONICITY Often, the most useful features of a technology can also be its biggest security vulnerabilities. This is certainly true with Ajax. The asynchronous nature of Ajax can open the door to many elusive security defects. An application that processes data asynchronously uses multiple threads of execution: At least one thread is used to perform the processing in the background, while other threads continue to handle user input and listen for the background processing to complete. It can be difficult to coordinate multiple threads correctly, and programming mistakes can lead to vulnerabilities. While asynchronicity problems certainly exist— and can be exploited— in traditional Web applications, they are more common in Ajax applications, where the user can continue to start new actions while past actions are still being processed. One of the most common faults in a multithreaded or asynchronous application is the race condition. Race condition faults can occur when the application implicitly relies on events happening in a certain order, but does not explicitly require them to happen in that order. A good example of this is the account deposit/withdrawal functionality of a banking application. RACE CONDITIONS The First Bank of Ajax manages a checking account for Ashley, a marketing director at Simon’s Sprockets. Ashley has her paychecks automatically deposited into her checking account. When a new paycheck is deposited, the banking program uses the steps shown in Figure 5-3 to modify Ashley’s account: 135 CHAPTER 5 AJAX CODE COMPLEXITY Get current payee account balance Cancel transaction No Does payor have enough Yes money? Reduce payor’s account Increase payee’s account Figure 5-3 Flowchart for the checking account deposit logic at the First Bank of Ajax In pseudocode, the process would look like this: x = GetCurrentAccountBalance(payee); y = GetCurrentAccountBalance(payer); z = GetCheckAmount(); if (y >= z) SetCurrentAccountBalance(payer, y – z); SetCurrentAccountBalance(payee, x + z); else CancelTransaction; Everything looks fine, and Ashley never has any problems with her account. Apart from her day job, Ashley moonlights as a singer in an 80’s cover band. One Saturday morning, she takes the $250 check from her Friday night gig at Charlie’s Bar and deposits it at exactly the same moment that the $2000 automatic deposit from her day job is being processed. The automatic deposit code executes: x = GetCurrentAccountBalance(Ashley); // $5000 y = GetCurrentAcccountBalance(SimonsSprockets); // $1000000 z = GetCheckAmount(); // $2000 is ($1000000 >= $2000)? Yes SetCurrentAccountBalance(SimonsSprockets, $1000000 - $2000); SetCurrentAccountBalance(Ashley, $5000 + $2000); 136 ASYNCHRONICITY At the exact same moment, the teller-assisted deposit code executes: x = GetCurrentAccountBalance(Ashley); // $5000 y = GetCurrentAcccountBalance(CharliesBar); // $200000 z = GetCheckAmount(); // $250 is ($200000 >= $250)? Yes SetCurrentAccountBalance(CharliesBar, $200000 - $250); SetCurrentAccountBalance(Ashley, $5000 + $250); Oops! Instead of $7250 in her account, now Ashley has only $5250. Her $2000 paycheck from Simon’s Sprockets was completely lost. The problem was a race condition in the banking code. Two separate threads (the automatic deposit thread and the teller-assisted deposit thread) were both “racing” to update Ashley’s account. The teller-assisted deposit thread won the race. The banking application implicitly relied on one thread finishing its update before another thread began; but it did not explicitly require this. Security Implications of Race Conditions Beyond just bugs in functionality like Ashley’s disappearing paycheck, race conditions can also cause serious security problems. Race conditions can occur in user authentication procedures, which may allow an unauthorized user to access the system or a standard user to elevate his privileges and perform administrative actions. File access operations are also susceptible to race condition attacks, especially operations involving temporary files. Usually, when a program needs to create a temporary file, the program first checks to determine whether the file already exists, creates it if necessary, and then begins writing to it. There is a potential window of opportunity for an attacker between the time that the program determines that it needs to create a temporary file (because one doesn’t already exist) and the time that it actually creates the file. The attacker tries to create his own file, with permissions that he chooses, in place of the temporary file. If he succeeds, the program will use this file, and the attacker will be able to read and modify the contents. Another common security vulnerability occurs when an attacker intentionally exploits a race condition in an application’s pricing logic. Let’s assume our sample ecommerce application has two public server-side methods: AddItemToCart and CheckOut. The server code for the AddItemToCart method first adds the selected item to the user’s order and then updates the total order cost to reflect the addition. The server code for the CheckOut method debit’s the user’s account for the order cost and then submits the order to be processed and shipped, as illustrated in Figure 5-4. 137 CHAPTER 5 AJAX CODE COMPLEXITY AddItemToCart User CheckOut Server 1. Add item to order 2. Update order total cost 1. Debit user’s account 2. Ship order Figure 5-4 Nonmalicious use of the AddItemToCart and CheckOut methods SECURITY NOTE The programmers wisely decided against exposing all four internal methods as public methods and calling them directly from the client. If they had designed the application in this way, an attacker could simply skip the function in which his account was debited and get his order for free. This attack will be discussed in detail in Chapter 6, “Transparency in Ajax Applications.” Even though the programmers made a good design decision regarding the granularity of the server API, they are still not out of the woods, as we are about to find out. The application’s client-side code executes the AddItemToCart call synchronously; that is, it will not allow the user to call the CheckOut method until the AddItemToCart call has completed. However, because this synchronization is implemented only on the client, an attacker can easily manipulate the logic and force the two methods to execute simultaneously. In the case of Ajax XMLHttpRequest calls, this can be accomplished as simply as changing the async parameter of the call to the open method from false to true. If an attacker can time the calls to AddItemToCart and CheckOut just right, it is possible that he might be able to change the order in which the internal methods are executed, as shown in Figure 5-5. AddItemToCart CheckOut User Server 1. Add item to order 2. Debit user’s account 3. Update order total cost 4. Ship order Figure 5-5 An attacker exploits a race condition by calling AddItemToCart and CheckOut almost simultaneously. 138 ASYNCHRONICITY As you can see in Figure 5-5, the attacker has made the call to CheckOut after AddItemToCart added the selected item to his order, but before the program had the chance to update the order cost. The attacker’s account was debited for the old order cost—probably nothing—and his chosen item is now being shipped out completely freeof-charge. Solving the Race Condition Problem The typical solution to a race condition problem is to ensure that the critical code section has exclusive access to the resource with which it is working. In our example above, we would ensure in the server-side code that the CheckOut method cannot begin while the AddItemToCart method is executing (and vice-versa, or else an attacker might be able to add an item to the order after his account has been debited). To demonstrate how to do this, let’s fix the bank deposit program so that Ashley won’t have to spend her weekend tracking down her missing paycheck. AcquireLock; x = GetCurrentAccountBalance(payee); y = GetCurrentAccountBalance(payer); z = GetCheckAmount(); if (y >= z) SetCurrentAccountBalance(payer, y – z); SetCurrentAccountBalance(payee, x + z); else CancelTransaction; ReleaseLock; In our pseudocode language, only one process at a time can acquire the lock. Even if two processes arrive at the AcquireLock statement at exactly the same time, only one of them will actually acquire the lock. The other will be forced to wait. When using locks, it is vital to remember to release the lock even when errors occur. If a thread acquires a lock and then fails before it is able to release the lock again, no other threads will be able to acquire the lock. They will either time out while waiting or just wait forever, causing the operation to hang. It is also important to be careful when using multiple locks, as this can lead to deadlock conditions. DEADLOCKS AND THE DINING PHILOSOPHERS PROBLEM Deadlocks occur when two threads or processes each have a lock on a resource, but are waiting for the other lock to be released. So, thread 1 has resource 1 locked and is waiting 139 CHAPTER 5 AJAX CODE COMPLEXITY for resource 2, while thread 2 has resource 2 locked and is waiting for resource 1. This situation is exemplified by the Dining Philosophers Problem illustrated in Figure 5-6. Socrates Chopstick 1 Kant Chopstick 2 Spaghetti Chopstick 5 Chopstick 3 Chopstick 4 Descartes Hobbes Kierkegaard Figure 5-6 In the Dining Philosophers Problem, the five philosophers must share five chopsticks. Five philosophers sit down to eat dinner. In the middle of the table is a plate of spaghetti. Instead of forks and knives, the diners are only provided with five chopsticks. Because it takes two chopsticks to eat spaghetti, each philosopher’s thought process looks like this: 1. Think for a while. 2. Pick up left chopstick. 3. Pick up right chopstick. 4. Eat for a while. 5. Put down left chopstick. 6. Put down right chopstick. Because there are only five chopsticks, the philosophers are forced to share them. If all of the philosophers decide to pick up their left chopstick at the same time, none of them will be able to pick up their right chopstick. Their right chopstick is someone else’s left 140 ASYNCHRONICITY chopstick, and is already being used. The philosophers will sit at the table, each holding one chopstick, and starve to death waiting for the other one to become available. Security Implications of Deadlocks If an attacker can successfully set up a deadlock situation on a server, then she has created a very effective denial-of-service (DoS) attack. If the server threads are deadlocked, then they are unable to process new requests. Apple’s QuickTime Streaming Server was discovered to be vulnerable to this type of attack (and was subsequently patched) in September 2004. Let’s return to the First Bank of Ajax, where the programmers have attempted to improve their concurrency by switching from one global lock to one lock per account. AcquireLock(payee); AcquireLock(payer); x = GetCurrentAccountBalance(payee); y = GetCurrentAccountBalance(payer); z = GetCheckAmount(); if (y >= z) SetCurrentAccountBalance(payer, y – z); SetCurrentAccountBalance(payee, x + z); else CancelTransaction; ReleaseLock(payer); ReleaseLock(payee); This design change still solves the race condition issue, because two threads can’t access the same payee or the same payer at the same time. However, the bank programmers failed to realize that an attacker could cause an intentional DoS deadlock by submitting two simultaneous deposits: one request in which party A pays party B, and a second request in which party B pays party A. Because A and B are both each other’s payer and payee, the two deposit threads will deadlock, each waiting to acquire a lock it can never obtain. The two accounts are now effectively frozen. If another thread tries to acquire exclusive access to one of the accounts (perhaps a nightly interest calculator), then it too will be deadlocked. Solving the Deadlock Problem Some programs attempt to avoid this situation by detecting when they are deadlocked and changing their behavior to break the deadlock. In the case of the dining philosophers, a philosopher might notice that it’s been five minutes since he picked up his left 141 CHAPTER 5 AJAX CODE COMPLEXITY chopstick and he still hasn’t been able to pick up his right chopstick. He would try to be polite by setting down his left chopstick and then continue to wait for the right chopstick. Unfortunately, this solution still has the same problem! If all of the diners simultaneously set down their left chopsticks, they will then be immediately able to pick up the right chopsticks, but will be forced to wait for the left ones. They will be caught in an infinite loop of holding a chopstick in one hand, setting it down, and then picking another one up with their other hand. This situation is a variation of a deadlock called a livelock. Activity is taking place, but no actual work is getting done. Given that threading defects can cause security vulnerabilities, the following list of suggestions will help developers find and fix potential threading defects. 1. Look for shared resources being accessed in the code. These include: files being read from or written to; database records; and network resources, such as sockets, being opened. 2. Lock these resources so that only one thread at a time can access them. It is true that this will reduce concurrency and slow down the system. On the other hand, the system will function correctly and securely. It is more important for code to execute correctly than quickly. Furthermore, if security is not a big concern for you, why are you reading this book? 3. Remember to release the lock as soon as the thread is finished using the resource, even in an error condition. In languages with structured exception handling, such as C++, Java, C#, and VB.NET, the best way to accomplish this is with a try/catch/finally pattern. Release the lock in the finally block. Even if an error occurs, the lock will be released correctly. 4. Whenever possible, avoid having a single thread lock more than one resource at a time. This will practically eliminate the possibility that your application will deadlock. 5. If this is not possible, consider lumping all resources into a single group that is locked and unlocked en masse. This, too, will practically eliminate the possibility of deadlock. A variation of this technique is to always lock resources in a particular order. For example, in order to obtain a lock on resource C, a thread must first obtain a lock on resource A and then resource B, even if that thread does not directly access A or B. This technique can be used to solve the Dining Philosophers Problem, as shown in Figure 5-7. Each chopstick is ordered from one to five. Before any philosopher can pick up a chopstick, he first needs to pick up all the lower-numbered chopsticks. So Socrates would need to pick up chopstick one and then chopstick two; Kant would need one, then 142 ASYNCHRONICITY two, then three; and so on all the way to poor René Descartes who needs to obtain all five chopsticks in order to eat his dinner. Socrates Chopstick 1 Kant Chopstick 2 Spaghetti Chopstick 5 Chopstick 3 Chopstick 4 Descartes Hobbes Kierkegaard Figure 5-7 Solution to the Dining Philosophers Problem Both deadlocks and race conditions can be extremely difficult to reproduce. Remember that the dining philosophers all had to stop thinking and pick up their chopsticks at exactly the same time. Remember that Ashley had to deposit her check from Charlie’s Bar at exactly the same time that her paycheck from her day job was being deposited. It is likely that the programmers who created the bank application never encountered this condition during the course of development or testing. In fact, the “window of opportunity” to perform a threading attack might not present itself unless the application is under heavy load or usage. If testing did not occur under these conditions, the developer and QA professionals could be completely unaware of this issue. Whether they are found by the development team, by end users, or by hackers, threading vulnerabilities can be found in many applications. Ajax applications may be more prone to race conditions and intentional deadlock attacks because, by definition, much of the application logic is processed asynchronously. The increased granularity of exposed Ajax server-side APIs also contributes to application vulnerability. Remember that we were able to manipulate the pricing logic in 143 CHAPTER 5 AJAX CODE COMPLEXITY the sample Ajax e-commerce application given earlier because there was a race condition between its two exposed server-side methods. If the application had been implemented as a traditional Web application and the two methods executed sequentially with a single page request, the race condition would have been avoided. CLIENT-SIDE SYNCHRONIZATION We have discussed the importance of synchronizing access to server-side resources, but we haven’t mentioned anything about client-side resources. There are two main reasons for this omission. First, while there are third party libraries that provide them, there are no synchronization methods built into JavaScript. Second, even if they did exist, any security measures (including synchronization or request throttling) implemented solely on the client are useless. As we discussed earlier, it is impossible to guarantee that clientside code is executed correctly or even executed at all. To an attacker, JavaScript scripts are not commands that must be obeyed, but rather suggestions that can be modified or ignored. Relying on client-side code for security is like relying on the fox to guard the hen house. SECURITY NOTE It bears repeating: Never rely on client-side code for security. It can be a good idea to implement a security check both on the server and the client. Hopefully, the majority of people using your application aren’t trying to attack it. By implementing client-side checks, you can improve the performance of the application for the law-abiding users. Never forget, however, to mirror every client-side check with a server-side check to catch the hackers who manipulate the script code. BE CAREFUL WHOSE ADVICE YOU TAKE Tricky bugs like the ones described in this chapter can be maddening to hunt down and kill. Sometimes they can take whole days, or even longer, to track down. Only the most stubborn or masochistic developer would spend more than a few hours unsuccessfully trying to fix a bug without enlisting some kind of help, be it calling on a coworker, reading a magazine article, consulting a book, or reading a blog. However, this begs the question: Whose advice can you trust? Because Ajax is such a young technology, most technical resources are targeted at beginners. The focus of beginner resources is on 144 BE CAREFUL WHOSE ADVICE YOU TAKE teaching functionality—and not security. There are numerous books on the shelves with titles like Teach Yourself Ajax in 23 Hours and Convert Your Application to Ajax in 30 Minutes or Less. How can a programmer possibly give any thought to security when the whole development process takes less than half an hour? Instead of being encouraged to constantly crank out code at a cheetah-like pace, it might make more sense to encourage developers to slow down and consider their design decisions. Even when training resources do address security, it’s usually done in a very cursory way. While a good argument could be made that security should be the very first aspect of programming that a student learns, in practice it’s usually one of the last. Look in any beginner’s Ajax programming book. You will probably find one short chapter on security positioned somewhere toward the back. To some extent this is unavoidable: New programmers need to understand the basic concepts involved with a technology before they can understand the security risks. They need to know how to use the technology before they can learn how to misuse it. The authors have reviewed many of the popular Ajax books, discussion forums, and even developer conference magazines and materials. In nearly every instance, we discovered blatantly insecure coding examples, buggy source code that is not suitable for production, and missing, vague, or even incorrect and misleading advice about Ajax security. As a result, the majority of Ajax resources available to developers not only fail to address security properly, but also expose them to insecure development practices and design patterns. Developers should be extremely careful whose advice they accept and the resources they choose to consult. Developers should adopt a good security mindset to help guide their evaluation of advice they receive. A good security mindset is actually a very pessimistic one. You must constantly be thinking about what could go wrong, how your code could be misused, and how you can minimize risk. You must think of these things throughout all phases of the application lifecycle, from the initial design stage all the way through production. Security must be baked into the application from the beginning. It cannot simply be brushed on at the end. CONCLUSIONS The message we are trying to convey here is not that asynchronicity is bad, or that JavaScript is inherently unstable, or that Ajax programming is an enormously complex proposition. Rather, we are saying that it is tricky to understand all the security aspects of certain programming problems. To a large extent this is because we, as an industry, do not emphasize security or teach secure programming practices very well. Dealing with 145 CHAPTER 5 AJAX CODE COMPLEXITY tough problems like race conditions can be especially difficult: They are hard to reproduce, much less fix. A frustrated programmer who has been battling an elusive bug for hours or days will eventually reach a point at which he just wants his program to work. If he comes across an answer that appears to solve the problem, he may be so relieved to finally get past the issue that he doesn’t fully investigate the implications of his fix. Situations like these are the fertile ground in which security defects are grown. 146 6 Transparency in Ajax Applications Myth: Ajax applications are black box systems, just like regular Web applications. If you are like most people, when you use a microwave oven, you have no idea how it actually works. You only know that if you put food in and turn the oven on, the food will get hot in a few minutes. By contrast, a toaster is fairly easy to understand. When you’re using a toaster, you can just look inside the slots to see the elements getting hot and toasting the bread. A traditional Web application is like a microwave oven. Most users don’t know how Web applications work—and don’t even care to know how they work. Furthermore, most users have no way to find out how a given application works even if they did care. Beyond the fundamentals, such as use of HTTP as a request protocol, there is no guaranteed way to determine the inner workings of a Web site. By contrast, an Ajax Web application is more like a toaster. While the average user may not be aware that the logic of the Ajax application is more exposed than that of the standard Web page, it is a simple matter for an advanced user (or an attacker) to “look inside the toaster slots” and gain knowledge about the internal workings of the application. BLACK BOXES VERSUS WHITE BOXES Web applications (and microwave ovens) are examples of black box systems. From the user’s perspective, input goes into the system, and then output comes out of the system, as illustrated in Figure 6-1. The application logic that processes the input and returns the output is abstracted from the user and is invisible to him. 147 CHAPTER 6 TRANSPARENCY IN AJAX APPLICATIONS ? Figure 6-1 The inner workings of a black box system are unknown to the user. For example, consider a weather forecast Web site. A user enters his ZIP code into the application, and the application then tells him if the forecast calls for rain or sun. But how did the application gather that data? It may be that the application performs realtime analysis of current weather radar readings, or it may be that every morning a programmer watches the local television forecast and copies that into the system. Because the end user does not have access to the source code of the application, there is really no way for him to know. SECURITY NOTE There are, in fact, some situations in which an end user may be able to obtain the application’s source code. These situations mostly arise from improper configuration of the Web server or insecure source code control techniques, such as storing backup files on production systems. Please review Chapter 3, “Web Attacks,” for more information on these types of vulnerabilities. White box systems behave in the opposite manner. Input goes into the system and output comes out of the system as before, but in this case the internal mechanisms (in the form of source code) are visible to the user (see Figure 6-2). Any interpreted script-based application, such as a batch file, macro, or (more to the point) a JavaScript application, can be considered a white box system. As we discussed in the previous chapter, JavaScript must be sent from the server to the client in its original, unencrypted source code form. It is a simple matter for a user to open this source code and see exactly what the application is doing. 148 BLACK BOXES VERSUS WHITE BOXES Figure 6-2 The user can see the inner workings of a white box system. It is true that Ajax applications are not completely white box systems; there is still a large portion of the application that executes on the server. However, they are much more transparent than traditional Web applications, and this transparency provides opportunities for hackers, as we will demonstrate over the course of the chapter. It is possible to obfuscate JavaScript, but this is different than encryption. Encrypted code is impossible to read until the correct key is used to decrypt it, at which point it is readable by anyone. Encrypted code cannot be executed until it is decrypted. On the other hand, obfuscated code is still executable as-is. All the obfuscation process accomplishes is to make the code more difficult to read by a human. The key phrases here are that obfuscation makes code “more difficult” for a human to read, while encryption makes it “impossible,” or at least virtually impossible. Someone with enough time and patience could still reverse-engineer the obfuscated code. As we saw in Chapter 2, “The Heist,” Eve created a program to de-obfuscate JavaScript. In actuality, the authors created this tool, and it only took a few days. For this reason, obfuscation should be considered more of a speed bump than a roadblock for a hacker: It may slow a determined attacker down but it will not stop her. In general, white box systems are easier to attack than black box systems because their source code is more transparent. Remember that attackers thrive on information. A large percentage of the time a hacker spends attacking a Web site is not actually spent sending malicious requests, but rather analyzing it to determine how it works. If the application freely provides details of its implementation, this task is greatly simplified. Let’s continue the weather forecasting Web site example and evaluate it from an application logic transparency point of view. 149 CHAPTER 6 TRANSPARENCY IN AJAX APPLICATIONS EXAMPLE: MYLOCALWEATHERFORECAST.COM First, let’s look at a standard, non-Ajax version of MyLocalWeatherForecast.com (see Figure 6-3). Figure 6-3 A standard, non-Ajax weather forecasting Web site There’s not much to see from the rendered browser output, except that the server-side application code appears to be written in PHP. We know that because the filename of the Web page ends in .php. The next logical step an attacker would take would be to view the page source, so we will do the same. Weather Forecast
Enter your ZIP code:
150 BLACK BOXES VERSUS WHITE BOXES There’s not much to see from the page source code either. We can tell that the page uses the HTTP POST method to post the user input back to itself for processing. As a final test, we will attach a network traffic analyzer (also known as a sniffer) and examine the raw response data from the server. HTTP/1.1 200 OK Server: Microsoft-IIS/5.1 Date: Sat, 16 Dec 2006 18:23:12 GMT Connection: close Content-type: text/html X-Powered-By: PHP/5.1.4 Weather Forecast
Enter your ZIP code:
The weather for December 17, 2006 for 30346 will be sunny.
The HTTP request headers give us a little more information to work with. The header XPowered-By: PHP/5.1.4 confirms that the application is indeed using PHP for its serverside code. Additionally, we now know which version of PHP the application uses (5.1.4). We can also see from the Server: Microsoft-IIS/5.1 header that the application uses Microsoft Internet Information Server (IIS) version 5.1 as the Web server. This implicitly tells us that Microsoft Windows XP Professional is the server’s operating system, because IIS 5.1 only runs on XP Professional. So far, we have collected a modest amount of information regarding the weather forecast site. We know what programming language is used to develop the site and the particular version of that language. We know which Web server and operating system are being used. These tidbits of data seem innocent enough—after all, what difference could it make to a hacker if he knew that a Web application was running on IIS versus Tomcat? The answer is simple: time. Once the hacker knows that a particular technology is being 151 CHAPTER 6 TRANSPARENCY IN AJAX APPLICATIONS used, he can focus his efforts on cracking that piece of the application and avoid wasting time by attacking technologies he now knows are not being used. As an example, knowing that XP Professional is being used as the operating system allows the attacker to omit attacks that could only succeed against Solaris or Linux operating systems. He can concentrate on making attacks that are known to work against Windows. If he doesn’t know any Windows-specific attacks (or IIS-specific attacks, or PHP-specific attacks, etc.), it is a simple matter to find examples on the Internet. SECURITY NOTE Disable HTTP response headers that reveal implementation or configuration details of your Web applications. The Server and X-Powered-By headers both reveal too much information to potential attackers and should be disabled. The process for disabling these headers varies among different Web servers and application frameworks; for example, Apache users can disable the Server header with a configuration setting, while IIS users can use the RemoveServerHeader feature of Microsoft’s UrlScan Security Tool. This feature has also been integrated natively into IIS since version 6. For maximum security, also remap your application’s file extensions to custom types. It does little good to remove the X-Powered-By: ASP.NET header if your Web pages end in .aspx extensions. Hiding application details like these doesn’t guarantee that your Web site won’t be hacked, but it will make the attacker work that much harder to do it. He might just give up and attack someone else. EXAMPLE: MYLOCALWEATHERFORECAST.COM “AJAXIFIED” Now that we have seen how much of the internal workings of a black box system can be uncovered, let’s examine the same weather forecasting application after it has been converted to Ajax. The new site is shown in Figure 6-4. The new Web site looks the same as the old when viewed in the browser. We can still see that PHP is being used because of the file extension, but there is no new information yet. However, when we view the page source, what can we learn? Aha! Now we know exactly how the weather forecast is calculated. First, the function getRadarReading makes an asynchronous call to a Web service to obtain the current radar data for the given ZIP code. The radar data XML returned from the Web service is parsed apart (in the handleReadingRetrieved function) to find the cloud density reading. A second asynchronous call (getForecast) passes the cloud density value back to the 154 BLACK BOXES VERSUS WHITE BOXES server. Based on this cloud density reading, the server determines tomorrow’s chance of rain. Finally, the client displays the result to the user and suggests whether she should take an umbrella to work. Just from viewing the client-side source code, we now have a much better understanding of the internal workings of the application. Let’s go one step further and sniff some of the network traffic. HTTP/1.1 200 OK Server: Microsoft-IIS/5.1 Date: Sat, 16 Dec 2006 18:54:31 GMT Connection: close Content-type: text/html X-Powered-By: PHP/5.1.4 This architecture makes it easier for the site developers to maintain the code, because they now only have to make changes in a single place. It can save bandwidth as well, because a browser will download the entire library only once and then cache it for later use. Of course, the downside of this is that the entire API can now be exposed after only a single request from a user. The user basically asks the server, “Tell me everything you can do,” and the server answers with a list of actions. As a result, a potential hacker can now see a much larger attack surface, and his task of analyzing the application is made much easier as well. The flow of data through the system is more evident, and data types and method signatures are also visible. DATA TYPES AND METHOD SIGNATURES Knowing the arguments’ data types can be especially useful to an attacker. For example, if an attacker finds that a given parameter is an unsigned, 16-bit integer, he knows that valid values for that parameter range from 0 to 65,535 (216-1). However, the attacker is not constrained to send only valid values. Because the method arguments are sent as strings over the wire, the attacker is not even constrained to send valid data types. He may send a negative value, or a value greater than 65,535, to try to overflow or underflow the value. He may send a nonnumeric value just to try to cause the server to generate an error message. Error messages returned from a Web server often contain sensitive information, such as stack traces and lines of source code. Nothing makes analyzing an application easier than having its server-side source code! It may be useful just to know which pieces of data are used to calculate results. For example, in MyLocalWeatherForecast.com, the forecast is determined solely from the current cloud density and not from any of the other current weather variables such as temperature or dew point. The usefulness of this information can vary from application to application. Knowing that the current humidity does not factor into the weather forecast at MyLocalWeatherForecast.com may not help a hacker penetrate the site, but knowing that a person’s employment history does not factor into a loan application decision at an online bank may. SPECIFIC SECURITY MISTAKES Beyond the general danger of revealing application logic to potential attackers, there are specific mistakes that programmers make when writing client-side code that can open their applications to attack. 158 SPECIFIC SECURITY MISTAKES IMPROPER AUTHORIZATION Let’s return to MyLocalWeatherForecast.com. MyLocalWeatherForecast.com has an administration page, where site administrators can check usage statistics. The site requires administrative authorization rights in order to access this page. Site users and other prying eyes are, hence, prevented from viewing the sensitive content. Because the site already used Ajax to retrieve the weather forecast data, the programmers continued this model and used Ajax to retrieve the administrative data: They added client-side JavaScript code that pulls the usage statistics from the server, as shown in Figure 6-7. scriptlibrary.js User weatherforecast.php GetRadarReading admin.php GetUsageStatistics Administrator Figure 6-7 Intended usage of the Ajax administration functionality Unfortunately, while the developers at MyLocalWeatherForecast.com were diligent about restricting access to the administration page (admin.php), they neglected to restrict access to the server API that provides the actual data to that page. While an attacker would be blocked from accessing admin.php, there is nothing to prevent him from calling the GetUsageStatistics function directly. This technique is illustrated in Figure 6-8. There is no reason for the hacker to try to gain access to admin.php. He can dispense with the usual, tedious authorization bypass attacks like hijacking a legitimate user’s session or guessing a username and password through brute force. Instead, he can simply ask the server for the administrative data without having to go to the administrative page, just as Eve did in her attack on HighTechVacations.net in Chapter 2. The programmers at MyLocalWeatherForecast.com never intended the GetUsageStatistics function to be called from any page other than admin.php. They might not have even realized that it could be called from any other page. Nevertheless, their application has been hacked and they are to blame. 159 CHAPTER 6 TRANSPARENCY IN AJAX APPLICATIONS scriptlibrary.js admin.php X Attacker GetUsageStatistics Figure 6-8 Hacking the administration functionality by directly accessing the client-side JavaScript function SECURITY NOTE In this case, it was easy for the attacker to discover the GetUsageStatistics function and call it, because it was defined in a shared library referenced by both the main user page weatherforecast.php and the administration page admin.php. However, even if GetUsageStatistics were to be removed from the shared library and defined only in admin.php, this would not change the fact that an attacker could still call the server method directly if he ever found out about its existence. Hiding the method is not a substitute for appropriate authorization. Hiding the method is an example of relying on “security through obscurity” and is a dangerous approach to take. The problems with depending on obscurity are discussed later in this chapter. Some of the worst cases of improperly authorized API methods come from sites that were once standard Web applications but were later converted to Ajax-enabled applications. You must take care when Ajaxifying applications in order to avoid accidentally exposing sensitive or trusted server-side functionality. In one real-world example of this, the developers of a Web framework made all their user management functionality available through Ajax calls. Just like our fictional developers at MyLocalWeatherForecast.com, they neglected to add authorization to the server code. As a result, any attacker could easily add new users to the system, remove existing users, or change users’ passwords at will. 160 SPECIFIC SECURITY MISTAKES SECURITY NOTE When converting an existing application to Ajax, remember to add authorizationchecking code to newly-exposed methods. Functionality that was intended to be accessed only from certain pages will now be available everywhere. As a result, you can no longer rely on the authorization mechanisms of the page code. Each public method must now check a user’s authorization. OVERLY GRANULAR SERVER API The lack of proper authorization in the previous section is really just a specific case of a much broader and more dangerous problem: the overly granular server API. This problem occurs when programmers expose a server API and assume that the only consumers of that API will be the pages of their applications and that those pages will always use that API in exactly the way that the programmers intended. The truth is, an attacker can easily manipulate the intended control flow of any client-side script code. Let’s revisit the online music store example from Chapter 1, “Introduction to Ajax Security.” function purchaseSong(username, password, songId) { // first authenticate the user var authenticated = checkCredentials(username, password); if (authenticated == false) { alert(‘The username or password is incorrect.’); return; } // get the price of the song var songPrice = getSongPrice(songId); // make sure the user has enough money in his account if (getAccountBalance(username) < songPrice) { alert(‘You do not have enough money in your account.’); return; } // debit the user’s account debitAccount(username, songPrice); // start downloading the song to the client machine downloadSong(songId); } 161 CHAPTER 6 TRANSPARENCY IN AJAX APPLICATIONS The intended flow of this code is straightforward. First the application checks the user’s username and password, then it retrieves the price of the selected song and makes sure the user has enough money in his account to purchase it. Next, it debits the user’s account for the appropriate amount, and finally it allows the song to download to the user’s computer. All of this works fine for a legitimate user. But let’s think like our hacker Eve would and attach a JavaScript debugger to the page to see what kind of havoc we can wreak. We will start with the debugger Firebug for Firefox. Firebug will display the raw HTML, DOM object values, and any currently loaded script source code for the current page. It will also allow the user to place breakpoints on lines of script, as we do in Figure 6-9. Figure 6-9 Attaching a breakpoint to JavaScript with Firebug You can see that a breakpoint has been hit just before the call to the checkCredentials function. Let’s step over this line, allow the client to call checkCredentials, and examine the return value (see Figure 6-10). 162 SPECIFIC SECURITY MISTAKES Figure 6-10 Examining the return value from checkCredentials Unfortunately, the username and password we provided do not appear to be valid. The value of the authenticated variable as returned from checkCredentials is false, and if we allow execution of this code to proceed as-is, the page will alert us that the credentials are invalid and then exit the purchaseSong function. However, as a hacker, this does us absolutely no good. Before we proceed, let’s use Firebug to alter the value of authenticated from false to true, as we have done in Figure 6-11. By editing the value of the variable, we have modified the intended flow of the application. If we were to let the code continue execution at this point, it would assume (incorrectly) that we have a valid username and password, and proceed to retrieve the price of the selected song. However, while we have the black hat on, why should we stop at just bypassing authentication? We can use this exact same technique to modify the returned value of the song price, from $.99 to $.01 or free. Or, we could cut out the middleman and just use the Console window in Firebug to call the downloadSong function directly. 163 CHAPTER 6 TRANSPARENCY IN AJAX APPLICATIONS Figure 6-11 The attacker has modified the value of the authenticated variable from false to true. In this example, all of the required steps of the transaction—checking the user’s credentials, ensuring that she had enough money in her account, debiting the account, and downloading the song—should have been encapsulated as one single public function. Instead of exposing all of these steps as individual methods in the server API, the programmers should have written a single purchaseSong method that would execute on the server and enforce the individual steps to be called in the correct order with the correct parameter values. The exposure of overly-granular server APIs is one of the most critical security issues facing Ajax applications today. It bears repeating: Never assume that client-side code will be executed the way you intend—or even that it will be executed at all. SESSION STATE STORED IN JAVASCRIPT The issue of inappropriately storing session state on the client is nothing new. One of the most infamous security vulnerabilities of all time is the client-side pricing vulnerability. Client-side pricing vulnerabilities occur when applications store item prices in a clientside state mechanism, such as a hidden form field or a cookie, rather than in server-side state. The problem with client-side state mechanisms is that they rely on the user to return the state to the server without tampering with it. Of course, trusting a user to hold data as tantalizing as item prices without tampering with it is like trusting a fiveyear-old to hold an ice cream cone without tasting it. When users are capable of deciding how much they want to pay for items, you can be certain that free is going to be a popular choice. While this issue is not new to Ajax, Ajax does add a new attack vector: state stored in client-side JavaScript variables. Remember the code from the online music store: // get the price of the song var songPrice = getSongPrice(songId); // make sure the user has enough money in his account if (getAccountBalance(username) < songPrice) { 164 SPECIFIC SECURITY MISTAKES alert(‘You do not have enough money in your account.’); return; } // debit the user’s account debitAccount(username, songPrice); By storing the song price in a client-side JavaScript variable, the application invites attackers to modify the value and pay whatever they like for their music. We touched on this concept earlier, in the context of making the server API too granular and allowing an attacker to manipulate the intended control flow. However, the problem of storing session state on the client is separate from the problem of having an API that is too granular. For example, suppose that the server exposes an AddItem function to add an item to the shopping cart and a second function, CheckOut, to check out. This is a well-defined API in terms of granularity, but if the application relies on the client-side code to keep a running total of the shopping cart price, and that running total is passed to the CheckOut function, then the application is vulnerable to a client-side pricing attack. SENSITIVE DATA REVEALED TO USERS Programmers often hard code string values into their applications. This practice is usually frowned upon due to localization issues—for example, it is harder to translate an application into Spanish or Japanese if there are English words and sentences hard coded throughout the source code. However, depending on the string values, there could be security implications as well. If the programmer has hard coded a database connection string or authentication credentials into the application, then anyone with access to the source code now has credentials to the corresponding database or secure area of the application. Programmers also frequently misuse sensitive strings by processing discount codes on the client. Let’s say that the music store in our previous example wanted to reward its best customers by offering them a 50-percent-off discount. The music store emails these customers a special code that they can enter on the order form to receive the discount. In order to improve response time and save processing power on the Web server, the programmers implemented the discount logic in the client-side code rather than the serverside code. The programmers must not have been expecting anyone to view the page source of the order form, because if they had, they would have realized that their “secret” discount code is plainly visible for anyone to find. Now everyone can have their music for half price. In some cases, the sensitive string doesn’t even have to be a string. Some numeric values should be kept just as secret as connection strings or login credentials. Most e-commerce Web sites would not want a user to know the profit the company is making on each item in the catalog. Most companies would not want their employees’ salaries published in the employee directory on the company intranet. It is dangerous to hard code sensitive information even into server-side code, but in client-side code it is absolutely fatal. With just five seconds worth of effort, even the most unskilled n00b hacker can capture enough information to gain unauthorized access to sensitive areas and resources of your application. The ease with which this vulnerability can be exploited really highlights it as a critical danger. It is possible to extract hard coded values from desktop applications using disassembly tools like IDA Pro or .NET Reflector, or by attaching a debugger and stepping through the compiled code. This approach requires at least a modest level of time and ability, and, again, it only works for desktop applications. There is no guaranteed way to be able to extract data from serverside Web application code; this is usually only possible through some other configuration error, such as an overly detailed error message or a publicly accessible backup file. With client-side JavaScript, though, all the attacker needs to do is click the View Source option in his Web browser. From a hacker’s point of view, this is as easy as it gets. COMMENTS AND DOCUMENTATION INCLUDED IN CLIENT-SIDE CODE The dangers of using code comments in client code have already been discussed briefly in Chapter 5, but it is worth mentioning them again here, in the context of code transparency. Any code comments or documentation added to client-side code will be accessible by the end user, just like the rest of the source code. When a programmer explains the logic of a particularly complicated function in source documentation, she is not only making it easier for her colleagues to understand, but also her attackers. 166 SPECIFIC SECURITY MISTAKES In general, you should minimize any practice that increases code transparency. On the other hand, it is important for programmers to document their code so that other people can maintain and extend it. The best solution is to allow (or force?) programmers to document their code appropriately during development, but not to deploy this code. Instead, the developers should make a copy with the documentation comments stripped out. This comment-less version of the code should be deployed to the production Web server. This approach is similar to the best practice concerning debug code. It is unreasonable and unproductive to prohibit programmers from creating debug versions of their applications, but these versions should never be deployed to a production environment. Instead, a mirrored version of the application, minus the debug information, is created for deployment. This is the perfect approach to follow for client-side code documentation as well. This approach does require vigilance from the developers. They must remember to never directly modify the production code, and to always create the comment-less copy before deploying the application. This may seem like a fragile process that is prone to human error. To a certain extent that is true, but we are caught between the rock of security vulnerabilities (documented code being visible to attackers) and the hard place of unmaintainable code (no documentation whatsoever). A good way to mitigate this risk is to write a tool (or purchase one from a third party) that automatically strips out code comments. Run this tool as part of your deployment process so that stripping comments out of production code is not forgotten. SECURITY NOTE Include comments and documentation in client-side code just as you would with server-side code, but never deploy this code. Instead, always create a comment-less mirrored version of the code to deploy. DATA TRANSFORMATION PERFORMED ON THE CLIENT Virtually every Web application has to handle the issue of transforming raw data into HTML. Any data retrieved from a database, XML document, binary file—or any other storage location—must be formatted into HTML before it can be displayed to a user. In traditional Web applications, this transformation is performed on the server, along with all the other HTML that needs to be generated. However, Ajax applications are often designed in such a way that this data transformation is performed on the client instead of the server. 167 CHAPTER 6 TRANSPARENCY IN AJAX APPLICATIONS In some Ajax applications, the responses received from the partial update requests contain HTML ready to be inserted into the page DOM, and the client is not required to perform any data processing. Applications that use the ASP.NET AJAX UpdatePanel control work this way. In the majority of cases, though, the responses from the partial updates contain raw data in XML or JSON format that needs to be transformed into HTML before being inserted into the page DOM. There are many good reasons to design an Ajax application to work in this manner. Data transformation is computationally expensive. If we could get the client to do some of the heavy lifting of the application logic, we could improve the overall performance and scalability of the application by reducing the stress on the server. The downside to this approach is that performing data transformation on the client can greatly increase the impact of any code injection vulnerabilities such as SQL Injection and XPath Injection. Code injection attacks can be very tedious to perform. SQL Injection attacks, in particular, are notoriously frustrating. One of the goals of a typical SQL Injection attack is to break out of the table referenced by the query and retrieve data from other tables. For example, assume that a SQL query executed on the server is as follows: SELECT * FROM [Customer] WHERE CustomerId = An attacker will try to inject her own SQL into this query in order to select data from tables other than the Customer table, such as the OrderHistory table or the CreditCard table. The usual method used to accomplish this is to inject a UNION SELECT clause into the query statement (the injected code is shown in italics): SELECT * FROM [Customer] WHERE CustomerId = x; UNION SELECT * FROM [CreditCard] The problem with this is that the results of UNION SELECT clauses must have exactly the same number and type of columns as the results of the original SELECT statement. The command shown in the example above will fail unless the Customer and CreditCard tables have identical data schemas. UNION SELECT SQL Injection attacks also rely heavily on verbose error messages being returned from the server. If the application developers have taken the proper precautions to prevent this, then the attacker is forced to attempt blind SQL Injection attacks (covered in depth in Chapter 3), which are even more tedious than UNION SELECTs. However, when the query results are transformed into HTML on the client instead of the server, neither of these slow, inefficient techniques is necessary. A simple appended 168 SPECIFIC SECURITY MISTAKES SELECT clause is all that is required to extract all the data from the database. Consider our previous SQL query example: SELECT * FROM [Customer] WHERE CustomerId = If we pass a valid value like “gabriel” for the CustomerId, the server will return an XML fragment that would then be parsed and inserted into the page DOM. gabriel Krahulik Mike 707-555-2745 Now, let’s try to SQL inject the database to retrieve the CreditCard table data simply by injecting a SELECT clause (the injected code is shown in italics). SELECT * FROM [Customer] WHERE CustomerId = x; SELECT * FROM [CreditCard] If the results of this query are directly serialized and returned to the client, it is likely that the results will contain the data from the injected SELECT clause. Holkins Jerry 1234567812345678 09-07-2010 At this point, the client-side logic that displays the returned data may fail because the data is not in the expected format. However, this is irrelevant because the attacker has 169 CHAPTER 6 TRANSPARENCY IN AJAX APPLICATIONS already won. Even if the stolen data is not displayed in the page, it was included with the server’s response, and any competent hacker will be using a local proxy or packet sniffing tool so that he can examine the raw contents of the HTTP messages being exchanged. Using this simplified SQL Injection technique, an attacker can extract out the entire contents of the back end database with just a few simple requests. A hack that previously would require thousands of requests over a matter of hours or days might now take only a few seconds. This not only makes the hacker’s job easier, it also improves his chances of success because there is less likelihood that he will be caught by an intrusion detection system. Making 20 requests to the system is much less suspicious than making 20,000 requests to the system. This simplified code injection technique is by no means limited to use with SQL Injection. If the server code is using an XPath query to retrieve data from an XML document, it may be possible for an attacker to inject his own malicious XPath clause into the query. Consider the following XPath query: /Customer[CustomerId = ] An attacker could XPath inject this query as follows (the injected code is shown in italics): /Customer[CustomerId = x] | /* The | character is the equivalent of a SQL JOIN statement in XPath, and the /* clause instructs the query to return all of the data in the root node of the XML document tree. The data returned from this query will be all customers with a customer ID of x (probably an empty list) combined with the complete document. With a single request, the attacker has stolen the complete contents of the back end XML. While the injectable query code (whether SQL or XPath) is the main culprit in this vulnerability, the fact that the raw query results are being returned to the client is definitely a contributing factor. This design antipattern is typically only found in Ajax applications and occasionally in Web services. The reason for this is that Web applications (Ajax or otherwise) are rarely intended to display the results of arbitrary user queries. Queries are usually meant to return a specific, predetermined set of data to be displayed or acted on. In our earlier example, the SQL query was intended to return the ID, first name, last name, and phone number of the given customer. In traditional Web applications, these values are typically retrieved by element or column name from the query result set and written into the page HTML. Any attempt to inject a simplified ;SELECT attack clause into a traditional Web application query may succeed; but because 170 SPECIFIC SECURITY MISTAKES the raw results are never returned to the client and the server simply discards any unexpected values, there is no way for the attacker to exploit the vulnerability. This is illustrated in Figure 6-12. User SELECT*FROM CreditCard Returned data Customer Server SELECT*FROM Customer SELECT* FROM CreditCard Filter data Returned data Customer CreditCard Database Figure 6-12 A traditional Web application using server-side data transformation will not return the attacker’s desired data. Compare these results with the results of an injection attack against an Ajax application that performs client-side data transformation (as shown in Figure 6-13). You will see that it is much easier for an attacker to extract data from the Ajax application. User SELECT*FROM CreditCard Returned data Customer CreditCard Server Return all data SELECT*FROM Customer SELECT* FROM CreditCard Selected data Customer CreditCard Database Figure 6-13 An Ajax application using client-side data transformation does return the attacker’s desired data. Common implementation examples of this antipattern include: • Use of the FOR XML clause in Microsoft SQL Server • Returning .NET System.Data.DataSet objects to the client • Addressing query result elements by numeric index rather than name • Returning raw XPath/XQuery results The solution to this problem is to implement a query output validation routine. Just as we validate all input to the query to ensure that it matches a predetermined format, we 171 CHAPTER 6 TRANSPARENCY IN AJAX APPLICATIONS should also validate all output from the query to ensure that only the desired data elements are being returned to the client. It is important to note that the choice of XML as the message format is irrelevant to the vulnerability. Whether we choose XML, JSON, comma-separated values, or any other format to send data to the client, the vulnerability can still be exploited unless we validate both the incoming query parameters and the outgoing results. SECURITY THROUGH OBSCURITY Admittedly, the root problem in all of the specific design and implementation mistakes we’ve mentioned is not the increased transparency caused by Ajax. In MyLocalWeatherForecast.com, the real problem was the lack of proper authorization on the server. The programmers assumed that because the only pages calling the administrative functions already required authorization, then no further authorization was necessary. If they had implemented additional authorization checking in the server code, then the attacks would not have been successful. While the transparency of the client code did not cause the vulnerability, it did contribute to the vulnerability by advertising the existence of the functionality. Similarly, it does an attacker little good to learn the data types of the server API method parameters if those parameters are properly validated on the server. However, the increased transparency of the application provides an attacker with more information about how your application operates and makes it more likely that any mistakes or vulnerabilities in the validation code will be found and exploited. It may sound as if we’re advocating an approach of security through obscurity, but in fact this is the complete opposite of the truth. It is generally a poor idea to assume that if your application is difficult to understand or reverse-engineer, then it will be safe from attack. The biggest problem with this approach is that it relies on the attacker’s lack of persistence in carrying out an attack. There is no roadblock that obscurity can throw up against an attacker that cannot be overcome with enough time and patience. Some roadblocks are bigger than others; for example, 2048-bit asymmetric key encryption is going to present quite a challenge to a would-be hacker. Still, with enough time and patience (and cleverness) the problems this encryption method presents are not insurmountable. The attacker may decide that the payout is worth the effort, or he may just see the defense as a challenge and attack the problem that much harder. That being said, while it’s a bad idea to rely on security through obscurity, a little extra obscurity never hurts. Obscuring application logic raises the bar for an attacker, possibly stopping those without the skills or the patience to de-obfuscate the code. It is best to look at obscurity as one component of a complete defense and not a defense in and of 172 SECURITY THROUGH OBSCURITY itself. Banks don’t advertise the routes and schedules that their armored cars take, but this secrecy is not the only thing keeping the burglars out: The banks also have steel vaults and armed guards to protect the money. Take this approach to securing your Ajax applications. Some advertisement of the application logic is necessary due to the requirements of Ajax, but always attempt to minimize it, and keep some (virtual) vaults and guards around in case someone figures it out. OBFUSCATION Code obfuscation is a good example of the tactic of obscuring application logic. Obfuscation is a method of modifying source code in such a way that it executes in exactly the same way, but is much less readable to a human user. JavaScript code can’t be encrypted because the browser wouldn’t know how to interpret it. The best that can be done to protect client-side script code is to obfuscate it. For example, alert("Welcome to JavaScript!"); might be changed to this: a = "lcome to J"; b = "al"; c = "avaScript!\")"; d = "ert(\"We"; eval(b + d + a + c); These two blocks of JavaScript are functionally identical, but the second one is much more difficult to read. Substituting some Unicode escape characters into the string values makes it even harder: a = "\u006c\u0063\u006fme t\u006f J"; b = "\u0061\u006c"; c = "\u0061v\u0061Sc\u0072ipt\u0021\")"; d = "e\u0072t(\"We"; eval(b + d + a + c); There are practically an endless number of techniques that can be used to obfuscate JavaScript, several of which are described in the “Validating JavaScript Source Code” section of Chapter 4, “Ajax Attack Surface.” In addition, there are some commercial tools 173 CHAPTER 6 TRANSPARENCY IN AJAX APPLICATIONS available that will automate the obfuscation process and make the final code much more difficult to read than the samples given here. HTML Guardian™ by ProtWare is a good example. It’s always a good idea to obfuscate sensitive code, but keep in mind that obfuscation is not the same as encryption. An attacker will be able to reverse engineer the original source code given enough time and determination. Obfuscating code is a lot like tearing up a bank statement—it doesn’t make the statement impossible to read, it just makes it harder by requiring the reader to reassemble it first. SECURITY RECOMMENDATION Don’t Don’t confuse obfuscation with encryption. If an attacker really wants to read your obfuscated code, he will. Do Do obfuscate important application logic code. Often this simple step is enough to deter the script kiddie or casual hacker who doesn’t have the patience or the skills necessary to recreate the original. However, always remember that everything that is sent to the client, even obfuscated code, is readable. CONCLUSIONS In terms of security, the increased transparency of Ajax applications is probably the most significant difference between Ajax and traditional Web applications. Much of traditional Web application security relies on two properties of server-side code—namely, that users can’t see it, and that users can’t change it. Neither of these properties holds true for client-side Ajax code. Any code downloaded to a user’s machine can be viewed by the user. The application programmer can make this task more difficult; but in the end, a dedicated attacker will always be able to read and analyze the script executing on her machine. Furthermore, she can also change the script to alter the flow of the application. Prices can be changed, authentication can be bypassed, and administrative functions can be called by unauthorized users. The solution is to keep as much business logic as possible on the server. Only server-side code is under the control of the developers— client-side code is under the control of attackers. 174 7 Hijacking Ajax Applications Myth: Ajax source code and APIs are not easily modified. JavaScript programs can modify themselves while they are executing. This allows other JavaScript programs to automatically hijack the program execution of an Ajax application and twist it into performing malicious activities and exposing private user data. In Chapter 6, "Transparency in Ajax Applications," we saw that an attacker can manipulate client-side source code and data to produce malicious results. This was done using a JavaScript debugger or by physically rewriting the JavaScript code on the client machine. In this chapter will we show you how other JavaScript programs can intercept and automatically modify an Ajax application’s source code. Ajax frameworks (such as Dojo or Prototype), so-called “on-demand” Ajax applications, and even an Ajax application’s server-side API can all be hijacked with devastating results. The root of all the security issues we discuss in this chapter stem from an interesting feature of JavaScript: its ability to redefine functions after they have been declared. 175 CHAPTER 7 HIJACKING AJAX APPLICATIONS HIJACKING AJAX FRAMEWORKS We have stated that JavaScript has the ability to redefine functions after they have been declared. What exactly do we mean? Consider the following block of code. Clicking on the button calls fires the onclick event, which calls our sum() function, which, in turn, displays the message sum is 11. However, after 5 seconds the setTimeout() call redefines our sum() function. If you click the button after 5 seconds you receive the message hijacked. Readers should note that this generates no errors or warning messages. Not only is there no visual indication that a function definition has changed, but developers cannot prevent someone from redefining the sum() function! This has some interesting possibilities. As we discussed in the last chapter, developers cannot ensure that their JavaScript code executes in a user’s Web browser because the user could willingly turn off JavaScript or use a JavaScript debugger to selectively remove code. Now we see that developers also cannot protect the integrity of their code from other running JavaScript programs! ACCIDENTAL FUNCTION CLOBBERING Sometimes this function clobbering is accidental. A developer may create a function called debug() to aid in developing their program. However, a third party library, SexyWidgets, might also have a debug() function. If both functions are declared using the function debug() {...}, then both functions are declared in the same global scope. When JavaScript is embedded in the Web browser, the global scope is the window object. These functions collide, and we can see in Figure 7-1 that the debug() function for SexyWidgets clobbers the developer’s debug() function. The last function declared with the same name in the same scope will silently clobber the earlier function definition. In this case the reference to the external JavaScript file SexyWidgets.js comes later in the HTML and overrides the developer’s debug function. 176 HIJACKING AJAX FRAMEWORKS window This code iterates though all the properties of the window object, and makes a list of all the properties that are functions. The above code generates the dialog box show in Figure 7-7. Notice that our two user-defined functions, BogusFunction1() and BogusFunction2() are in the list, as are lots of common functions like window.alert() and window.setTimeout(). 185 CHAPTER 7 HIJACKING AJAX APPLICATIONS Figure 7-7 A list of all the properties on the window object that are functions in Firefox By compiling a list of what functions normally exist on the window object, we can write a program that detects user-defined functions from the window object.2 When we find a function, we can call the valueOf() function of the function object to extract the source code of that function. How does it work in Internet Explorer? Well, global objects and user-defined functions are not enumerable properties of the window object in Internet Explorer. So our for(var i in window) code snippet will never return a user-defined function. However, this is where namespaces help an attacker! If we know someone is using the 2 The functions that normally exist on the window object will vary from Web browser to Web browser. A list can easily be compiled by modifying the above code sample slightly. 186 HIJACKING ON-DEMAND AJAX org.msblabs.common namespace, we simply enumerate the properties of the org.msblabs.common object to find all the functions. Unfortunately this means that for Internet Explorer we have to know the name of the namespace a Web site is using. However, this usually is not a secret. An attacker can learn the namespace by visiting the Web site ahead of time and examining the layout of the JavaScript code. So far we have described how a JavaScript program can monitor the JavaScript environment and extract all the source code for user-defined functions. But how can we detect when new code is added? Simple. Every time our function scanner code runs, it makes a list of the user-defined functions it finds. We use the setInterval() function to repeatedly call our function scanning code and collect the source code for any new functions it detects. The authors created a tool called HOOK, which monitors the JavaScript environment and detects when new JavaScript functions are added. HOOK, itself, is written in JavaScript and is not a browser plug-in. As such, you can use the tool on almost all browsers or inside of an XSS payload. Let’s consider an example with a basic JavaScript framework called FRAME. Using HOOK to examine the code we see that FRAME has only two functions spread over 18 lines of code. The first function handles decrypting the text string. The second function calls the first function to decrypt some encrypted code, and then adds the decrypted code into the JavaScript environment through an eval() call. In Figure 7-8 we have marked the JavaScript variable containing the encrypted code. To keep this example simple we preassigned this code to a variable. In a real-world situation, this code would have come from the server in the response of an Ajax request. After we click the Load On-Demand Code button, the FRAME framework decrypts the new functions and adds them to the environment. Meanwhile, HOOK has been checking every five seconds, using a setInterval() call, to see if more functions have been added to the environment. In Figure 7-9 we see that HOOK detects that FRAME has added four new functions to itself and is now 30 lines of code long. Also visible in the background of the figure is Firebug, which does not show any of the new functions added to FRAME. 187 CHAPTER 7 HIJACKING AJAX APPLICATIONS Figure 7-8 HOOK enumerates the window object and extracts the two user-defined functions in the FRAME framework. Figure 7-9 HOOK has detected that four functions have been loaded on-demand. Notice Firebug, in the background, does not display these new functions. 188 HIJACKING ON-DEMAND AJAX Finally, in Figure 7-10 HOOK shows us the new functions that FRAME has added. We can see three basic math functions, as well as one function that sets a secret key. It is also worth pointing out that all of the functions that we captured are displayed in a format that is easy to read. HOOK doesn’t apply this formatting. HOOK extracts JavaScript functions by calling valueOf() on all user-defined function objects it detects. The JavaScript interpreter in the browser applies the formatting for us. Figure 7-10 HOOK has detected and extracted the four new functions.We see the function secret() contains what looks to be a password or key. HOOK is an ideal tool for debugging and monitoring on-demand Ajax applications. By accessing and indexing user-defined functions, HOOK can see all functions currently available to the JavaScript interpreter. This has the additional benefit of side-stepping all forms of JavaScript obfuscation, making HOOK an excellent tool for Web security testing or JavaScript malware analysis. HOOK should also illustrate that there is truly nothing you can do to prohibit people from accessing your JavaScript code. And, once they see your code, they can clobber it with malicious versions of your functions and completely hijack your program’s client-side logic. HOOK can be downloaded from www.msblabs.org. 189 CHAPTER 7 HIJACKING AJAX APPLICATIONS HIJACKING JSON APIS All of the above hijacking examples involve being able to execute JavaScript in other domains. Typically this is the result of an XSS vulnerability or on a site that allows users to upload their own pieces of JavaScript, such as a JavaScript widget. This practice is especially common in JavaScript mashups, which we discuss in depth in Chapter 10, “Request Origin Issues.” An Ajax application’s server-side API provides an attacker with various opportunities. We saw in Chapter 4, “Ajax Attack Surface,” that all these Ajax endpoints provide a larger attack surface for an attacker to scour for flaws. However, an Ajax application that uses JSON can be vulnerable to a specific type of hijacking known as JSON hijacking.3 JSON hijacking is a unique combination of Cross Site Request Forgery (CSRF) and JavaScript function clobbering. As we saw in Chapter 3, “Web Attacks,” CSRF works because a browser automatically attaches any cached HTTP authentication credentials or any appropriate cookies to outgoing requests. If Bob is logged into Gmail and visits a Web page on evil.com that contains a script tag like Figure 7-11 The Ajax endpoint PastTrips.ashx returns a nested JSON array containing a user’s past trips booked on HighTechVacations.net. Here we have a literal array defined. Internally, the JavaScript interpreter calls the array constructor function Array() to create an array object from the supplied array literal. Next, the JavaScript interpreter checks to see if an operation is performed on this array. For example, [1, 2, 3].join(",") is perfectly valid JavaScript code. However, in this block of JavaScript no operations are performed on this array. Because the array object was never assigned to a variable, it is unreferenced, and the object will eventually be cleared by the JavaScript interpreter’s garbage collection routines. Thus, by pointing a SCRIPT tag at an Ajax endpoint on a Web server that returns JSON, we can force the JavaScript interpreter to execute the array constructor function Array(). 191 CHAPTER 7 HIJACKING AJAX APPLICATIONS We know from the “Hijacking Ajax Frameworks” section earlier in the chapter that JavaScript code can clobber other functions, including internal functions. It turns out we can clobber the Array() function as well! An attacker can replace the array constructor with their own malicious version, which can capture all the contents of the array and send them to a third party and capture the method. Consider the following piece of code. function Array() { var foo = this; var bar = function() { var ret = "Captured array items are: ["; for(var x in foo) { ret += foo[x] + ", "; } ret += "]"; //notify an attacker. Here we just display it alert(ret); }; setTimeout(bar, 100); } In our malicious array function, we set the variable foo equal to the current object (the array that is being created). We also create an anonymous function bar(), which will iterate all the properties of the variable foo (all the items stored in the array). All the data collected is simply displayed to the user, but it is trivial for an attacker to send this data to a third party using an Image object. The last thing the evil array constructor does is use the setTimeout() function to call our bar() function after 100 milliseconds. This ensures that by the time the bar() function is called, the elements in our array literal have been properly loaded into the array so that our bar() function can steal the contents. An attacker can use these methods to steal JSON data returned by a Web site’s API as seen in Figure 7-12. Let’s say Alice has logged into HighTechVacations.net. By this we mean Alice has authenticated herself, and HighTechVacations.net has issued her a cookie with a session ID. Next, Alice is tricked into visiting the malicious Web site evil.com, as seen in Step 1 of Figure 7-12. evil.com returns a Web page to Alice’s browser with the following code: 192 HIJACKING JSON APIS JSON Hijacking Demo ... clipped for brevity evil.com 3 [JSON Array] 1 GET/HTTP/1.1 HTML 2 GET PastTrips.ashx HTTP/1.1 Cookie: AE783F… Alice [JSON Array] HighTechVacations.net Figure 7-12 Alice visits evil.com, which includes a script tag whose source attribute points to another Web site. Because JavaScript on evil.com has replaced the array constructor, the contents of any JSON arrays returned by the third-party Web site can be stolen by evil.com. 193 CHAPTER 7 HIJACKING AJAX APPLICATIONS The first script tag contains a malicious array constructor that will steal the contents on any arrays that are created and report them back to evil.com. The second script tag contains an external reference to the PastTrips.ashx endpoint on HighTechVacations.net. As with any classic CSRF attack, this forces Alice’s browser to send an authenticated request to PastTrips.ashx on HighTechVacations.net, which responds with a JSON array containing Alice’s past trips booked through HighTechVacations.net. This occurs in Step 2 of Figure 7-12. When the browser receives this JSON array inside of the script tag, it passes it to the JavaScript interpreter, which sees the array literal and calls the malicious array constructor. This malicious code steals the JSON array of Alice’s past trips and sends them back to evil.com as illustrated in Step 3 of Figure 7-12. Figure 7-13 shows that evil.com has been able to access the JSON data returned by Ajax endpoints on HighTechVacations.net. Readers can compare the trip data in Figure 7-13 with the data that is returned when we directly talked with HighTechVacations.net in Figure 7-11 and see they are identical. Figure 7-13 evil.com has successfully used JSON hijacking to steal the list of Alice’s past trips from HighTechVacations.net. 194 HIJACKING JSON APIS JSON hijacking is not a hypothetical attack created by Web security researchers. JSON hijacking vulnerabilities have been discovered in several large, well-known Ajax applications, including Google’s Gmail. HIJACKING OBJECT LITERALS There is nothing special about JavaScript arrays that enables us to hijack them. An attacker could also clobber an object constructor function, such as Object(), to steal data from Ajax APIs that return JSON object literals instead of array literals. In fact, simply replacing function Array() with function Object() turns our malicious array constructor into a malicious object constructor! There is one catch, though. While an array literal on a line of code all by itself is valid JavaScript, an object literal is not. Consider the following block of JavaScript code: When the JavaScript interpreter parses this, a syntax error invalid label is thrown. This occurs because the curly braces { and } of the object literal are interpreted as the start and end of a code block and not as a object literal. Thus the "frequentFlyer" sequence is interpreted as a JavaScript label instead of the name of an object property, and JavaScript labels cannot include a quote character.4 This is what we meant earlier when we said that most JSON is a valid subset of JavaScript. JSON objects literals inside of parentheses such as ({"suit": "spades", "value": "jack"}) are valid JavaScript. So, just because you are using JSON objects instead of JSON arrays doesn’t automatically protect you from JSON hijacking. ROOT OF JSON HIJACKING When JSON hijacking was first discussed and demonstrated in 2006 and 2007, all the proof of concepts used Mozilla-specific JavaScript extensions like setter or __defineSetter__. This led many people to believe that these vulnerabilities only existed in Mozilla-derived browsers like Firefox, because only those browsers supported 4 We bet you didn’t know that JavaScript had labels. Well it does. Things become even more confusing when you realize that JavaScript doesn’t have a “goto” keyword to jump to those labels! To learn more, check out the obscure labeled continue feature of JavaScript 195 CHAPTER 7 HIJACKING AJAX APPLICATIONS extensions like __defineSetter__. Unfortunately, this is not the case. As anyone can see, our malicious array or object constructor does not use Mozilla-specific code. Does this mean all the other browsers are affected by this issue? The answer is no, they are not. To understand, think about the two conditions that make API hijacking possible. 1. JSON array literals and object literals returned by Ajax endpoints are valid JavaScript. 2. The JavaScript interpreter automatically calls the array or object constructor when it encounters the appropriate literal. Of these two reasons, there is nothing that can be done about Reason 1. Indeed, JSON was deliberately designed to be a subset of JavaScript to ease its parsing through the use of the eval() function. However, Reason 2 is specific to each browser’s JavaScript interpreter. The authors tested the major browsers and concluded that the JavaScript interpreter in Mozilla-derived browsers (known as SpiderMonkey) is the only JavaScript interpreter that invokes the array or object constructors when a literal is encountered. It turns out that Mozilla-derived browsers (Mozilla, Firefox, Netscape, IceWeasel, and so on) are still the only browsers vulnerable to API hijacking, but for a completely different reason than previously believed! DEFENDING AGAINST JSON HIJACKING Conceptually, JSON hijacking is easy to defend against. When Ajax endpoints are contacted directly using a script tag, the endpoint returns data that is immediately executed by the JavaScript interpreter. However, when an Ajax endpoint is contacted using an XMLHttpRequest object, the developer can do whatever he wants with the returned data. Thus, most defenses against JSON hijacking revolve around the application deliberately tainting responses from an Ajax endpoint with bad data. If the Ajax endpoint is contacted directly with a script tag, this bad data is immediately executed and prevents the JavaScript interpreter from reaching the JSON literals. Suppose that the PastTrips.ashx Ajax endpoint from HighTechVacations prefixes its JSON responses with a line of syntactically invalid JavaScript code.5 If PastTrips.ashx is contacted using a script tag, the JavaScript interpreter executes the following block of code: 5 This is an insecure example! Do not taint your JSON responses with malformed JavaScript source code. We use this example to show how easy it is to solve JSON hijacking in an insecure way. We will discuss how to actually secure against JSON hijacking in the next few paragraphs. Again, do not ever use this example in production code! 196 HIJACKING JSON APIS I’/\/\ a bl0ck of inva1id $ynT4x! WHOO! [["AJAXWorld", "2007-04-15", "2007-04-19", ["ATL", "JFK", "ATL"], 95120657, true], ["Honeymoon", "2007-04-30", "2007-05-13", ["ATL", "VAN", "SEA", "ATL"], 19200435, false], ["MS Trip", "2007-07-01", "2007-07-04", ["ATL", "SEA", "ATL"], 74905862, true], ["Black Hat USA", "2007-07-29" "2007-08-03", ["ATL", "LAS", "ATL"], 90398623, true]]; The JavaScript interpreter would attempt to execute this—and would throw a syntax error when it encounters the first line. This prevents the JavaScript interpreter from ever reaching the JSON literals. If PastTrips.ashx was contacted legitimately by JavaScript using XMLHttpRequest, then the client-side JavaScript could remove the line of malformed code from the response before attempting to parse the JSON object. We have established that to secure Ajax endpoints that return JSON against JSON hijacking we need to somehow taint the data that is returned and then undo that tainting in client-side code before evaluating the response. But how should we taint the data? Unfortunately, this is where many people make mistakes. Consider our (insecure) solution of tainting JSON responses with malformed JavaScript code. An attacker could define her own error handler function by overriding window.onerror(). Depending on various conditions, the attacker might be able to trap the error and see the JSON object that is returned. Another common (and insecure) solution is to wrap the JSON response in a multiline comment using /* and */. Consider a Web service that returns a list of names that uses comment tainting and is accessed directly using a script tag. Here is what is returned into the script tag. If the application didn’t perform properly input validation, it is conceivable that a pair of malicious users, Eve and Nidhi, could craft their names so that Eve’s name is Eve*/["bogus and Nidhi’s name is bogus"]/*Nidhi. This results in the following code in the script tag In this case, the array literal ["bogus", "Jill", "Mary", "Jen", "Amy", "bogus"] is not inside of JavaScript comments and will be passed to the malicious array constructor where it is stolen. While this is a more sophisticated attack, comment tainting can be dangerous because it depends on the Web developer to perform proper input validation. If a developer forgets to properly validate that names are only composed of letters, then Eve and Nidhi can insert */ or /* to terminate the comment prematurely and expose the rest of the data. By now we hope we have impressed upon you that, while you must perform input validation on everything, a defense in depth strategy is always prudent. Comment tainting only works if another defensive measure is in place. Thus, is not the most effective solution. The best solution to securing you application against JSON hijacking is to taint responses with an infinite loop, as shown here. This causes the JavaScript interpreter to go into an infinite loop if someone attempts to hijack the Ajax endpoint. This method is superior for two reasons. First, unlike comment tainting, infinite loop tainting doesn’t require two pieces of text surrounding the JSON that could be prematurely terminated. Second, for(;;); consists of nothing but a JavaScript keyword and some symbols. There is no way an attacker can clobber or override the for keyword. Some people suggest using while(1);. This is not an ideal solution because 1 is a numeric literal, and it could be possible that some JavaScript interpreters would invoke the number constructor function Number() when a numeric literal is encountered. An attacker could conceivably use this=0; inside a malicious number constructor and literally redefine the value of 1, making the while conditional evaluate to false, which in turn causes the JavaScript interpreter to fall through to the JSON literal. The same possibility could apply to using while(true); as an infinite loop and boolean literals. The authors currently know of no JavaScript interpreters that do this, but it certainly is possible that one might do so in the future. As a security-conscious developer, you must think not only about how to secure your application now, but how you can secure your application in such a way that minimizes the chance it will become insecure in the future due to technological changes. 198 CONCLUSIONS SECURITY RECOMMENDATION Developers should use infinite loop tainting to secure their Ajax endpoints that return JSON against JSON hijacking. Specifically, developers should use for(;;);. Not only is it composed exclusively of JavaScript keywords, it is also shorter than while(1);. The for(;;); statement in the response can easily be removed in client-side JavaScript using the substring() function on the responseText property of the XMLHttpRequest object. The following JavaScript code can be used to remove infinite loop tainting from an Ajax endpoint. The defang() function should be called with the responseText property of the XMLHttpRequest object before the JSON is parsed. In this code we are using Crockford’s JSON parsing library. function defangJSON(json) { if(json.substring(0,8) == "for(;;);") { json = json.substring(8); } Return json; } var safeJSONString = defangJSON(xhr.responseText); var jsonObject = safeJSONString.parseJSON(); CONCLUSIONS We’ve seen that JavaScript’s dynamic nature allows other JavaScript programs to automatically modify an Ajax application’s source code. Function clobbering, previously thought to be an annoyance that led people to use namespaces, can be used maliciously to completely alter a program’s source code as well as passively monitor data flow through the program. We’ve seen that JavaScript code can be used to track and trap new pieces of source code that are downloaded on demand. By now we hope we have driven home the point that anyone can reverse engineer the client-side portion of your application, even if it is loaded dynamically in small bits and pieces. Unfortunately, there is nothing a developer can do to prevent such malicious activities. We also saw that not only can user-defined functions clobber other user-defined functions, but we can also 199 CHAPTER 7 HIJACKING AJAX APPLICATIONS override internal functions like window.alert() and even native object constructors. This enables an attacker to perform JSON hijacking attacks against users and steal any data your application returns through JSON-enabled Ajax endpoints. Developers should use infinite loop tainting to secure applications against JSON hijacking. Having thoroughly demonstrated the security vulnerabilities an attacker can exploit in the client-side programming logic of an Ajax application, in the next chapter we focus on the security issues in storing data on the client using client-side storage systems. 200 8 Attacking Client-Side Storage Myth: The client’s machine is a safe place to store data. There are several security issues when Ajax applications store data on the client. Not only is client-side storage easily viewed or modified by an attacker, client-side storage methods can also leak access to these storage spaces to untrusted third parties. This can allow an attacker to remotely read all offline data stored on the client by an Ajax application. Even security-conscious developers who explicitly avoid putting sensitive data in client-side storage systems can inadvertently do so when they use client-side storage to cache data tables or trees. Only by fully understanding the access methods of each clientside storage method and implementing expiration policies and proper access control can a developer truly secure an Ajax application that utilizes client-side storage. OVERVIEW OF CLIENT-SIDE STORAGE SYSTEMS The client-side portions of Web applications have been hobbled from fully participating as major components of an application by four roadblocks: • Sufficient penetration of (semi-) standards compliant browsers allowing developers to easily write cross-platform client-side programs • Sufficient penetration of personal computers fast enough to parse and interpret large and complex client-side programs • A means to transmit data back and forth between the client and server without interrupting the user’s experience 201 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE • A large, persistent data storage system on the client to persist the input and output of our computations between different pages The first requirement was satisfied by time as Web standards matured and Web developers and users pressured the browser manufactures to conform to standards. It is now far easier to write cross-browser JavaScript than in the Web dark ages of the 1990s. Moore’s Law, which states computing power doubles every 18 months, took care of the second requirement. Modern computers run complex interpreted programs inside a browser much faster than before. Remember how long Java applets took to run in the mid 1990s on a Pentium 90 with 32MB of RAM? The third requirement was handled by the pillar of Ajax: the XMLHttpRequest object. Ajax applications seamlessly move data without the long, full page refreshes of yesteryear. The final requirement has recently been met with the rise of JavaScript-accessible client-side storage systems. Offline Ajax is a perfect example. Offline Ajax allows users to access Web applications without being connected to the Internet. We discuss offline Ajax application in depth in Chapter 9, “Offline Ajax Applications.” However, client-side storage is essential for this capability. The benefits of client-side storage include reducing Ajax traffic by storing data on the client, improving a slow network connection, or persisting data across domains or browser instances. In this chapter we examine several different client-side storage methods and discuss how to use them securely. Specifically, we examine HTTP cookies, Flash Local Shared Objects, Mozilla’s DOM storage, and Internet Explorer’s userData. Before we dive into the different implementations for client-side storage, we should examine how long the data is stored on the client. There are two classifications, persistent and nonpersistent, which denote how long data is stored in a system. Nonpersistent data is stored temporarily on the client and is discarded when the user closes the Web browser. Persistent data is stored on the client in a more permanent capacity. It survives if the user closes and reopens the browser, or even if she reboots her machine. Data stored persistently usually has an expiration date. Much like a jug of milk in your fridge, once the expiration date for the persistent data has passed, the Web browser deletes it. When developers are selecting a data storage system it is important to know whether the data stored in the system will be stored persistently. GENERAL CLIENT-SIDE STORAGE SECURITY As we learned in the myth at the start of this chapter, there are several significant security concerns related to storing data on the client. When we examine each method for storing data on the client, readers should keep several questions in mind. Knowing the answers will help you pick the most appropriate and secure client-side storage method for your application. These questions include: 202 OVERVIEW OF CLIENT-SIDE STORAGE SYSTEMS • What browsers are supported? While there are some frameworks like Dojo.Storage that attempt to abstract away the differences between storage methods, you could end up with a poorly implemented feature depending on which browser your users access your application with. • Does the storage method offer persistent, nonpersistent, or both forms of data storage? If you can only store data persistently, it is up to you to implement code to delete and purge data when appropriate. • How much data can you store? What is the default capacity? What is the maximum capacity? It does not matter how appealing the other features of a storage method are if it cannot offer enough space for your application. • What data types can you store? If a storage method can only save strings, then you will have to handle serialization and deserialization of other data types. As mentioned earlier, this is a step that attackers like to focus on because it is very easy to cause Denial of Service attacks in custom serialization and deserialization code. Be aware of which storage methods force you to do some heavy lifting. • What are the access policies for the storage method? What other domains, services, and Web pages can access the data by default? What features does the storage method have that allow you to limit who can access the data? • How do you clean up or remove old data? Leaving unnecessary data around isn’t just sloppy, it can also be a security vulnerability. While no secret can be protected on the client, leaving the sensitive data scattered all around for long periods of time isn’t going to help matters. Pay attention to which methods automatically delete data for you or allow you to set an expiration date for the data. • How easy is it for the user to delete the data? If you pick a volatile storage method, your application will need to handle situations in which the client-side data disappears. You did write your application to handle errors gracefully, right? • How easy is it to read the data stored on the machine? Attackers can definitely read any data you store on the client, regardless of the method you pick. The real question is, how much work must an attacker perform to read what is stored? Never, never, never store anything secret in client-side storage! • How easy is it to modify the data stored on the machine? Attackers can definitely modify any data you store on the client, regardless of the method you pick. The real question is, how much work must an attacker perform to write over the stored data? This is an excellent vector to launch attacks and is another example of input that requires validation. 203 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE HTTP COOKIES HTTP cookies are one of the most basic forms of client-side storage. To fully appreciate the limitations and security issues of using cookies as a storage mechanism, we must explorer the history of cookies. In case you missed the memo, HTTP is a stateless protocol. This means that the server treats each request as an isolated transaction that is not related to any previous request. Cookies were created in 1994 by Netscape as a means to impose a state-keeping mechanism on top of the HTTP. Fundamentally, cookies are a mechanism to allow the Web server to store a small amount of data on a user’s machine. A user’s Web browser attaches this cookie data to outgoing requests back to the Web server that set the data.1 Figure 8-1 shows the browser’s cookie jar—where cookies the Web browser has received are stored. Figure 8-1 The browser’s cookie jar displays a list of cookies the browser has and all of their properties. To impose state-keeping, a Web server can store a unique identifier for each visitor inside a cookie and send the cookie to the visitor’s Web browser. Every time that visitor requests a page from that Web server, their Web browser attaches the cookie containing the unique identifier to the outgoing HTTP request. This allows the Web server to differentiate between different, distinct users accessing their resources. Remember, each user has a 1 This is actually a simplification. We discuss how developers can control which cookies get sent to which Web servers later in this section. 204 HTTP COOKIES different unique identifier. This differentiation allows Web applications on the Web server to store session information about each user.2 Some common uses of session data include keeping the contents of a shopping cart or a user’s language preference. The following are the HTTP headers of a Web server’s response where a cookie is set. HTTP/1.1 200 OK Server: Microsoft-IIS/5.1 Date: Wed, 06 Jun 2007 00:05:42 GMT X-Powered-By: ASP.NET Cache-Control: private Content-Type: text/html; charset=utf-8 Content-Length: 909 Connection: Keep-Alive Set-Cookie: shoppingCart=51349,90381,7744; Expires=Tue, 03-Jun-2008 05:00:00 GMT; Path=/Assign/Shop/ The Set-Cookie header is what tells the Web browser to store a cookie. In the preceding code it appears that the cookie represents some kind of online shopping cart. Notice that in addition to a name/value of data to store, the Web application is able to specify other attributes of the cookie. For example, this cookie declaration sets an expiration date for the cookie. This means the cookie is stored persistently on the client’s machine until that expiration date. Once the expiration data has passed, the Web browser will automatically delete that cookie. There is no real limit on how far in the future the expiration date of a cookie can be set. Sharp-eyed readers will notice in Figure 8-1 that the PREF cookie that Google sets does not expire until 2038! If a cookie is set, but is not given an expiration date, it is considered a nonpersistent cookie and will be discarded when the user closes the Web browser. Thus, the use of the Expires directive allows Web applications to store arbitrary data persistently on the client inside a cookie, while excluding the Expires directive provides nonpersistent client-side data storage. It’s paramount to remember that cookies were designed to store small amounts of data on the client’s machine to impose state on top of HTTP. They weren’t intended to be a general client-side storage mechanism. This has profound consequences. For example, the Web browser sends the appropriate cookies to the Web server on each and every request. There is no way to change this behavior. In fact, it makes sense that the Web browser would send the appropriate cookies on every request. Without a cookie containing a unique identifier allowing the Web to differentiate incoming requests for the same 2 As we saw in the “Session Hijacking” section of Chapter 3, if an attacker gains access to a user’s unique identifier he can impersonate that user by making fraudulent requests with the stolen unique identifier. 205 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE resource, all requests would revert to being stateless.3 The Web browser sends the appropriate cookies to each and every page because the Web browser has no idea which Web pages on the server actually use the data and which don’t. So, the browser simply sends all appropriate cookies all the time for all pages, regardless of whether the server-side code of the Web application actually uses cookies or not. We discuss the security problems of this design later in this section. COOKIE ACCESS CONTROL RULES Surely we aren’t transmitting every cookie we have to every Web site we visit, are we? The answer is: No. Only the cookies that are appropriate when requesting a given Web page are added to the outgoing request. What do we mean by appropriate cookies? Well, cookies can have access control rules that tell the Web browser which pages should get the cookie. For example, a cookie can tell the browser what domains, what protocols, or what paths it is valid for. When a browser wants to request a page from a Web server, it checks the URL of the resource against all the cookies in the browser’s cookie jar. If the URL passes all the access control rules for a cookie, then that cookie is added to the outgoing request. In Figure 8-1, we can see that the PREF cookie used by Google should only be sent with requests to URLs ending in .google.com, regardless of the URLs path or use of SSL. Cookie access control rules form the basis for the access control rules used by all other client-side storage methods. We will examine these rules in fine detail now, and note any differences from these ground rules in the specific sections covering the other client-side storage methods. Cookies can define access rules using three different properties. These rules determine which cookies are attached to an outgoing request. These properties are: which domain names can access the cookie; which path or folder structure is needed to access the cookie; and whether the cookie must be sent over a secured connection or not. By default, cookies will be sent with all requests to the same domain that set the cookie, regardless of the path and regardless of whether the channel is secure or not. Figures 8-2 through 8-4 illustrate the access control of a default cookie. 3 Technically, there are other methods of differentiating users between requests other than cookies, such as using URL session state, but cookies are the most commonly used method. 206 HTTP COOKIES GET/Items/Buy.php HTTP/1.1 Host: www.store.com … Cookie: item=8441 HTTP/1.1 200 Ok … www.store.com:80 Figure 8-2 Normal HTTP transaction where www.store.com sets a cookie with default access control GET/Shopping.php HTTP/1.1 Host: www.store.com … HTTP/1.1 200 Ok … Set-Cookie: item=8441 www.store.com:80 Figure 8-3 The Web browser sends this cookie to all pages on www.store.com regardless of the page’s path. GET/Checkout/ HTTP/1.1 Host: www.store.com … Cookie: item=8441 HTTP/1.1 200 Ok … www.store.com:443 Figure 8-4 The cookie is also sent to an SSL-enabled Web server running on a different port of same domain that issued the cookie. Figure 8-2 shows www.store.com setting a cookie (item=8441). As you can see from the Set-Cookie header, this cookie has no special attributes limiting its domain or path. Because there is no Expires attribute, the cookie that is created is a nonpersistent cookie named item containing the value 8441 with default access control. Figure 8-3 shows that this cookie is sent in all Web page requests to www.store.com, regardless of the path to that Web page. The item cookie is even sent to other HTTP services running on other 207 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE ports on www.store.com. Figure 8-4 shows the Web browser sending the item cookie to an SSL-enabled Web server running on port 443 of www.store.com. The flip side of this is that any cookies set in the SSL version of the Web site at www.store.com:443 will also be sent in requests for pages on the non-SSL Web server at www.store.com:80. As expected, Figure 8-5 shows that the item cookie assigned by www.store.com is not transmitted to other domains such as www.other.com. This prevents a malicious Web site such as evil.com from reading the cookies stored for bank.com. Figure 8-6 shows us that using the default access control restrictions, cookies assigned in the www subdomain of store.com are not transmitted to other subdomains of store.com such as support. This applies to domain names that are below (i.e., domain names that are subdomains of) the domain that set the cookie. For example, cookies set by www.store.com will not be sent to subdomains of www.store.com such as test.www.store.com. GET/faq.html HTTP/1.1 Host: www.other.com … HTTP/1.1 200 Ok … www.other.com:80 Figure 8-5 The item cookie from www.store.com is not sent to other domains. GET/Contact.aspx HTTP/1.1 Host: support.store.com … HTTP/1.1 200 Ok … support.store.com:80 Figure 8-6 By default, cookies are not transmitted to other subdomains. So what happens if Web sites in two different domains want to access each others’ cookies? Consider a company where Web applications on the press relations department’s Web site (pr.company.com) need to read cookie data from Web applications on the sales department’s Web site (sales.company.com). This can be accomplished with the Domain attribute for a cookie. This allows a Web application to specify what other domains can 208 HTTP COOKIES access a cookie. Figure 8-7 shows a page on sales.company.com that sets a cookie using the Domain attribute. Figure 8-7 The Sales department’s Web site can set a cookie that can be read by other Web sites in the *.company.com domain hierarchy. The Set-Cookie header for this response is Set-Cookie: sessionid=901-42-1861; Domain=.company.com; Expires=Fri, 06-Jun-2008 02:41:25 GMT; Path=/. The Domain attribute tells the Web browser that this cookie should be sent for any Web site whose domain ends with company.com. Figure 8-8 shows the PR department’s Web site reflecting the value of the sessionid cookie that was set in the sales.company.com domain and received from the Web browser. There is one major limitation of the Domain attribute: The domain name must contain at least two periods. This is sometimes referred to as the Two Dots Rule.4 For example .company.com is a valid Domain attribute value but .com or .net are not. This rule exists to limit cookie sharing to within subdomains of a subdomain of a top-level domain. This means that subdomains of company.com can share cookies but company.com and store.com cannot share cookies. If this rule didn’t exist, Web sites could set cookies that would get transmitted to every .com or .org Web site the 4 There is an exception to the Two Dots Rule: country code top-level domains (ccTLDs) such as .co.uk or .co.jp. Even though these contain two dots, they define domain access that allows all the Web sites in .co.uk or .co.jp to read each others’ cookies. For ccTLDs you much specify three dots like .store.co.uk. In the past, many Web browsers had bugs that allowed setting cookies for entire ccTLDs. For the most part these bugs have been fixed. 209 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE user visits! Sharing between high level Web sites would almost never be a good idea from a security and privacy perspective, and the browsers do not allow it. If you are designing applications that need to do this, you probably need to rethink your architecture. Figure 8-8 pr.company.com is able to access cookies from sales.company.com that have the appropriate Domain attribute. The access control for cookies also permits a developer to specify which folders inside of a domain have access to the cookie. This is accomplished with the Path attribute. Whereas the Domain attribute tells the Web browser, “Only send this cookie to Web pages whose domain ends in X,” the Path parameter tells the Web browser, “Only send this cookie to Web pages whose path starts with X.” For example, consider the situation in which http://www.store.com/Products/OldStock/index.php sets a cookie with an attribute Path=/Products/. Table 8-1 explains the reasons different Web pages will or will not receive this cookie. Table 8-1 URLs on store.com and whether they can access a cookie whose path is restricted to /Products/ URL Can access cookie? Reason http://www.store.com/Products/ Yes In allowed path http://www.store.com/Products/Specials/ Yes In allowed path https://www.store.com/Products/Specials/ Yes In allowed path, SSL version of site in same domain 210 HTTP COOKIES URL http://www.store.com/Products/New/ http://www.store.com/ http://www.store.com/Support/contact.php Can access cookie? Yes No No Reason In allowed path Path doesn’t start with /Products/ Path doesn’t start with /Products/ The final access control rule for cookies is that cookies can require that they be sent only over encrypted connections. Back in Figure 8-4, we saw that a cookie set by a Web server running on www.store.com:80 would be sent to an SSL-enabled Web server running on www.store.com:443. By this logic, cookies set by a Web application communicating with the browser over an encrypted connection would be attached to requests sent unencrypted to the Web server running on port 80! This inadvertently transmits data that is presumably supposed to be encrypted over an unencrypted channel. This could be extremely dangerous depending on what data is stored in the cookie. In general, if a cookie is being set by an application communicating over an encrypted connection, you should always assume it contains sensitive information that should never go out over the wire unencrypted. The Secure attribute tells the Web browser that this cookie should only be attached to requests that are transmitted over an SSL-encrypted connection. This will prevent transmission of the data over unencrypted channels. SECURITY RECOMMENDATION Don’t Don’t allow cookies from encrypted connections to leak into unencrypted connections. If a cookie is being set by an application communicating over an encrypted connection you should always assume it contains sensitive information that should never go out over the wire unencrypted. Do Do use the Secure attribute on all cookies set by Web pages communicating with the user over SSL to ensure the cookies are not accidentally sent in the clear. STORAGE CAPACITY OF HTTP COOKIES Assuming we decide to use cookies to persistently store arbitrary data on the client, how much data could we actually store? Well, RFC2109 defines how user agents and Web servers should implement cookie handling. It states, “in general, user agents’ cookie 211 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE support should have no fixed limits.” However, unlimited client-side storage isn’t reasonable for all devices, especially mobile devices with small storage capacity. The RFC does come down from its ivory tower of what should happen and gives some practical advice. It states that user agents “should provide at least 20 cookies of 4096 bytes, to ensure that the user can interact with a session-based origin server.” Unfortunately, the RFC is vague as to whether this means at least 20 cookies per domain with no more than 4096 bytes per cookie—or whether this means at least 20 cookies per domain and no more than 4096 bytes of shared space for all of the cookies in that domain. As is often the case with vague RFCs, the major browsers implemented cookies differently. Firefox allows a maximum of 4096 bytes for each cookie, and up to 50 cookies per domain. Internet Explorer allows up to 4096 bytes total, spread over a maximum of 20 cookies. This means you can have one cookie with 4096 bytes or 20 cookies with 204 bytes, but the cumulative size of all cookies for a domain cannot be larger than 4096 bytes. Actually, IE limits you even from using the full 4K. The length of the name and the length of the data combined, excluding the equals sign, must be less than 4094 bytes. This means IE is the lowest common denominator and thus Web sites can only safely store 4094 bytes per domain using cookies. As we have stated again and again, cookies were never intended to provide a mechanism for long-term data storage on the client. In addition to low storage capacity, this leads to another problem as well. Consider the cookie storage Web application shown in Figure 8-9. This application allows a user to store short notes persistently in a cookie on his local machine. In this case we are storing a quotation. Because the quotation is stored in a cookie, it is automatically added to every outgoing Web request that’s applicable for the cookie attributes. Figure 8-10 shows an HTTP proxy inspecting the Web browser’s requests. We can see that our quotation has been appended to a request for a style sheet. In fact, we will repeatedly send the quote to the server attached to every Web request we make. Every image we fetch, every external JavaScript, even every XMLHttpRequest we make. Even if we use the Path attribute of the cookie to try and minimize which requests are sending the quotation along with it, we are still spamming the server with needless information. Depending on how your Ajax application is designed, most of your XMLHttpRequests will likely be going back to the same directory the application is hosted in, thus preventing you from using Path to strip your XMLHttpRequests of the client-side storage cookies. 212 HTTP COOKIES Figure 8-9 A simple Web application that stores short notes in a cookie Figure 8-10 When data is stored persistently using cookies it is repeatedly and needlessly sent to the Web server. 213 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE To illustrate this more clearly, think of how cookie storage would apply in the real world. Using a cookie to store data is equivalent to remembering an errand you have to do after work by shouting it at the end of every sentence you say. It would sound something like this: Bryan: Hey Billy, what’s shaking? Billy: Hey Bryan. Just finishing this chapter on offline Ajax. Pick up Red Bull on the way home! Bryan: … … Uhhhhh, Ok. Why are you shouting that at me instead of writing it down? Billy: Because I chose a poor client-side storage methodology. Pick up Red Bull on the way home! Bryan: … … Ok, this is just weird. I’m leaving. Billy: You should be glad I can only store 4KB of data this way. Pick up Red Bull on the way home! First of all, this makes everything that Billy (the Web browser) has to say larger. Granted, the maximum size of this needless cookie storage baggage is only 4KB. While this is not even a hiccup for a cable modem, you should consider a GPRS mobile device where the shared stream bandwidth averages 21KB per second. Second, the data is completely pointless to the server. It doesn’t need to know what’s in client-side storage. If it did, the server would store it on the server! Third, it broadcasts everything that is stored on the client to anyone within listening distance on the network. This is a security risk because it allows an eavesdropper to steal data that is being stored in client-side storage simply by listening to your network traffic. For example, if you are on a wireless network, that means everyone who is on the network can see your Web traffic (unless you are using SSL). Not only can a passive observer see all the data that your Ajax application is storing on the client, she can also see how the data is changing. Just like you can measure the acceleration of a car by observing how its velocity changes, an attacker can follow what a user is doing with an application based on how the contents of the user’s client-side storage are changing. Consider a hypothetical WordPress plug-in for composing new blog posts. This plug-in will auto-save whatever text you have written at fixed intervals inside cookies to prevent you from losing your work. Any XMLHttpRequests or RSS feed updates 214 HTTP COOKIES going back to your site from your Web browser will contain the HTTP cookies that contain what you have already typed, but haven’t yet published. This will broadcast everything you are typing, as you type it, to anyone who can see your Web requests. Imagine sitting in a local Starbucks and someone stealing the article you are typing before you’ve even published it! LIFETIME OF COOKIES We know that cookies will be persistent or not depending on whether the Expires attribute was set on the cookie when it was created. Nonpersistent cookies are deleted as soon as the browser window is closed, and thus are useless for long-term data storage on the client’s machine. So, how long do cookies last? How reliable are cookies as a form of persistent storage? There have been various studies over the years with conflicting results. In March of 2005, Jupiter Research released a report stating 54 percent of Internet users have deleted cookies stored by their browser.5 In addition, the report found that 39 percent of users delete cookies on a monthly basis. However, in April of that year, Atlas Solutions released a report titled “Is the Sky Falling on Cookies?”, which gathered statistics by actually measuring how long a cookie stayed on a machine instead of how long the user says a cookie stays on his machine.6 There were interesting discrepancies. For example, 40 percent of users who said they deleted cookies weekly had cookies older than 2 weeks. Forty six percent of people who said they deleted cookies monthly had cookies older than 2 months. It should be said that Atlas Solutions sells products for online marketing, visitor impression and lead tracking, and Web site optimization. Uniquely tracking individual users, which is largely accomplished with cookies, is a key aspect of all of their business solutions. It is not surprising their report would find that cookie tracking is still a viable means of tracking individual users. However, even using Atlas’s potentially skewed data, we can learn a lot. According to their report, 39 percent of all cookies are deleted within 2 weeks of being created. And, 48 percent of all cookies are deleted within 1 month of being created. While these lifetime statistics are acceptable for tracking unique visitors to a site, they may be less acceptable for long-term storage of data, depending on what developers want to store on the client using cookies. Still, it is clear, developers must ensure their application is not dependent on data persisting on the client. 5 You can find more details at: http://www.jupitermedia.com/corporate/releases/ 05.03.14-newjupresearch.html. 6 The full report is available at: http://www.atlassolutions.com/pdf/AIDMIOnCookieDeletion.pdf. 215 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE ADDITIONAL COOKIE STORAGE SECURITY NOTES Developers must remember that cookies values are sent in HTTP headers. Certain values like carriage return and line feed (ASCII characters 0x0D and 0x0A) delimit different HTTP headers in a request or response. If you are using cookies as a client-side storage method, you must encode any data you are storing to prevent a malicious user from injecting his own HTTP headers into Web traffic through client-side storage data. Depending on how the application is written, a smart attacker can use unencoded cookie values to inject his own HTTP headers into a response. The headers can be used to poison caching proxies by adding cache directive headers or even replacing the entire response! This type of an attack is known as HTTP Response Splitting.7 A good rule of thumb is to use JavaScript’s escape() and unescape() functions to URL-encode all data you will be storing in a cookie as client-side storage. Please note that JavaScript’s escape() function will expand special characters like space, <, or > to a three-character escape sequence like %20. This expansion can further cut into the 4094 bytes you have to store data on the client using cookies. Developers must remember that cookie values are fairly easy to discover. Not only are cookie values broadcast on outgoing HTTP requests, but the browser often provides a visual interface to examine the cookie jar. We saw Firefox’s cookie jar window in Figure 8-1. Modifying cookies is fairly easy as well. Usually cookie jars are stored on the client as a simple text file. The most common format for this file is the Netscape cookies.txt format. This is a simple tab delimited file storing cookie name, value domain, expiration data, secure attribute, and other data. This file can be opened and modified with any basic text editor. In Figure 8-11 we see the cookies.txt file for Firefox open in Notepad. As we have reiterated time and time again, do not store secret or confidential data on the client. COOKIE STORAGE SUMMARY • Cookies can be persistent or nonpersistent. Nonpersistent cookies are immediately discarded when the browser is closed. By default, cookies are nonpersistent. • All persistent cookies automatically expire after some period of time. However, the expiration date can be set decades into the future. • By default, Web servers running on different ports of the same hostname can read each other’s cookies. 7 You can read Amit Klein’s paper on HTTP Response Splitting at: www.cgisecurity.com/lib/ whitepaper_httpresponse.pdf. 216 HTTP COOKIES • Developers must use the Path attribute to prevent broadcasting cookies to other pages. • Developers should only use the Domain attribute if absolutely necessary. If you have to share data, use the most specific domain name as possible. • Cookies are trivially easy to view and edit on the local machine. Nothing more sophisticated than a text editor is needed to do this. Most major browsers have no integrity checks to prevent cookies from being modified on the local machine. • All appropriate cookies are sent to the Web server on each and every request. Other computers in the same collision domain (very common in wireless networks) or any upstream computer are capable of seeing these cookie values. Figure 8-11 Attackers can view or modify cookie data with a simple text editor. 217 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE FLASH LOCAL SHARED OBJECTS8 Flash’s Local Shared Objects (LSOs) are data collections that are stored persistently on a user’s machine. Flash can programmatically store and retrieve large amounts of information inside these data collections. For example, Flash can store primitive ActionScript data types including objects, arrays, numbers, Booleans, and strings inside of an LSO.9 This means that developers do not need to implement their own data serialization/deserialization layer or encode their data in any way before storing it in LSOs. LSOs are not capable of storing Flash’s visual objects such as sounds, sprites, or movie clips. By default an LSO can store 100K. A user can allocate more space for a Web site, up to an unlimited amount, through the Flash player’s Settings Manager as seen in Figure 8-12. Figure 8-12 The Settings Manager allows the user to allocate space for LSOs or disable them all together. LSOs have been a feature of Flash since version 6 was shipped in March 2002, and are, as of this book’s printing, installed on roughly 97 percent of Web users’ machines.10 Their high storage capacity, ability to persist data across reboots, and large market penetration make LSOs an attractive method for long-term data storage. 8 This book focuses on detecting and fixing security vulnerabilities in Rich Internet Applications (RIA) written in JavaScript. Doing this with other RIA frameworks such as Flash, Apollo, or Silverlight is beyond the scope of this book. However, many Ajax applications use Flash and its LSOs to persist data solely for the purpose of storing large amounts of data on the client. We confine our discussion in this section solely to security issues of using Flash as a data storage system. 9 ActionScript is the programming language in which Flash programs are written. 10 Adobe tracks Flash’s browser penetration in various markets here: http://www.adobe.com/ products/player_census/flashplayer/version_penetration.html. 218 FLASH LOCAL SHARED OBJECTS LSOs are sometimes called Flash cookies or super cookies because of the large amounts of data they can store. However, LSOs differ from traditional HTTP cookies in several ways. First of all, LSOs are managed by the Flash Virtual Machine. The browser has no way to access them. This means that unlike cookies, data stored in an LSO is not automatically attached to outgoing HTTP requests. Consequently, it is not possible for an HTTP response header to modify the data inside an LSO the way a Set-Cookie header can modify an existing cookie. Because a plug-in and not the browser manages LSOs, users cannot delete LSOs using the Web browser’s feature to clear offline content or cookies. As a result, LSOs can survive on a machine owned by a privacy-conscious user who routinely clears the cookies and the browser cache. LSOs also cannot expire the way cookies can. The concept of expiring data simply doesn’t exist in the LSO model. LSOs are stored indefinitely until they are purposely deleted. This can be done through the Flash Player’s Settings Manager, as shown in Figure 8-13, or by removing the actual files that contain the LSOs. Figure 8-13 Individual LSOs or all LSOs can be deleted through the Flash Player’s Settings Manager. This raises the question: How are LSOs actually stored on the client’s machine? LSOs are stored inside of files with a .sol file extension located in a special directory called #SharedObjects on the user’s machine. For Windows XP machines, the location is C:\Documents and Settings\\Application Data\Macromedia\ Flash Player\#SharedObjects. On Linux machines, this directory is located at ~/.macromedia/Flash_Player/#SharedObjects. Inside the #SharedObjects directory is a single directory with a random name of 8 alphanumeric characters. With 2.8 trillion possible values, this random name is a security feature that prevents everyone from having a single well-known LSO storage folder location.11 It is inside this randomly named 11 Mozilla-derived browsers use a similar security method by randomizing the folder names of user profiles. 219 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE directory that LSOs are stored. First, a directory is created for the hostname that served the Flash object that created the LSO. Inside the hostname directory are more folders representing the directory path to the Flash object that created the LSO. Figure 8-14 shows how the .sol file holding an LSO created by flash.revver.com is stored on the local machine. Figure 8-14 A Flash LSO is stored inside the #SharedObjects folder according to the location of the Flash object that created it. In this example, the original page we went to was http://one.revver.com/watch/285862 on the video-sharing site Revver to get our lonelygirl15 drama fix. On that page is an OBJECT tag linking to the Flash player for Revver, located at http://flash.revver.com/player/ 1.0/player.swf. This Flash object saved data into the LSO stored in revverplayer.sol in Figure 8-14. We can see that the location of revverplayer.sol on the local file system C:\Documents and Settings\(USER_NAME)\Application Data\Macromedia\Flash Player\#SharedObjects\(RANDOM_NAME)\flash.revver.com\player\1.0\player.swf\revver player.sol. As you can see, the path to the Flash object on the Web server that stored the Flash object mirrors the path to the LSO on the local machine. In this case the path is \player\1.0\player.swf\. Notice that the name of the Flash object is actually the last directory in the path under which the LSO is stored. This allows a single Flash object to save different LSOs under different file names. The final thing to notice is the LSO is stored 220 FLASH LOCAL SHARED OBJECTS under the hostname in which the Flash object resides, not the Web page that referenced the Flash object. So can any other Flash objects access the LSO stored by player.swf in our example? The answer is: No. The Flash object that stored the LSO is part of the path to the .sol file. Even if there were another Flash object, OtherFlash.swf, served from the same directory as player.swf on flash.revver.com, it could not access the revverplayer.sol because it would be looking in the wrong directory. There is no way for OtherFlash.swf to access \player\1.0\player.swf\ on the client machine. The default security policy for LSOs concerning data sharing is stricter than the security policy for cookies. Like cookies, by default LSOs prevent other domains from accessing the data stored in the LSO. The policy is stricter in that objects that did not create the LSO cannot access it. This is analogous to setting the Path attribute on a cookie. In Flash’s case, however, the filename of the Flash object that created the LSO is part of the Path attribute. As we have seen, including a Flash object’s filename in the path to the disk location where the LSO is stored prevents other objects from accessing it. For two Flash objects to be able to share data stored in an LSO, we must specify that the LSO is stored in a path that doesn’t include the filename of the Flash object that created it. We can accomplish this task by specifying a storage path for the LSO when it is created. In ActionScript, this is accomplished by passing a string containing the desired path as the second parameter in the SharedObject.getLocal() function call. This is the function call that creates an LSO. Only paths that are below the path of the Flash object are allowed. This is best illustrated with an example. Consider a site with two Flash objects. foo.swf is hosted at http://site.com/media/some/dir/foo.swf and bar.swf hosted at http://site.com/media/other/ dir/bar.swf. In this scenario, there are four available paths where foo.swf can read or write LSO: /media/some/dir/, /media/some/, /media/, or /. This is in addition to /media/some/dir/foo.swf/, which only foo.swf can access. Similarly there are four available paths where bar.swf can read and write LSOs: /media/other/dir/, /media/other/, /media/ or /, plus the bar.swf only location of /media/other/dir/bar.swf/. Thus any LSOs that foo.swf or bar.swf writes into the /media/ or / paths can be read by the other Flash object and vice versa. Continuing our comparison with cookies, LSOs also have a secure flag that is analogous to a cookie’s Secure attribute. This flag is controlled by passing a Boolean as the third argument to the SharedObject.getLocal() function call when creating the LSO. When this argument is set to true, only Flash objects that were served over a secure connection are capable of accessing the LSO. With the path parameter, the secure flag provides means to limit which Flash objects on the same server can access which LSOs. However, LSO data is not automatically appended onto all outgoing HTTP requests the 221 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE way cookie data is, so the secure flag is not as important to protect data leakage as it is with cookies. Rounding out our cookie comparison is the Flash equivalent of the Domain attribute. Flash allows a concept known as cross-domain scripting.12 The implications of crossdomain scripting are far beyond sharing client-side storage across multiple domains. Cross-domain scripting provides a mechanism for Flash objects from one host to load a Flash object from another host and access its internal functions and variables! This is an elective and one-way process. If Flash1.swf on http://site.com wants to access the data inside Flash2.swf on http://other.com, then Flash2.swf must explicitly allow Flash1.swf to do so. Even if Flash2.swf grants this permission to Flash1.swf, Flash2.swf cannot access the internal contents of Flash1.swf unless Flash1.swf also explicitly allows Flash2.swf to do so. Flash2.swf can grant cross-domain permission in two different ways. The first method is to grant permission in the code of the Flash object itself, using the System.security.allowDomain() function. This allows each individual Flash object to control cross-domain permissions. In our example, Flash2.swf on http://other.com would include the ActionScript statement: System.security.allowDomain('site.com'). A programmer can add cross-domain scripting privileges to multiple domains by repeatedly calling this function for different domains. Entire subdomains can be added by using * as a wildcard character. For example, System.security.allowDomain('*.site.com') grants cross-domain scripting permission to all subdomains of site.com, such as press.site.com or qa.site.com. Domain permissions in Flash do not follow the Two Dots Rule like the Domain attribute for cookies. This means programs can use System.security.allowDomain('*') to grant cross-domain permission to the entire Internet! The security implications of this are quite clear. You are granting anyone from any Web site access to the variables and functions of your Flash object. The second method to grant cross-domain scripting permissions is by using a global policy file. The global policy file performs the same function as System.security.allowDomain()calls, but it grants permissions for all Flash objects hosted on a domain. This policy is usually stored in the crossdomain.xml file in the Web root of a site. The following is the cross-domain policy for Amazon.com (located at http://www.amazon.com/crossdomain.xml). 12 Do not confuse Flash’s cross-domain scripting with the security vulnerability cross-site scripting, discussed in Chapter 3. They are not related to each other in any way. 222 FLASH LOCAL SHARED OBJECTS We see that all Flash objects from all subdomains of six different countries can access each other. Oddly, there are redundant listings in the policy: both www.amazon.com and images.amazon.com entries are covered by the *.amazon.com entry. Using (or its evil step-sisters *.com, *.net, or *.org) in a global policy can be extremely dangerous. The Flash Virtual Machine (VM) checks the permissions hard coded inside a specific Flash object in addition to the permissions in the crossdomain.xml file. In other words the final permission set used is the union, and not an intersection, of the permissions defined inside the Flash object and the permissions in crossdomain.xml. Consider a Flash object called MembersAPI.swf that uses System.security.allowDomain('members.site.com') to ensure that only other trusted Flash objects hosted on members.site.com have permission for cross-domain scripting. Now, let’s say an IT administrator or another Web developer deploys a crossdomain.xml file that allows access to “*”. Suddenly any site on the Internet now has permission to access the previously protected MembersAPI.swf. Even worse, the original Web developer has no idea the site is now in danger because the application continues to function as normal! This is not an abstract or hypothetical situation. In August of 2006, noted Web security expert Chris Shiflett discovered and demonstrated the full scope of this danger.13 He noticed that the popular photo-sharing site Flickr had a cross-domain policy with an entry , allowing the entire Internet remote scripting access to the Flickr’s Flash object. With the help of Julien Couvreur, he created a Web 13 For the full story see: http://shiflett.org/blog/2006/sep/the-dangers-of-cross-domain-ajax-with-flash. 223 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE page on his own domain with a Flash object that used cross-domain scripting to load a Flash object on Flickr that manages a Flickr user’s friends list. When a Flickr user would visit Chris’s page, his evil Flash object could instruct the Flickr Flash object to add Chris to that user’s friends list. This is very similar to the Samy worm, which we discuss in Chapter 13 “JavaScript Worms.” Chris was able to use Flickr’s code to do evil things because they made the mistake of sharing it with the entire Internet! If you are creating an application that must use entire Internet ( “*”) cross-domain permissions, you should probably rethink your application’s architecture. Developers should know which domains need to access their Flash objects unless they are offering some kind of public API as in a Web mashup (see Chapter 11, “Web Mashups and Aggregations”). Even in those conditions, you should isolate any globally accessible Flash objects on their own separate domain such as api.host.com. This will protect Flash objects hosted on the rest of your domains and subdomains. This is the approach Flickr took to solve the security issue Chris Shiflett discovered. Figure 8-15 shows how the hypothetical online bookstore BillysBooks.com provides a public API for venture capital mashups while protecting the Flash objects that handle sensitive user functions. PublicAPI.swf doSearch(q) prodImage(id) getReviews(id) … Members.swf getMessages(q) addToCart(id) addToWishList(id) … api.BillysBooks.com Allow From:* www.BillysBooks.com Allow From: www.site.com evil.com mash-up.com evil.com Figure 8-15 Isolate globally accessible Flash objects in a separate subdomain to protect other sensitive functions and data. 224 FLASH LOCAL SHARED OBJECTS The .sol file format that stores the serialized version of an LSO has been reverse engineered and is well-understood. There are numerous open source tools that can read and modify the contents of an LSO. Developers cannot trust any data they retrieve from client-side storage and must validate it before consuming it. In Figure 8-16 we are using Alexis Isaac’s open source Sol Editor to modify an LSO. This LSO was used by a Web site to track and store when a visitor’s trial membership started. By editing this date we always have a valid trial membership. This is an actual example from an adult Web site the authors found in the wild while performing a security assessment. Figure 8-16 Attackers have an arsenal full of free tools to view or edit Flash Local Shared Objects (LSOs). FLASH LOCAL SHARED OBJECTS SUMMARY • LSOs are persistent. Developers can emulate nonpersistent storage by implementing code to clear out LSO data using the browser’s unload() events. • LSOs cannot be configured to expire automatically. Developers are responsible for implementing this feature. 225 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE • By default, LSOs are only accessible by the Flash objects that created them. Programmers must explicitly create the LSO to be accessible by other Flash objects within the same domain. • Use the Path parameter to SharedObject.getLocal() only if you absolutely must share data between different Flash objects. Consider sequestering all Flash objects that must share data inside a single directory. • Flash’s cross-domain scripting can be very dangerous. Developers must be careful about which domains they allow access to. • LSOs can store complex data structures such as arrays, objects, and Booleans. The Flash Player takes care of serializing and deserializing the data. • The contents of LSOs can be easily viewed or edited with a specialized tool. There are many of these tools, such as Alexis Isaac’s open source Sol Editor. There are no built-in integrity checks to prevent tampering with LSOs. DOM STORAGE DOM storage is the Mozilla implementation of the client-side data storage features defined in the unofficial HTML 5 specification. HTML 5 is a working draft created by the Web Hypertext Application Technology Working Group (WHATWG). It is not an official standard, but that is often the way things work in the Web world. DOM storage provides JavaScript objects that can be used to store data persistently using the globalStorage object and nonpersistently using the sessionStorage object. DOM storage seems like a rather weird and confusing name for these features. WHATWG refers to them as client-side session and persistent storage of name/value pairs. DOM storage is actually an internal name for the features that Mozilla chose simply because other names like mozStorage, Storage and sessionStorage were already used! However, looking deeper, calling these features DOM storage starts to make a little bit of sense. For example, the sessionStorage object deals with storage data on a specific domain for the lifespan of a single browsing view (be it a window or tab).The globalStorage object holds information persistently for specific domains. The JavaScript language has no concept of URLs, HTML documents, or even support for reading from or writing to data sources. Storage systems that are keyed on domain names don’t fit neatly into this mold. Thus, much like alert dialogs, confirmation boxes, and timers, DOM storage is a feature you don’t associate with Web browsers, but is, in fact, provided by the Web browser environment. Just be happy you don’t have to call it client-side session and persistent storage of name/value pairs! 226 DOM STORAGE It is important to stress that DOM storage has nothing to do with storing data in hidden INPUT tags in an HTML page. This approach is commonly taken in Web applications as a way to store data temporarily on a single Web page. To store it across multiple pages, the Web server must make sure to include the hidden INPUT tag in each and every page it writes to the client. In contrast, DOM storage functions just like other objects in JavaScript: by storing and retrieving properties of the respective objects. SESSION STORAGE Session storage is a nonpersistent storage area that is shared among all pages from the same domain that are opened in the same browser window.14 Once the browser window is closed, the session storage data is automatically discarded. Figure 8-17 illustrates the most basic usage of session storage. http://site.com/items.php Session Storage: Owner = ‘Billy’ Lang = ‘en-us’ http://site.com/index.php Session Storage: Owner = ‘Billy’ Lang = ‘en-us’ http://site2.com/links.asp Session Storage: (empty) Figure 8-17 Session storage is domain-specific, and different domains cannot access each other’s data. Here we see that pages items.php and index.php from site.com both have JavaScript access to the same name/value pairs Owner and Lang. When the user navigates to site2.com, the session storage for that domain is loaded (in this case it is empty). site2.com cannot access the data stored in site.com’s session storage. Figure 8-18 shows what happens when a user who switches from site.com to site2.com finally navigates back to site.com, all inside the same browser window. As you can see, when the user returns to site.com, the previous session storage instance is loaded. This behavior is identical to nonpersistent cookies. Figure 8-19 shows how session storage works across multiple browser windows. 14 We use the terms window and tab interchangeably here. If I have a single window for my Web browser open, but that window has multiple tabs, each tab has its own Session Storage object. 227 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE http://site.com/items.php Session Storage: Owner = ‘Billy’ Lang = ‘en-us’ http://site2.com/links.asp Session Storage: (empty) http://site.com/index.php Session Storage: Owner = ‘Billy’ Lang = ‘en-us’ Figure 8-18 Session storage lasts for the life of the browser window. Returning to a site allows access to previously stored session storage values. http://site.com/items.php Session Storage: Owner = ‘Billy’ Lang = ‘en-us’ http://site2.com/links.asp Session Storage: (empty) http://site.com/index.php Session Storage: Owner = ‘Billy’ Lang = ‘en-us’ Open In New Window Or Tab http://site.com/items.php Session Storage: (empty) http://site.com/show.php Session Storage: useFlash=‘false’ Figure 8-19 Session storage data is unique to each domain in each browsing window. To understand Figure 8-17, it is best to visualize how the browser manages different session storage instances. Each browsing window maintains a table of session storage objects. Each session storage object in the table is associated with a domain name. The domain of whatever Web page the user is currently viewing selects which session storage object is exposed to JavaScript. If JavaScript tries to read or write to a session storage object and there is no session storage object for that domain, a new empty session storage object is created. Under this model, you can understand how the session storage associated with site.com can contain different data for two different browsing windows. This also explains why the session storage for site.com is originally empty when opened in a new window. 228 DOM STORAGE Session storage is implemented in Firefox through the appropriately named property sessionStorage on the window object. The following code snippet shows how to use the sessionStorage object in a Firefox browser. window.sessionStorage.lang = 'en-us'; sessionStorage.timeZone = 'UTC-5'; sessionStorage['textDirection'] = 'rtl'; sessionStorage.setItem("time", "military"); alert(sessionStorage.lang); //displays "en-us" As we can see, the sessionStorage object can be manipulated much like traditional JavaScript objects. You can add your own data to it using by supplying a name and value pair. As with other objects, you can use object.name = value notation or assign the value using associative array notation such as object['name'] = value. There is also a setItem() function that you can use. The sessionStorage object is a property of the window object that is the global context object for JavaScript running inside a browser. Thus, you can access sessionStorage with or without the window object prefix. Session storage combines features from Flash LSOs with features from cookies to create the best choice for nonpersistent data storage on the client. Like nonpersistent cookies, session storage data is discarded by the browser automatically. This prevents old, forgotten, and potentially confidential data from lying around on a user’s machine. Like LSO, session storage has a much higher store capacity than cookies. Furthermore, session storage has the data privacy properties of an LSO: Its stored data is not broadcast to the world with each and every HTTP request. The only real drawback of session storage is that it is only currently implemented in Mozilla-based browsers. GLOBAL STORAGE Global storage is a persistent storage area that is shared among all browser windows for the same domain. There is no built-in mechanism to automatically expire any data stored in global storage. Mozilla implements global storage through the JavaScript object globalStorage. This object, like sessionStorage, is a property of the window object. The following code snippet shows how to use the globalStorage object. You will notice that, as with the sessionStorage object, you can manipulate the globalStorage object using name and value notation, associative array notation, or by using the setItem() function. globalStorage[location.hostname].shoppingCart = '8471'; globalStorage[location.hostname].shoppingCart += ', 7032'; 229 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE globalStorage[location.hostname][ 'shoppingCart'] += ', 2583'; globalStorage[location.hostname].setItem("coupons", "no"); alert(globalStorage[location.hostname].coupons); //displays "no" When accessing the globalStorage object, developers must provide a domain key. The domain key is in the form of a domain name and must follow the Two Dots Rule like the Domain attribute for HTTP cookies.15 For example, the Web page http://www.sales.company.com/page.html can use company.com, sales.company.com, or www.sales.company.com as a domain key. Sharp-eyed readers will notice that the domain key company.com doesn’t actually have two dots. While the literal requirement of the Two Dots Rule to have two periods in the domain key does not hold true, the spirit of the rule (that we need at least a domain name and a top-level domain name in the domain key) applies. Using a parent domain as a domain key is acceptable as long as the domain key follows the Two Dots Rule. This enables Web pages on different subdomains to share the same data through the globalStorage object. Recall our cookie-sharing example from earlier in the chapter (Figures 8-7 and 8-8) in which a company’s press relations department’s Web site pr.company.com wants to share data with the sales department’s Web site sales.company.com. If Web pages from both subdomains use company.com as the domain key for the globalStorage object, they will be able to access each other’s data. Valid domain keys for a Web page also include any subdomains of the current domain, even if those subdomains don’t really exist. Continuing our example, neverneverland.www.sales. company.com or even not.going.to.find.me.www.sales.company.com are valid domain keys for the Web page http://www.sales.company.com/page.html. However, Web pages on a subdomain must use domain keys, either their parents’ domain names or subdomain names need to be present in their domain name hierarchy. So while http://www.sales.company.com/page.html can use company.com, sales.company.com, www.sales.company.com, or even neverneverland.www.sales.company.com as a domain key, 15 Actually, the WHATWG HTML 5 draft allows for public storage using a TLD such as com as the domain key or even using an empty domain key ‘’. This feature creates a storage area accessible to every site on the Internet. Third-party advertisers can use public storage to track users across different Web sites and build very detailed profiles about their browsing habits. If a tracked user visits a site where he has an account or other personal information, there is a danger the third-party advertisers could associate specific Web browsing activity with a specific personally identifiable human. While public storage does create the possibility for some interesting cross-site applications, the potential for massive privacy violations caused Mozilla developers to exclude public storage from their implementation of DOM storage. We feel that while the WHATWG has done a good job collecting suggestions that attempt to mitigate the dangers of global storage, most of these solutions do not adequately protect the user. We sincerely hope other Web browsers that implement DOM storage follow Mozilla’s example and exclude this dangerous feature. 230 DOM STORAGE it may not use the domain keys pr.company.com or magic.pr.company.com or othercompany.com because none of those domain keys are within the domain hierarchy of http://www.sales.company.com/page.html. Attempting to access these illegal domain keys will cause JavaScript to throw a security exception. THE DEVILISH DETAILS OF DOM STORAGE Based on everything you have seen so far, you might assume that sessionStorage and globalStorage act just like any regular JavaScript objects that are created with a line of code like var obj = new Object();. In fact, sessionStorage and globalStorage are special objects that implement the storage interface defined by WHATWG. Mozilla does such a good job masking the gritty details for the storage interface that you might not even know the difference. Unfortunately, they did such a good job that Web developers can easily misuse these objects and create insecure code. Even though you access the DOM storage objects like normal JavaScript objects, you cannot overwrite them with new objects that attempt to clear their contents because they are supplied by the browser environment. Calls to globalStorage["site.com"] = new Object() or sessionStorage = new Object() will not clobber the old object and delete the data in DOM storage. Instead, the contents of DOM storage remain untouched and JavaScript will throw an exception. As we mentioned in Chapter 5, “Ajax Code Complexity,” runtime errors are tough to track down because they only occur under certain situations. Even worse, the data you were trying to remove is not deleted! This brings up an interesting dilemma: How do you remove the data from DOM storage? Neither sessionStorage nor globalStorage have a clear() function. Instead, developers must loop through all members of the storage objects and invoke the delete operator or the removeItem() function to remove each name/value pair. Removing unnecessary data from sessionStorage is not as important as doing so for globalStorage because the browser will automatically discard the data inside sessionStorage when the browser is closed. However, there are situations in which you will want to clear sessionStorage. Consider a user who is logged into a Web site and doesn’t close her Web browser. If she returns to the browser after a long period of time, the session she established with the server should have expired after a reasonable amount of time. Any information that is in the sessionStorage object is still present even though she is now logging back into the application and reestablishing her session state. In this case, a programmer should purge all data in sessionStorage and repopulate it with new data that is only relevant to the new session state. This prevents reuse of old data associated with a session state that no longer exists. The following JavaScript code contains functions to remove data for both DOM storage methodologies. 231 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE function clearSessionStorage() { for(var i in sessionStorage) { delete sessionStorage[i]; } } function clearGlobalStorage() { var name = window.location.hostname; for(var i in globalStorage[name]) { delete globalStorage[name][i]; } } sessionStorage.setItem("owner", "Billy"); sessionStorage.setItem("lastPage", "/products/faq.php"); globalStorage[location.hostname].wearingPants = "Nope"; alert(sessionStorage.length); //displays 2 clearSessionStorage(); alert(sessionStorage.length); //displays 0 alert(globalStorage[location.hostname].length); //displays 1 clearGlobalStorage(); alert(globalStorage[location.hostname].length); //displays 0 Another way the sessionStorage and globalStorage objects differ from regular JavaScript objects is that the value of their name/value pairs cannot be arbitrary data types; they must be strings. This forces the Web programmer to serialize and deserialize other data types as it moves in and out of storage. As mentioned in the “Validating Serialized Data” section of Chapter 4, “Ajax Attack Surface,” this places more work on the programmer, who needs to take steps to validate the input before it is consumed by the user. Unfortunately, Firefox hides this strings only limitation of DOM storage by automatically converting data for you. This can create some odd situations. Consider the following code snippet. sessionStorage.useFlash = false; //... if(sessionStorage.useFlash) { //use Flash for something sexy... } else { //boring HTML for you } 232 DOM STORAGE When sessionStorage.useFlash = false; executes, the browser automatically converts the Boolean value false into the string false. Thus the string false is stored in the sessionStorage object under the key useFlash. When sessionStorage.useFlash is called, the browser transparently converts this to the function call sessionStorage.getItem("useFlash"), which returns an object. This object is a StorageItem object and is used internally to track properties of the data stored inside DOM storage. To get the actual string value that was stored, developers need to call ToString() on the object returned by getItem(). However, the person who coded this code snippet forgot. Instead, the if statement conditional becomes if this object is true, meaning if the object is valid or defined. Because the object is defined, the conditional evaluates to true and the code that does something with Flash is executed! This most certainly is not what the code looks like it will do, and this behavior is caused entirely by the browser. After all, the developer explicitly set useFlash equal to false. When does false equal true? When the browser converts a Boolean to a string to an object without any obvious signs to the user or developer! Certainly we are not saying this automatic conversion is a security vulnerability. However it makes it very easy to write buggy JavaScript code that could create unintended and potentially insecure behaviors. DOM STORAGE SECURITY There are several issues that Web developers need to be aware of when using DOM storage. Session storage and global storage are defined on a per domain basis, and all Web pages on that domain have access to the same storage objects. This means that all Web pages on a host can read or overwrite any data that was stored in the sessionStorage object or the globalStorage object by any other Web page on that host. In plain terms there is no DOM storage equivalent of a cookie’s Path attribute. There is no DOM storage equivalent of a cookie’s Domain attribute. DOM storage objects for host.com are accessible by JavaScript server from any other service running on that host. Data stored in global storage is easily viewed or modified. Mozilla uses a SQLite database located in the file webappsstore.sqlite in a user’s profile directory. On Windows XP, this is located at C:\Documents and Settings\USER_NAME\Application Data\Mozilla\ Firefox\Profiles\RANDOM_NAME\. In Figure 8-20 we are browsing the contents of global storage using the open source tool SQLite Database Browser. This is still further proof that that no data stored on the client is safe from inspection or tampering. 233 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE Figure 8-20 Mozilla implements global storage as an easily editable SQLite database. DOM STORAGE SUMMARY • DOM storage provides both persistent and nonpersistent storage options. • Persistent data stored in DOM storage cannot be configured to expire automatically. Developers are responsible for implementing this feature. • By default, the same DOM storage system is accessible by every Web page on a domain regardless of the path. • Persistent DOM storage can be shared across subdomains using a common domain key. This is similar to the cookies’ Domain attribute. Developers should always use the most specific domain key possible. • DOM storage can only store strings. Mozilla will automatically convert other data types to strings. This can have unintended and potentially dangerous consequences. The developer is responsible for serializing and deserializing complex data types and all the security risks that action entails. See Chapter 4, for more details. • Persistent DOM storage can be viewed and edited using SQLite tools. There are no built-in integrity checks to prevent tampering with DOM storage. 234 DOM STORAGE INTERNET EXPLORER USERDATA Microsoft released a persistent client-side storage feature with Internet Explorer 5 known as userData. It is implemented using proprietary extensions to CSS behaviors. These extensions are a nasty, completely nonstandard and rather counterintuitive relic from the browser wars in the late 90s. Its obscurity, difficulty, and browser-specific nature led few Web developers to use it and most developers to be completely unaware of its existence. IE’s userData has the capacity to store complete XML documents. Complex data types can be converted to XML and stored inside of userData. In this approach, data is inserted into an XML data island (another IE-specific feature). Then, the entire XML data island is stored in userData. However, storage abstraction frameworks like Dojo.Storage mask these XML features of userData and typically only expose name/value pairs as strings. In certain situations userData can store much more data than other client-side storage methods. Internet Explorer imposes a per page data limit, as well as a limit for the entire domain. Attempts to store more data than is allowed will cause a catchable JavaScript exception. Table 8-2 shows the capacity for userData in Internet Explorer’s different security domains. Table 8-2 userData’s storage capacity in various Internet Explorer security zones Security zone Page limit Domain limit Intranet 512KB 10MB Local Machine, Trusted Sites, Internet 128KB 1MB Restricted 64KB 640KB The two most relevant domains are Internet and Intranet. IE’s native store for normal sites on the public Internet is larger than the default size of Flash’s LSOs, but smaller than Mozilla’s DOM storage. userData’s storage capacity for Intranet applications is completely unmatched by other methods. With 10MB of data, it is common for intranet applications to use userData as a cache for entire data tables, tree structures, and other larger structures. Developers must remember that userData is a persistent storage system and not a memory-resident cache that is discarded when the browser is closed. The developer must take extreme care when caching large generic data structures. Even if the developer explicitly avoids storing sensitive or confidential information in userData, the data inside of generic data structures could be sensitive, providing a back door to inadvertently storing confidential data persistently. Figure 8-21 shows a sample Web application that stores and retrieves data persistently using Internet Explorer’s userData feature. 235 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE Figure 8-21 Sample application storing a secret in userData under the name “secr3t.” Because name/value string pairs are stored as XML node attributes in the userData XML document, Internet Explorer automatically converts certain characters that have special meaning in XML to their corresponding XML character entity. For example, the double quote character (") is replaced with " and the ampersand character (&) is replaced with &. Developers need to be aware that this automatic encoding increases the size of the data they are trying to store. They must account for the possibility that a string that should fit inside userData will not be stored because it is too large when encoded. Data sharing with userData is extremely limited. You cannot share data between different domains or even subdomains of the root domain. You cannot share data with other Web servers on different ports of the same hostname. You can only share data between Web pages inside the same directory on the same domain. For example, data stored by http://company.com/Storage/UserData.html can be accessed by http:// company.com/Storage/Checkout.html or any other page inside the /Storage/ directory. Attempting to access data from other pages simply returns null. These are the default restrictions and cannot be changed. This default closed policy is almost the exact opposite of the default cookie policy. This constitutes one of the few good security decisions in Internet Explorer 5. There is no programmatic method to delete all the data stored inside of userData storage. You can delete name/value pairs one at a time using the removeAttribute() function on the HTML element with the .userData style. Unfortunately there is no easy way to enumerate through all the name/value pairs actually inside of userData. 236 DOM STORAGE Presumably the developer should know all the name/values that are in client-side storage. However, we are all human and the lack of a clean clear() function like the code we created for purging DOM storage inevitably means that data is going to be left in userData storage long after it should have been deleted. The expires property on the userData element helps somewhat by allowing a developer to set a date when the data will be discarded automatically. By default all data stored in userData never expires. Internet Explorer provides no indication of when a Web site is using userData to store information on the client. There is no mention of userData anywhere inside the GUI, and clearing the browser’s cookies, cache, history, or offline content will not delete any userData stored on a machine. All of these factors increase the chance that stale data will persist on the client’s machine for long periods of time. Developers must ensure their applications remove data as soon as it is no longer needed. Viewing the information stored in userData is tricky, but doable. First you have to turn on “Show hidden files and folder” and uncheck “Hide protected operating system files” under Folder Options in Windows Explorer. Next you navigate to the userData folder. On a Windows XP machine this is located at C:\Documents and Settings\ USER_NAME\UserData. userData is stored inside of XML files, but the XML files are stored using the same caching mechanism that Internet Explorer uses for its browser cache. In this system, an index file named index.dat holds entries with metadata about all the items that are saved. The individual items (in this case XML files of userData storage from different domains) are stored in one of five randomly named folders. You can locate the XML file for a particular userData storage system by examining the index.dat file or by simply checking all the XML files in the folders. Figure 8-22 shows the XML file containing our userData storage system from Figure 8-21. Figure 8-22 Using a hex editor to view the XML file holding the name/value pair that we stored in Figure 8-21. 237 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE Editing the information stored in userData is a complex task. You cannot simply edit the XML file in the cache folders directly. If you do, JavaScript will throw a malformed data exception when you attempt to load the modified data. This means there is some kind of hash or length of the XML file stored inside the index.dat file. Unfortunately index.dat is not an open file format. There are only a few Web sites on the Internet with detailed information about its internal structure.16 After a late night with lots of trial and error, we were able to discover that the length of the XML file is indeed being stored inside index.dat. Notice, in Figure 8-23, the +0x20 offset of an item’s entry in index.dat holds the file length for that entry. In this case the value is 136 bytes; exactly the length of our XML file containing the persistent userData! Figure 8-23 The entry in index.dat shows the original length (136 bytes) of the XML file containing the name/value pair set in Figure 8-21. Now, an attacker can modify data stored persistently with userData. He can edit the XML file as much as he wants, as long as he goes back and updates the index.dat file to have the correct length. In Figure 8-24 we are modifying the secret password for the Web application from Figure 8-21; and, in Figure 8-25 we are overwriting the old XML file length in index.dat with the length of the modified file. 16 The wiki at www.latenighthacking.com/projects/2003/reIndexDat/ was extremely helpful. The authors thank Louis K. Thomas for all his work. 238 DOM STORAGE Figure 8-24 Changing the secret password stored in the XML contents of userData Figure 8-25 Editing the length in index.dat to reflect the new length of the userData XML file modified in Figure 8-24 Finally, in Figure 8-26 we open Internet Explorer and confirm that our hacked value is loaded from userData storage into the Web application! Again, we must reiterate that every form of client-side storage can be viewed and modified by a user. Developers must never trust data they retrieve from client-side storage. 239 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE Figure 8-26 The hacked value inside userData was successfully loaded by the application without any indication something nefarious was afoot! SECURITY SUMMARY • userData provides persistent storage. Developers can emulate nonpersistent storage by implementing code to clear out userData using the browser’s unload() events. • userData can be explicitly configured to expire automatically. By default, data does not expire. • userData is only accessible to other Web pages inside the same directory on the same Web server running on the same port number. There is no way to change this. There is no way to share data between domains. • userData is capable of storing XML or strings. The developer is responsible serializ- ing and deserializing complex data types into one of these formats. • The contents of userData can be viewed with a text editor. Editing userData requires a hex editor. GENERAL CLIENT-SIDE STORAGE ATTACKS AND DEFENSES Now that we have examined some of the security concerns specific to different clientside storage methods, we discuss general attacks that apply to all methods. Cookies, LSOs, DOM storage, and userData all have various access control rules that determine 240 GENERAL CLIENT-SIDE STORAGE ATTACKS AND DEFENSES which Web resources have access to a shared storage space. Developers who fail to restrict which resources can access the storage space can leak access to untrustworthy sites or Web pages. These pages can steal or modify the client side data! Consider the situation of Sam, who is using an online word processor served from http://sexywebapps.com/WebWord/ that stores his three most recent documents on the client using DOM storage’s globalStorage object. Eve creates a Web page: http://sexywebapps.com/eves-evil/. If Sam ever navigates to Eve’s site, Eve can access Sam’s documents because http://sexywebapps.com/eves-evil/ has access to the same shared storage space that http://sexywebapps.com/WebWord/ uses. In this section we explore three different ways access to shared storage is leaked to untrustworthy applications and show developers how to secure their applications against these vectors. CROSS-DOMAIN ATTACKS Cross-domain attacks are possible when you share access to a client-side storage system with applications on other subdomains, inadvertently providing access to subdomains beyond your control. The vulnerability is caused by developers not properly restricting which domain names have access to the storage system. Cross-domain attacks are common inside of large companies, Web-hosting providers, or ISPs. In these scenarios many different departments may have subdomains directly under a parent domain such as somedepartment.company.com or username.isp.com. As an example, if research.company.com and dev.company.com are sharing client-side data storage by setting the equivalent domain restriction to company.com, then any subdomain of company.com, such as sales.company.com, can also access this data. The cross-domain attack vector applies to client-side storage systems using cookies, LSOs, or DOM storage. A good defense is to isolate the Web applications that need to share data inside their own subdomain. For example, Web applications on research.share1.company.com and dev.share1.company.com can use share1.company.com as the domain name to share the data through. This prevents applications in sales.company.com from accessing the data. Developers should always use the most specific domain name possible to limit other subdomains from accessing their data. In this case using share1.company.com is a much better choice than company.com because it limits possible eavesdroppers to share1.company.com. Developers should also verify whether it is even necessary to share data. Perhaps the applications can be moved to a single, isolated domain such as researchdev.company.com to exclude all sites beyond their control. 241 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE CROSS-DIRECTORY ATTACKS Cross-directory attacks are possible when you develop Web applications that use clientside storage for a Web server that has directories that are outside your control. Depending on the client-side storage method, you could be vulnerable to cross-directory attacks even if you are not explicitly sharing the data with another application. Ultimately cross-directory attacks are caused by developers not properly restricting the path on which Web pages have access to the storage system. Common victims of crossdirectory attacks are social networking sites or university Web servers, where different users or departments are assigned different folders on the same Web server. For example, each MySpace user is given personal Web space at http://www.myspace.com/ USERNAME. The authors’ alma mater, Georgia Tech, issues new students Web space at http://prism.gatech.edu/~USERNAME. Cross-directory attacks can occur using cookies or DOM storage for client-side storage. Consider the sessionStorage object. This object has is no concept of a restrictive Path attribute. Every page on the domain has access to the same session storage area. http://prism.gatech.edu/~acidus can steal anything that http://prism.gatech.edu/~bsullivan stores. The globalStorage also lacks a path restriction feature making it completely susceptible to cross-directory attacks. Cookies default to having a Path attribute of /, sharing their values with every page on the domain. The easiest way to avoid cross-directory attacks is to use an appropriate storage method. Avoid DOM storage in these situations because you cannot reliably secure DOM storage objects against this attack vector. Developers should sequester applications using cookies for storing their own unique directories where they have control over the subdirectories. Next, use the cookies’ Path attribute to limit cookie access exclusively to the directory for the application. Flash LSOs are extremely resilient against crossdirectory attacks because they are only vulnerable if you are sharing the LSO with another Flash object and have to store the LSO in a common directory close to the Web root. Consider two Flash objects on the same host located at /Internal/Tools/ IssueTracker.swf and /Internal/Tools/HR/TimeSheets/Reporter.swf that are sharing data. Just as with domain names, developers should always use the most specific directory name possible to limit access to the shared data. In this case the Flash objects should use /Internal/Tools/ when creating the LSO using getLocal(), as that is the most specific path common to both Flash objects. Developers can also isolate Flash objects that must share LSOs into their own special directory the same way we isolated Web applications on different domains into a unique domain to defend against cross-domain attacks. In our example IssueTracker.swf and Report.swf could be moved into the path /Internal/Tools/HR-Special/. This path should also be passed to Flash’s getLocal() function to share the LSO between the Flash objects. 242 CONCLUSIONS CROSS-PORT ATTACKS Cross-port attacks are fairly uncommon but are very dangerous. This attack vector occurs when another Web server beyond your control is running on a different port on the same hostname as your application. This should not be confused with shared hosting, where numerous Web sites are run from the same computer on the same IP address with a different hostname. Every client-side storage method except Internet Explorer’s userData is vulnerable to this type of attack because they use only the domain name instead of using the domain name and port number to restrict access. Cookies can use the secure attribute, but that simply forces the rogue Web server running on another port to also use SSL. Path restriction also will not defend against this vector because the attacker can recreate the directory structure necessary to access your data on their rogue Web server. Thankfully other untrustworthy Web servers running on other ports on the same domain name as trusted Web servers is fairly rare. However, if the opportunity presents itself, they can be very damaging because they bypass all access restrictions. There is a real-world example that turns this attack vector on its head by targeting applications on the Web server running on the nonstandard port number. Consider a Web administration application located at http://site.com:8888/Admin/ that is using a client-side storage system. An attacker simply creates a Web page at http://site.com/Admin/. If the attacker can trick the administrator into visiting this bogus site, his malicious JavaScript in http://site.com/Admin/ has access all the client-side storage data saved by http://site.com:8888/Admin/! CONCLUSIONS Client-side storage is an excellent way to offload some of the data storage requirements onto a user’s machine. It can also enhance the user experience by preserving data between browser refreshes to reduce the load on the server. Client-side storage can also cache large data structures to improve the performance of an application by manipulating the data locally before sending changes to the server. All of these features lay the groundwork for building offline Ajax applications. All of these methods suffer from common security issues. Regardless of the storage method, all data stored on the client can be accessed and modified. Developers must never keep sensitive or confidential information inside client-side storage. On an application in which the user decides what to place in client-side storage (such as a word processor) the developer must take steps to clean this data as often as practical to prevent an uneducated user from hurting themselves. Finally, nearly all of these storage mechanisms can inadvertently expose 243 CHAPTER 8 ATTACKING CLIENT-SIDE STORAGE client-side data to malicious third parties unless the developer uses proper access control methods. Table 8-3 summarizes the various features and limitations of the four clientside storage methods discussed in this chapter and should help developers to choose the appropriate method for their application. Table 8-3 High level-comparison of different features of four client-side storage methods Feature Cookies Flash local shared objects userdata DOM storage Supported All browsers All browsers with IE 5+ only Flash 6+ plug-in Firefox and Firefox derived browsers 2.0+ Type of storage Persistent, nonpersistent Persistent Persistent Persistent, nonpersistent Default size 4094 bytes (lowest common denominator among major browsers) 100KB 128KB 5MB (much larger for trusted intranet applications) Maximum size 4094 bytes Unlimited Varies 5MB (requires user changing settings) Allowed data types Strings Arrays, Booleans, Strings Numbers, Objects, Strings Strings Data can Yes No Yes No automatically expire Can limit which Yes Yes Yes Yes domains have access Ease of deleting Easy data on machine Medium Hard Hard Ease of reading Very Easy Must download Easy Easy data on machine special tool Ease of editing Easy data on machine Medium Hard Medium 244 9Offline Ajax Applications Myth: Offline Ajax applications have minimal security issues because they are only occasionally connected to the Internet. Offline Ajax applications tend to rely on client-side code even more than Ajax applications. As a result, offline applications suffer even more from code transparency issues than their online counterparts. In addition, offline frameworks like Google Gears provide more features to client-side code, such as SQL databases. Because of these additional features, offline applications can suffer from unique new problems like client-side SQL Injection. OFFLINE AJAX APPLICATIONS Offline Ajax applications are Web applications that can be used even when you are not connected to the Internet. The architecture of a typical offline application is shown in Figure 9-1. An offline application abstracts away where the data or resources a user interacts with are located. When the application is in online mode (i.e., it is connected to the Internet), this data is manipulated on the Web server through standard Ajax methods. However, when the user is offline, the application transparently uses a locally cached copy of the HTML, CSS, and JavaScript files that would normally be downloaded from the Web server. The application’s data is stored and manipulated inside of a client-side database. When the user eventually reconnects to the Internet, the application updates the Web 245 CHAPTER 9 OFFLINE AJAX APPLICATIONS server with any changes the user made to the cached data. Reconnecting also resynchronizes the cached versions of the Web resources, program logic, and data. Ideally the user is unaware as to whether the application is in offline or online mode; when this is true, the application transparently switches between local and remote resources. Such applications are said to be occasionally connected because users can use almost all of the application’s functionality when offline and use online mode to receive fresh data. Client Application UI Data Switch Server Data Layer Internet Local Data Layer Database Sync Engine Figure 9-1 Offline applications can use data stored either on the local machine or a remote Web server, depending on whether or not the user is connected to the Internet. A Web-based email application is a good candidate for an offline application. The new mail icons, logos, border graphics, CSS, HTML pages, and JavaScript for the application can all be stored locally on a user’s machine. A client-side database can store the user’s address book, inbox, and other mail folders. A user can read email, compose new email, and make changes to the address book. Features like searching email, adding pictures to an address book entry, rich text editing, or even spell checking can all be implemented in JavaScript on the client. Once the user connects to the Internet, the mail application can send all the freshly composed email to the Web server, update the address book, and download any new messages. As we discussed in Chapter 6, “Transparency in Ajax Applications,” an attacker can easily collect and analyze all the client-side source code of an Ajax application. This provides the attacker with knowledge about Ajax endpoints on the Web server, function names and arguments, program flow, and more. This information is exposed to the client in an effort to improve the features and responsiveness of the Ajax application. At least with standard Ajax applications, developers can minimize the amount of business logic they push to the client. This is extremely difficult to do with offline Ajax 246 GOOGLE GEARS applications. Developers must expose enough program logic and data on the client to make the application functional when it is not connected to the Internet at all. This magnified transparency makes offline Ajax applications especially susceptible to all the issues discussed in Chapter 6. In this chapter we focus most of our discussion on Google Gears, because Google Gears is by far the most popular and widespread offline Ajax framework. However, the security concepts surrounding locally served files, client-side databases, and managing complex client-side code discussed here are equally applicable to different frameworks. At the end of the chapter we discuss some of the other frameworks and methods used to create offline applications. GOOGLE GEARS Google Gears is a browser plug-in designed to help developers create Ajax applications that work offline. It currently supports Internet Explorer (on both the Windows XP and Windows Vista platforms) and Firefox running on Windows XP, Window Vista, Mac OSX, or Linux. To provide offline access, Google Gears consists of three components. • LocalServer. A server that caches and serves Web pages resources locally • Database. A client-side SQL database to store data locally • WorkerPool. An execution environment that provides separate Worker threads so computationally intense JavaScript code will not affect the user experience Google Gears allows developers to capture Web resources in the LocalServer feature. When the user is offline, these resources are served transparently from the user’s local machine. It is important to note that the application URL does not change regardless of whether the application is in online or offline mode. If the Web email client mentioned in the previous section was hosted from http://sexyajaxapps.com, then that hostname would always appear in the URL regardless of the application mode. Google Gears provides a client-side database accessible directly from JavaScript. This database is a SQLite database that has been modified to add security features and to support full text searching. The WorkerPool feature of Google Gears is especially interesting. It allows developers to run long, computationally intense JavaScript functions, such as encryption, sorting, or shortest path algorithms, outside of the Web browser’s thread. This leaves the UI responsive—and prevents the browser from becoming unresponsive. 247 CHAPTER 9 OFFLINE AJAX APPLICATIONS NATIVE SECURITY FEATURES AND SHORTCOMINGS OF GOOGLE GEARS Google Gears has several built-in security features. At its base, Google Gears follows the Same Origin policy. Web sites can only open databases that were created for that site’s origin. Web sites that use the LocalServer for offline caching can only capture URLs and use manifests from the site’s origin. Google defines a unique origin using only a URL’s scheme, hostname, and port. Thus any Web page on http://site.com:80 can access databases of captured resources used by any other Web page on http://site.com:80. There is no way to limit which paths inside a Web server can access Google Gears’ resources. To put it another way, there is no equivalent in Google Gears for the Path attribute in Cookies. This means that Google Gears is fundamentally vulnerable to Cross Directory Attacks, as described in Chapter 8, “Attacking Client-Side Storage.” While currently there is no mechanism to share Google Gears data between different domains or origins, Google is researching that possibility. This could open Google Gears to Cross Domain Attacks and Cross Port Attacks as well. SECURITY RECOMMENDATION Don’t Don’t deploy offline applications built using Google Gears on Web servers with other applications you cannot control. Do Do pay attention to who else can publish content on the Web server and the existing applications on that Web server. Every one of those applications can access LocalServer resources and data stored in the client-side database. If possible, sequester offline Ajax applications built on top of Google Gears in their own subdomain to defend against Cross Directory Attacks. Google Gears is an opt-in service. The first time a Web site attempts to access the Google Gears API, the Google Gears browser plug-in displays a dialog box, as shown in Figure 9-2. This dialog is generated by the browser plug-in and cannot be overridden or hijacked from JavaScript in the way the browser’s native alert() or confirm() functions can be shimmed, as discussed in Chapter 7, “Hijacking Ajax Applications.” Moreover, a Web page cannot control or change any of the text in this dialog. This is very positive for a 248 GOOGLE GEARS security point of view.1 If the user does not check the Remember checkbox in this dialog, it will be presented to him every time he visits a new page that uses Google Gears on that host. Yes, this is exactly as annoying as it sounds! Unfortunately Google Gears does not tell the user which Google Gears’ features a Web site wants to use. In the above example, the user has no idea whether localhost wants to create a local database or spawn a Worker process from the WorkerPool. Once a unique scheme-host-port origin has been granted access to Google Gears it can use all the features of Google Gears. There is no way to allow a Web site access to only certain features of Google Gears.2 Figure 9-2 Users must explicitly allow a Web site to utilize the Google Gears browser plug-in. The permission list denoting which Web sites can use Google Gears is stored in a SQLite database in the file permissions.db inside of the Google Gears data directory. There are no internal integrity checks or hashes that prevent this file from being modified by a desktop application other than the Google Gears browser plug-in. This means other attacks against an end user’s computer from viruses or malware could alter the permissions database and permit Web sites the user has not explicitly allowed (or even Web sites the user previous denied) access to Google Gears. Google Gears data files are stored in user-specific directories based on the operating systems that are supported. Google Gears relies on the operating system to prevent users 1 One of the numerous security problems with Microsoft’s ActiveX technology in the 1990s was that Web sites could control the text that appeared in the confirmation dialog presented to users. Malicious people inserted text like “Click yes to get free exclusive access to our new program!” and other misleading messages to trick users into executing harmful ActiveX controls. 2 We are not saying that this is a good or bad thing from a security point of view. It probably makes things easier from a usability point of view. However the consequence is that Google Gears has an all-or-no features granularity. 249 CHAPTER 9 OFFLINE AJAX APPLICATIONS from accessing each other’s Google Gears data. The following list gives the location of the Google Gears data directory under various operating systems and plug-ins. • Internet Explorer on Windows Vista. Example: C:\Users\\ AppData\LocalLow\Google\Google Gears for Internet Explorer • Internet Explorer on Windows XP. Example: C:\Documents and Settings\\Local Settings\Application Data\Google\Google Gears for Internet Explorer • Firefox on Windows Vista. Example: C:\Users\\AppData\ Local\Mozilla\Firefox\Profiles\\Google Gears for Firefox • Firefox on Windows XP. Example: C:\Documents and Settings\\ Local Settings\Application Data\Mozilla\Firefox\Profiles\\Google Gears for Firefox • Firefox on Linux. Example: /home//.mozilla/firefox// Google Gears for Firefox • Firefox on Mac OS-X. Example: Users//Library/Caches/Firefox/ Profiles//Google Gears for Firefox Any weakness or vulnerabilities in the underlying operating system could expose a user’s Google Gears data. This is not a security issue with Google Gears, but is important for developers to know. Of course, if a user’s underlying system is compromised by an attacker, that user probably has much more important things to be concerned with than whether her Google Gears data was leaked! Google Gears has numerous security features built into its implementation of SQLite and how the database is accessed. The Google Gears implementation of SQLite does not support the ATTACH or DETACH statements. ATTACH and DETACH allow SQLite to open other SQLite databases on the user’s machine. Not implementing these statements is a security feature that prevents Web sites from using the Google Gears database feature to read the contents of arbitrary SQLite databases on a user’s hard drive. Google Gears also does not implement the PRAGMA statement, which is used to configure various SQLite settings. This is a wise choice, as some of these settings could be changed to deliberately cause performance degradation and possibly compromise security. Of course, whenever you are querying a SQL database, there is the danger of SQL Injection vulnerabilities. As we learned in Chapter 3, “Web Attacks,” the root cause of SQL Injection is using unvalidated input and ad hoc SQL statements. Luckily, Google Gears supports parameterized queries, which, when used, help protect applications against SQL Injection. We discuss SQL Injection and Google Gears in depth later in this chapter. 250 GOOGLE GEARS Google Gears does not currently limit the amount of data an approved Web site can store on a user’s machine. A Web site could perform a Denial of Service attack against a user’s local machine by storing more and more data on the machine until the user’s drive is filled. A malicious Web site could perform this type of attack on purpose, but a legitimate Web site might also inadvertently cause a Denial of Service attack. For example, a bug in the code could cause some JavaScript code to stay in a loop, writing data over and over onto the user’s machine. Developers need to be especially careful and courteous when storing data on a user’s machine. Furthermore, developers need to be aware that Google Gears provides no safety net to prevent an application from inadvertently filling a user’s hard drive. Unfortunately, while Google Gears provides a way for developers to store data on a user’s drive, there is no documentation for the end user to explain how the user can delete data stored inside Google Gears. Furthermore, the Google Gears browser plug-in does not provide a GUI interface for clearing saved data. Instead, users must manually clear out the data themselves. To do this, users must go to the directory in which Google Gears is located (based on the operating system and Web browser) and delete any files they find. EXPLOITING WORKERPOOL WorkerPool is a very interesting feature of Google Gears. It allows you to spawn off a Worker that is a separate process that runs a block of JavaScript code. This process allows computationally expensive tasks to run without disrupting the user interface or causing the browser to become unresponsive. JavaScript can communicate with Workers by sending messages and utilizing callback functions. But what do we mean when we say WorkerPool uses a separate process? Google doesn’t really provide a definition in the documentation. The Google Gears developer documents state that JavaScript inside of Worker code cannot access the DOM because, “Workers do not share any execution state” with the browser. Google also refers to Workers as processes and not threads, because no state is shared between separate Workers as in traditional threads.3 What is interesting about Workers is what happens to the executing code. Let’s suppose that Alice has approved http://site.com/ to use Google Gears. Alice visits http://site.com/SomeCoolApp/, which starts a Worker processing some extremely complex task. While this Worker is running in the background, Alice clicks on a link to the BBC World News Web site. What happens to the Worker process that is still executing when Alice leaves the Web site that spawned the Worker? The answer is, it keeps running! 3 You can find more information at http://code.google.com/apis/gears/api_workerpool.html. 251 CHAPTER 9 OFFLINE AJAX APPLICATIONS Let’s say Alice is using a tabbed browser like Firefox and that Alice has two tabs open: one to the CNN Web site and one to http://site.com/CoolAjaxApp/. CoolAjaxApp has created a Worker process that is running in the background. Alice clicks a link in the second tab and navigates to the BBC. As mentioned, the Worker process created by CoolAjaxApp is still running in the background. Next, Alice closed the tab with the BBC’s Web site and so has closed the browser tab that created the Worker process. What happens? The Worker still continues to execute! In fact, until Alice closes her Web browser, the Worker process will continue to run! Remember how Google Gears does not place an upper limit on the amount of storage? Let’s consider a Denial of Service attack in which a Worker process writes junk entries into a SQLite database in an attempt to fill up the user’s hard drive. In a test run on a laptop computer with a 2.1Ghz Intel Pentium processor, the authors were able to write approximately 110 megabytes a minute into a SQLite database using a hidden WorkerPool. Imagine if Eve sets up an evil Web site on http://www.someisp.com/eve/. Alice has already approved all Web pages on www.someisp.com:80 for Google Gears access because her friend Bob has a cool application that uses Google Gears running on http://www.someisp.com/bob/AjaxApp/. Remember, there is no way Alice can restrict which pages on www.someisp.com have access to Google Gears. Eve tricks Alice into visiting her Web site (by posting links to the malicious page on various blogs, spam email, etc.). As soon as Alice visits Eve’s evil Web page, all she sees is a Web page telling her that the Web page she requested could not be found. This is a trick. Eve has created a fake 404 page that spawns a Worker process in the background. The Worker page begins to write junk data into a Google Gears database on Alice’s laptop. Alice is confused, but continues her browsing. She visits various other pages, opens and closes tabs, and even takes a coffee break. All the while Eve’s evil Worker process has been saving junk data on Alice’s hard drive at a rate of 110 megabytes per minute. This means after an hour and a half, Eve’s evil Worker process has filled up 10 gigabytes of space on Alice’s hard drive! It’s important to remember that there is nothing inherently dangerous or insecure about Google Gears’ WorkerPool. The WorkerPool simply allows JavaScript programs to isolate computationally expensive tasks so they don’t affect the user’s experience. These tasks continue to run, regardless of what the user does, until the user closes her Web browser. The browser also gives no indication of any kind that a Worker has been created. All of these features, while not malicious in their own right, can be twisted by an attacker to perform malicious activities they were unable to launch in the past. As an example, Cross-Site Scripting (XSS) attacks can cause much more damage using WorkerPool because the attacker will continue to execute even after the user has left the infected page. 252 GOOGLE GEARS LOCALSERVER DATA DISCLOSURE AND POISONING LocalServer allows the caching of all types of resources. Many times these resources return different content for different users. Often this information contains sensitive, user-specific data. Unfortunately, any Ajax application on a host can access the LocalServer of any other Ajax application on the same host as long as the application knows the correct LocalServer name. Consider a Web site called Ajax Mail, which is an implementation of the offline Web mail application we discussed at the start of this chapter. Ajax Mail is hosted on the Web site SexyAjaxApps.com, which hosts multiple other offline applications, including a malicious application called EveIsEvil. Let’s say a user logs into Ajax Mail and a copy of inbox.html is stored in Google Gears’ LocalServer. Figure 9-3 shows the user’s inbox. Figure 9-3 A user’s inbox for the offline application Ajax Mail If the user visits Eve’s application in offline mode, Eve’s Web page can steal the contents of the inbox by pointing an iframe at inbox.html. Because the user is in offline mode, the inbox that is stored in the cache is retrieved and Eve can read the cached inbox out of the iframe as shown in Figure 9-4. This allows Eve to steal sensitive data directly out of the LocalStore object. 253 CHAPTER 9 OFFLINE AJAX APPLICATIONS Figure 9-4 Eve’s application can read the content stored in Ajax Mail’s LocalServer because both applications are hosted on the same server. SECURITY RECOMMENDATION Don’t Don’t store any extra resources in LocalServer that are not used by the application while in offline mode. Do Do keep the resources stored in LocalServer to the absolute minimum needed for the application to operate properly in offline mode. Anything else in LocalServer is just baggage that could contain sensitive data or information useful to an attacker. If the data isn’t in LocalServer, an attacker cannot steal it. We see that malicious Ajax applications can access cached resources from other Ajax applications on the system Web server. However, there is nothing stopping a malicious site from overwriting cached resources of another application on the same server. 254 GOOGLE GEARS Perhaps Eve wants to replace the inbox.html page for the Ajax Mail application from the previous section with her own version. The code for this follows: var STORE_NAME = 'ajax-mail' var store; var server; function setupSmash() { if (!window.google || !google.gears) { //No Google Gears return; } server = google.gears.factory.create('beta.localserver', '1.0'); store = server.openStore(STORE_NAME); //capture a local copy of our fake inbox page store.capture('fake-inbox.html', null); } function smashTheCache() { //remove the original version from the cache store.remove("../AjaxMail/inbox.html"); //set our fake inbox as the new cached inbox page store.copy('fake-inbox.html', "../AjaxMail/inbox.html"); } First Eve captures a copy of her fake inbox page, fake-inbox.html, and stores it in the same LocalServer name that Ajax Mail uses. This allows Eve to manipulate it inside of the LocalServer cache. Next, Eve smashes Ajax Mail’s cached copy of inbox.html. She uses store.remove()to delete the cached version and uses store.copy() to associate the contents of her file fake inbox file, fake-inbox.html, with the URL for the inbox. Figure 9-5 shows that Eve has changed the contents of the inbox for Ajax Mail while in offline mode. Specifically, Eve has changed the value of certain emails to be the exact opposite of their original meaning. Not only can attackers poison the LocalServer cache with fake HTML pages, they can also attack other resources like Cascading Style Sheets or even external JavaScript files. We explore various tricks attackers can perform with poisoned Cascading Style Sheets in Chapter 12, “Attacking the Presentation Layer.” For now, let’s consider what Eve could do if she poisons the LocalServer cache with a new JavaScript file. Eve can overwrite an application’s program logic with her own malicious code contained in the new JavaScript file. Eve’s malicious code acts just like the normal application so as to not 255 CHAPTER 9 OFFLINE AJAX APPLICATIONS arouse suspicion, but her code also silently captures sensitive data like usernames, passwords, financial data, or emails. The LocalServer can be poisoned allowing any Web site on a host to take complete control over an offline Google Gears Ajax application on the same host! Figure 9-5 Eve’s application can modify content stored in Ajax Mail’s LocalServer, corrupting its meaning. SECURITY RECOMMENDATION Don’t Don’t use static or predictable LocalServer names. This makes it trivial for malicious applications to access and manipulate resources stored in LocalServer. Do Use a different LocalServer name for each and every user. Using a secure hash of someone’s user name as a base is a good method of creating an easily reproducible—but difficult to guess—LocalServer name. This is a security through obscurity method. While it is not a comprehensive security solution, it does make it more difficult for malicious applications to tamper with resources cached in LocalServer. 256 GOOGLE GEARS DIRECTLY ACCESSING THE GOOGLE GEARS DATABASE Databases in Google Gears, much like the other forms of client-side storage discussed in Chapter 8, can be accessed and edited outside of the browser. There are many free or open source tools, such as the SQLite Database Browser, that can open and manipulate SQLite databases. These databases are stored inside directories representing the schemehostname-port origin identity of the Web site that created them, and the database directories are stored inside of the Google Gears data directory, whose location was provided in the last section. It is trivial to find and access these databases as shown in Figure 9-6. Figure 9-6 Google Gears databases can be manipulated with any tool that can read and write SQLite database files. As with any form of client-side storage, developers should not expect that data written into a Google Gears database is safe from modification. Developers cannot trust that data retrieved from a Google Gears database is not malicious. They must perform input validation on all data returned from the database before acting upon it. Developers must also implement data integrity checks to prevent data tampering as Google Gears does not have native data integrity features. Developers should always perform input validation on data they retrieve from a Google Gears database. 257 CHAPTER 9 OFFLINE AJAX APPLICATIONS SQL INJECTION AND GOOGLE GEARS As mentioned earlier, the Google Gears SQLite database is SQL injectable, just like any other SQL database. While SQLite does not support all features of SQL, it is fairly complete and contains a number of features that can aid in a SQL Injection attack. For example, every SQLite database contains a special table called sqlite_master. This table defines the schema for the database. Among other things, we can query this table to get a list of all the user tables in the database, much like we can query the sysobjects table in Microsoft’s SQL Server. The sqlite_master table also contains a column called sql, which contains the CREATE TABLE SQL statement originally used to create the table. This allows us to determine the data types of a column, such as date or integer, as well as column attributes like NOT NULL or PRIMARY KEY. SQLite also has an interesting feature: Column data types are not enforced. You can insert text values into columns marked as integers. This makes it easier to perform UNION SELECT SQL Injection attacks, because the attacker only needs to match the correct number of columns between both queries—not the data types as well. However, this indifference that SQLite applies to data types also means that attackers cannot use casting or converting to deliberately invoke errors and extract data. So, while UNION SELECT SQL Injection attacks are easier to perform against SQLite databases, they are also one of the only methods to maliciously extract data out of SQLite databases. SQLite interprets -- as a comment. This allows an attacker to comment out the end of the SQL statement they are injecting into without having to worry about repairing any syntax errors their injection causes. Finally, SQLite does not support multiple queries in a single execution step. For example, the statement SELECT name FROM Customers; SELECT * FROM Orders will only return results for the first query. This prevents the single query database dumping attacks discussed in Chapter 6. Let’s look at attacking a sample Web application, List Mania, which allows users to store multiple lists of various items online. List Mania uses Google Gears to store the lists locally for offline access. Figure 9-7 shows List Mania operating normally. As with traditional server-side SQL Injection, we begin looking for vulnerabilities by entering special characters into text boxes and trying to deliberately cause a SQL syntax error. The common way is to specify a single quote (') to cause an error for having a mismatched number of quotes. When we attempt to access the list ToDo' we receive a SQL error in the JavaScript error console, as shown in Figure 9-8. 258 GOOGLE GEARS Figure 9-7 List Mania is an offline Ajax application that allows users to manage multiple lists. Figure 9-8 A verbose SQL error message caused by mismatch of quotes in the SQL statement This error message is quite verbose. It even tells us the full SQL statement that the application tried to execute! All database errors in Google Gears are thrown to the application and can be captured using try { ... } catch(e){ } blocks. The object that is caught contains a message property that contains the verbose error. Next we use a UNION SELECT attack to extract out the all the names of the tables from the local database, as shown in Figure 9-9. 259 CHAPTER 9 OFFLINE AJAX APPLICATIONS Figure 9-9 SQL Injection is used to extract the names for database tables inside a Google Gears database. In this figure you can see we use the AND 1=0 clause to prevent the first query from returning any results that would pollute our table list. We also use a comment (--) so any trailing parts of the original SQL query are ignored. From here, it is trivial—though tedious—to extract all the data out of the local database. Obviously, any JavaScript inside the application is capable of accessing the database. So, Cross-Site Scripting attacks can be used to steal data from a client’s database in the same way they can be used to steal information from client-side storage systems, as demonstrated in Chapter 8. However, it is possible an attacker might not know the name of the database to connect to. To prove this is not a limitation, the authors created GGHOOK, as in Google Gears HOOK. GGHOOK is based on the on-demand Ajax hijacking framework HOOK from Chapter 7. GGHOOK recursively scans through all instantiated objects in the JavaScript environment looking for an object with the same functions and properties as the Google Gears Database object. Once it finds the database object, GGHOOK queries the sqlite_master table and then extracts out all the data from all the user tables. Figure 9-10 shows GGHOOK stealing all the data out of our List Mania application. 260 GOOGLE GEARS Figure 9-10 GGHOOK locates any Google Gears database object inside the JavaScript environment and automatically dumps out all the data. As we have mentioned, Google Gears supports parameterized SQL queries. For example, the following ad hoc SQL query is very dangerous. db.execute("SELECT * FROM Users WHERE username='" + uname + "' AND password='" + pwd + "'"); Google Gears allows developers to use parameterized SQL queries using the ? character to specify a placeholder. Developers can pass an array of variables as an optional second parameter to the execute() function. Each ? in the query string is then replaced with the corresponding variable in the array. Google Gears throws an exception if the number of ? characters in the query string differs from the length of the array. The following code shows how to properly use parameterized SQL queries in Google Gears. db.execute("SELECT * FROM Users WHERE username=? AND " + "password=?", [uname, pwd]); 261 CHAPTER 9 OFFLINE AJAX APPLICATIONS While it is rare, sometimes it is not possible to use parameterized queries. For example developers cannot use the ? placeholder to specify the table name a query should run against. Thus the following code is not valid: db.execute("SELECT * FROM ? ", [tbl_name]); Parameterized queries also cannot be used when there are an unknown number of conditional statements in the WHERE clause. This problem most commonly occurs with the search feature of a Web site, where a user can specify a variable number of conditions for her search query. For example, a user’s search for “Ajax AND offline AND security” translates into a SQL statement that looks like the following: SELECT * from Articles WHERE content LIKE '%Ajax%' AND content LIKE '%offline%' AND content LIKE '%security%' A developer does not know in advance how many conditionals there will be in the WHERE clause, and thus can’t create a parameterized query string with the correct number of ? placeholders ahead of time. This problem is usually solved with complex stored procedure logic; but SQLite does not support stored procedures. Instead, a developer is forced to dynamically construct an ad hoc SQL query based on the number of conditionals a user is searching for. Whenever possible, developers should always use parameterized SQL queries. If you think that you have to dynamically construct the SQL query, consult a database programmer to see if there are any other options. Situations that require an ad hoc SQL query are fairly rare and can typically be mitigated with an architectural change. If you must use an ad hoc SQL query, make sure that you perform rigorous whitelist input validation that validates both data type and range. See Chapter 4, “Ajax Attack Surface,” for more information about proper whitelist input validation techniques. HOW DANGEROUS IS CLIENT-SIDE SQL INJECTION? Just because client-side SQL Injection is possible, should we even care? Is client-side SQL Injection even a security issue? After all, the database exists locally on a user’s machine. The attacker can simply open the database with a tool like SQLite Database Browser and see the contents of the database without needing to extract the data using SQL Injection. Furthermore why SQL inject yourself? The database probably only contains your personal data anyway! All of this is true. SQL injecting yourself is silly when you can simply edit the database directly. Client-side SQL Injection becomes interesting when an attacker can perform a 262 GOOGLE GEARS SQL Injection attack on someone else’s client-side database. We’ve seen that XSS can access this data trivially because it can talk directly to the database. Let’s consider a fictitious Web-based instant messaging application called WebIM.com. WebIM stores conversations a user has with other users locally in a Google Gears database. Eve finds a client-side SQL Injection vulnerability in the way the client-side code processes instant messages. Figure 9-11 shows Eve sending Alice an instant message with a SQL Injection attack that uses a UNION SELECT to extract out the data about Alice’s conversations with other users. WebIM.com ‘UNION SELECT… ‘UNION SELECT… Success? Eve Alice Figure 9-11 It is difficult for attackers to extract data from a client-side SQL Injection vulnerability because they cannot directly talk to the client they are attacking. The problem is: How does Eve get the results of her attack? Eve is exploiting a vulnerability in JavaScript code that is running in Alice’s Web browser, but Eve cannot talk directly with Alice’s Web browser! Eve cannot see a verbose SQL error message in Alice’s error console or access the record set returned by the Google Gears database on Alice’s machine. Eve cannot see any HTML rendered inside Alice’s Web browser as a result of the SQL Injection that might display all of Alice’s conversations. One sure way Eve could access the results of her UNION SELECT attack is if Eve had code running on Alice’s Web browser that reads these variables. However, if Eve had code access on Alice’s browser, she could have just talked to the database directly to steal Alice’s conversations instead of having to use SQL Injection! It would appear that, because Eve cannot see the results of her attack, client-side SQL Injection is not dangerous. The problem with this argument is that while Eve cannot necessarily see the results of her attack, she is still executing SQL commands on Alice’s machine! Instead of a UNION 263 CHAPTER 9 OFFLINE AJAX APPLICATIONS SELECT, perhaps Eve performs a DELETE FROM or a DROP TABLE attack to destroy data on Alice’s machine. Perhaps Eve updates tables in the database with garbage or inserts malicious data into the database, which is later synchronized with the database on the Web server, using Alice’s credentials! SQLite’s support for triggers creates some very interesting possibilities for attackers to leave persistent SQL commands that can monitor a user’s database activity. Of course, the full scope and exploitability would depend on how the vulnerable application was written, especially in light of SQLite’s inability to run two queries in a single execution statement. Furthermore, don’t discount Eve’s apparent inability to see the response. It’s possible that the data extracted from the database is sent from Alice’s machine as part of normal behavior. For example, let’s say WebIM allows users to set up auto-response actions that will send a particular response if a certain message is sent (many instant messaging desktop clients allow you do to this). Alice might have an auto-response action for incoming messages containing the word work. Eve’s SQL Injection attack would contain the word work and the data the attack extracts would get sent back to Eve in an auto-response message. Eve could very well receive the following message from Alice: Sorry, I’m on vacation and not thinking about [DATABASE DUMP HERE]! DOJO.OFFLINE Dojo.Offline is an optional extension of Dojo that helps developers quickly create offline applications. It is based upon Google Gears, with some extra features and integration with the rest of the Dojo framework. The most security-applicable features that Dojo.Offline adds on top of the features provided by Google Gears are the ENCRYPT and DECRYPT SQL commands. These commands allow users to store sensitive data inside of a Google Gears database in an encrypted format. The stated goal of Dojo.Offline’s encryption support is to protect sensitive data in the event that a computer is stolen, as mentioned in the documentation at http://docs.google.com/View?docid=dhkhksk4_8gdp9gr#crypto. SECURITY NOTE Don’t confuse encryption with security. Encryption just prevents someone from reading the data should they happen to get access to the encrypted data. It does not protect the data from any other type of attack. For example, attackers could install a keylogger on a user’s system to steal the password and decrypt the data whenever they want. The function hijacking techniques discussed in Chapter 7 could be used to steal the data from inside the application when it is returned unencrypted by Dojo. 264 DOJO.OFFLINE Dojo.Offline uses the JavaScript implementation of the Advanced Encryption Standard (AES) created by Chris Veness with a 256 bit key length. Internally Dojo.Offline uses a WorkerPool to run the encryption/decryption routines in the background so as to not adversely affect the user experience. The following code snippet shows how to encrypt and decrypt data using Dojo.Offline. Note the use of the ENCRYPT and DECRYPT statements. dojox.sql("INSERT INTO CUSTOMERS VALUES (?, ?, ENCRYPT(?))", "Neuberg", "Brad", "555-34-8962", password, function(results, error, errorMsg){ if(error){ alert(errorMsg); return; } }); //... later in code dojox.sql("SELECT last_name, first_name, " + "DECRYPT(social_security) FROM CUSTOMERS", password, function(results, error, errorMsg){ if(error){ alert(errorMsg); return; } // go through decrypted results alert("First customer's info: " + results[0].first_name + " " + results[0].last_name ", " + results[0].social_security); }); We see in the preceding code that the password is used as an argument for the dojox.sql() function. dojox.sql() uses the password to either encrypt data being inserted into the database or to decrypt data returned with a SELECT statement. Because AES is a symmetric encryption algorithm, the same key that encrypts the data also decrypts the data. So where does this key come from? KEEPING THE KEY SAFE Keeping encryption or decryption keys safe is critical in any cryptographic system.4 Absolutely do not store the key anywhere in the JavaScript program! Do not hard code it into the JavaScript source code. Do not store it inside the client-side database. Do not 4 Please note, a full discussion of cryptography is certainly beyond of the scope of this book. The authors recommend Bruce Schneier’s landmark book, Applied Cryptography. 265 CHAPTER 9 OFFLINE AJAX APPLICATIONS store it in some other mechanism for client-side storage. Do not cache it inside a cookie. Remember, Dojo.Offline’s encryption support is supposed to protect the data from someone who happens to come across the data. If you store the password along with the data, then anyone who steals a laptop has both the encrypted data and the key to decrypt it! Instead, configure your application so that the user is prompted for a key whenever data needs to be encrypted or decrypted. If a program will be using the key repeatedly in a short time frame, you can cache the key in a JavaScript variable. However, developers need to be careful about how they cache the key. Allow each user of your application to enter his or her own password to encrypt the data. Do not use a single encryption key for every user, as a single password compromise would expose all the data on the system. You should not use a simple text box or JavaScript’s confirm() function to prompt the user for the password. Both of these methods display the user’s password in the clear as they input it. Instead, use an HTML INPUT tag with a TYPE="password" attribute to create a text box that masks the password as the user types it in. Once the program has received the encryption key from the user, developers need to be very careful about how they handle the key. We want to minimize the exposure of the key inside the JavaScript environment where other untrusted code might be able to access it. Most importantly, we don’t want to leave the password visible inside of a DOM element or JavaScript variable after we have used it to perform the encryption. The password box in which the password was entered should be cleared immediately. Minimize the number of variables the password is stored in. If possible, avoid storing the password inside a global variable. As we mentioned in Chapter 5, any JavaScript variables declared without using the keyword var are created as global variables. Clear the contents of the variable holding the password immediately after you pass it to the dojox.sql() function. By sequestering the key inside of functions and keeping it out of the global scope, we prevent other pieces of JavaScript from accessing the contents of the key. This is especially important in mash-up applications where untrusted third-party widgets can run in the same environment as your code. KEEPING THE DATA SAFE Not only must you protect the key, but you also need to protect the data. You should minimize the number of locations that store unencrypted copies of sensitive data by removing all DOM elements and clearing all variables that store the unencrypted data when they are no longer needed. You should also take steps to prevent encrypting data with a mistyped key. Once data has been encrypted, only the exact key will properly decrypt it. To prevent users from accidentally corrupting the data, ask a user to enter the password twice. Present the user 266 DOJO.OFFLINE with two INPUT tags of TYPE=password and ensure the two values match before encrypting the user’s data. There is no native way to detect if a user-supplied password is the correct password to decrypt encrypted data. AES doesn’t produce an error if encrypted text is decrypted into invalid text. AES will happily decrypt encrypted data into gibberish. To enrich the user experience, some developers will encrypt a piece of known plaintext. For example, if an application stores encrypted social security numbers, developers will store a dummy entry whose social security number is 999-99-9999 (which is not a valid SSN). When the user attempts to decrypt the data, the program will first try to decrypt the dummy row and check if the result is 999-99-9999. If it is not, the program knows the user entered an invalid password and prompts the user to reenter the password. While this is nice from a user interface point of view, it reduces the security of the application by creating a known plaintext attack. An attacker who captures a copy of the encrypted data has a piece of encrypted text that he knows decrypts to 999-99-9999. The attacker can then use this information to mount an attack against the encrypted data to discover the key. SECURITY RECOMMENDATION Don’t Don’t encrypt specific plaintext and check if a password is valid by comparing the decrypted text with the known original text string. This exposes you to a cryptographic known plaintext attack. Do If you want to provide some feedback, try validating the format of the decrypted data instead of performing a direct string comparison. Whitelist input validation regular expressions are perfect for this. For our social security number example, if the user supplied the correct password then each decrypted social security number should match the regex: /^\d\d\d-\d\d-\d\d\d\d$/. Matching the decrypted text against this regex doesn’t tell an attacker anything about the encrypted data that they didn’t already know. By looking at the application, an attacker would already know that the encrypted data is a social security number. GOOD PASSWORDS MAKE FOR GOOD KEYS Dojo.Offline claims that it uses 256 bit keys for AES encryption and decryption. However Dojo provides virtually no information about how these keys are created. If a 267 CHAPTER 9 OFFLINE AJAX APPLICATIONS user enters a password that is a single ASCII character in length, they have entered only 8 bits of data. Dojo.Offline then pads the password out with 0xFF to a length of 32 characters. While this password is now 256 bits long (32 characters × 8 bits per character = 256 bits), only 8 bits of that are supplied by the user. A key only has as much entropy or randomness as the user (or a true random number generator) supplies. No encryption algorithm can make a key stronger. Thus, no matter how robust an encryption algorithm is—or how large its keys are—the strength of the algorithm can ultimately come down to the quality of the key. To get the full benefit of Dojo.Offline’s encryption, users must enter a 32 character password. However, we realize that it might not be practical to require your users to have 32 character passwords. One option, if a user enters less than 32 characters, is to append the supplied password to itself over and over until you have 32 characters. For example, if a user enters pass1 as their password, your application should actually use pass1pass1pass1pass1pass1pass1pa as the password. While this approach is not as strong as having 32 unique characters it’s still better than padding passwords with known characters like 0xFF. Regardless of how you construct a password whose length is long enough to take advantage of the full key size, it should be a strong password. A strong password contains a mix of uppercase and lowercase letters, numbers, and special characters like @ $ _ ! and }. There are numerous JavaScript code snippets that can gauge the strength of a supplied password. Developers should use JavaScript code to validate the strength of the password and reject bad passwords. CLIENT-SIDE INPUT VALIDATION BECOMES RELEVANT It is funny how everything comes full circle. An early use of JavaScript was to validate input user entered into Web forms before sending it across the Internet to the Web server for processing. The thinking was that sending information across the Internet cloud was expensive in terms of time, and there was no point in processing a user’s information if it was incorrectly formatted anyway. As we mentioned in Chapter 4, this led to the unfortunate consequence that developers only performed input validation on the client-side. As a result, Web security professionals (the authors included) preach that client-side input validation only increases the application’s performance and user experience, while server-side input validation is the only way to guarantee security. Unfortunately this is not true for offline applications. As we saw at the start of the chapter in Figure 9-1, offline Ajax frameworks increase the client’s role in business logic. In fact, offline Ajax applications strive to make the 268 CLIENT-SIDE INPUT VALIDATION BECOMES RELEVANT concept of online or offline completely transparent to the user. The application behaves in (almost) the same way, regardless of whether the user is connected to the Internet or not. As we have mentioned, this means the user is interacting with client-side code, which stores everything the user is doing and synchronizes the data with the Web server when the client connects to the Internet. If no client-side input validation occurs, then the client-side logic is vulnerable to all kinds of parameter manipulation attacks as discussed in Chapter 3. Ajax applications already push more of a Web application to the client, and offline Ajax applications do push even more logic to the client. Just as we perform whitelist input validation on the server for security purposes, developers must perform client-side validation to ensure the security of their offline Ajax applications. That being said, what resources exist to aid a developer in performing whitelist input validation on the client? Ideally we want a simple validator object with a large number of static functions to perform whitelist validation on types of data. It might look something like this: Validate.US.State("GA"); //true Validate.EmailAddress("eve@evil.org"); //true Validate.AbsoluteURL("http://www.memestreams.net"); //true Validate.Date.YYYYMMDD("2007-04-29"); //true; Validate.US.ZIPCode("30345"); //true Validate.CreditCard.Visa("4111111111111111"); //true This approach leaves the developer free to decide how to style or name their form elements and how and when to perform validation. Astonishingly, the authors were unable to find a standalone validation library with these characteristics. Instead, most of validation libraries were tied into much larger, and more complex, frameworks. Many of these frameworks were specific server-side application frameworks like ASP.NET, Struts, or Rails. Even worse, the validation libraries that were part of client-side frameworks forced various dogmatic coding styles on developers. Dirty examples we found in the wild include forcing the overuse of object literals, weird framework Extend() functions, particular CSS class names, bizarre naming conventions for ID or name attributes, and mandatory event overriding. Libraries are supposed to help programmers, not force a particular group’s structure or coding philosophy on someone else. We are extremely disappointed with the state of client-side JavaScript validation frameworks. Developers should check to see if any of the frameworks they are currently using already contain client-side JavaScript validation code. How well these integrated validation libraries work in an offline Ajax application varies. 269 CHAPTER 9 OFFLINE AJAX APPLICATIONS OTHER APPROACHES TO OFFLINE APPLICATIONS We have spent this chapter focusing on Google Gears and derived offline frameworks. While most developers seem to be embracing this architecture, we would be negligent not to mention alternative offline architectures and their security concerns. One alternative approach to offline application is to copy the entire application (HTML files, JavaScript files, external style sheets, XML documents, etc.) to a user’s local machine. CAL9000, a popular Web security application takes this approach. Users open the HTML files using their Web browser. It is important to note that these files are not served using a locally run Web server. Instead, the pages are accessed using the file:// URI. From a security perspective, the biggest danger is that JavaScript served from file:// URLs run in a more privileged environment than do normal Web pages. This JavaScript has access to resources not usually available to JavaScript served from remote Web servers. Examples include reading and writing to files on the local machine, sending XMLHttpRequests to any domain on the Internet, and more. Also, these applications tend to be very client heavy. It is difficult to translate seamlessly from online to offline mode using the architecture. These offline applications tend to have no online component; as a result, the entire application is written in JavaScript and exposed in client-side code. Another approach is to push part of the application to a local Web server. These local Web servers are more advanced than the simple LocalServer found on Google Gears. They can use server-side scripting languages like PHP or ASP.NET. However, things quickly get confusing. Is logic written in server-side languages running on a client-side Web server considered the client-side or the server-side? The question is moot. Regardless of whether the logic running on a user’s local machine is written in a combination of PHP and JavaScript or exclusively in JavaScript, all the program source code is accessible to an attacker for analysis. Interesting offline frameworks to keep an eye on are Adobe’s Apollo, Joyent Slingshot, the cool stuff WHATWG is coming up with (and that Firefox 3 is consuming), and POW—the Plain Old Web Server for Firefox. The features of these frameworks vary, but the security issues discussed in this chapter transcend individual frameworks. CONCLUSIONS We have seen that there are some serious security issues to contend with when building offline applications. All of the code transparency issues discussed in Chapter 6 take on a much larger role. While in a standard Ajax application developers can minimize the amount of business logic they push to the client, in an offline Ajax application, the developer’s hands are tied. If they don’t push some business logic to the client, their 270 CONCLUSIONS application will not be usable in offline mode. Many of the features of Google Gears carry security concerns. The lack of restrictions on disk usage as well as the all-ornothing security model and susceptibility to Cross Directory Attacks are also a concern. Transparent local caching features like LocalServer can pave the way to cache poisoning vulnerabilities. Offline Ajax applications have also created a whole class of client-side injection attacks, requiring developers to perform client-side validation to ensure application security. Finally, be extremely careful with how you store and manage unencrypted source data and keys when using cryptographic add-ons like Dojo.Offline’s ENCRYT and DECRYPT keywords. 271 This page intentionally left blank 10 Request Origin Issues Myth: Ajax doesn’t make any traditional Web application attack vector any worse than it currently is. We have spent a good part of the book discussing how Ajax increases the scope of traditional Web application attack vectors. For example, Ajax endpoints increase the attack surface of your Web application that must be secured against traditional attacks like SQL Injection or Cross-Site Scripting (XSS). Code transparency increases the amount and detail level of information your application leaks to an untrustworthy client. However, one of the components of Ajax makes some traditional attack vectors worse than their pre-Ajax equivalent. The flexibility, features, and speed of the XMLHttpRequest object has increased the damage and danger of attackers actively harvesting large amount of private data using targeted HTTP requests. In short XHR has exacerbated the problem of how Web servers handle human-generated HTTP requests as opposed to script-generated HTTP requests, the problem known as request origin uncertainty. ROBOTS, SPIDERS, BROWSERS, AND OTHER CREEPY CRAWLERS The Hyper Text Transfer Protocol (HTTP) is the plumbing language of the World Wide Web. It is the protocol used by clients to retrieve information from Web servers. All Web browsers—whether we’re talking about Firefox on a Windows machine, Internet Explorer on a Macintosh, or Opera on a cell phone—communicate using HTTP. Automated Web crawling robots used by Google to index the Internet, or by Netcraft to 273 CHAPTER 10 REQUEST ORIGIN ISSUES gather statistics, also speak HTTP. RSS readers speak HTTP when they are retrieving a news feed. Anything that talks to a Web server is known as a user agent, and all user agents talk with Web servers using HTTP. Figure 10-1 shows the many diverse user agents that can speak to a Web server using HTTP. Web Browser Web Browser Web Browser Web Crawler Figure 10-1 Different user agents on different types of systems all communicate with Web servers using HTTP. The HTTP standard dictates how user agents and Web servers interact. A full discussion of HTTP is well beyond this chapter’s scope. A detailed analysis of all the features of HTTP could (and does) fill a large book.1 It should be sufficient to say that HTTP defines all aspects of how a Web server and a user agent create and maintain connections with each other, and how they communicate on top of those connections. 1 For a good HTTP reference the authors recommend David Gourley and Brian Totty’s HTTP: The Definitive Guide. 274 ROBOTS, SPIDERS, BROWSERS, AND OTHER CREEPY CRAWLERS “HELLO! MY NAME IS FIREFOX. I ENJOY CHUNKED ENCODING, PDFS, AND LONG WALKS ON THE BEACH.” Because this chapter revolves around the differences between HTTP requests that are generated by humans and those requests generated programmatically by JavaScript, we must first explore how Web servers deal with HTTP requests from different user agents. If all user agents speak HTTP, how does the Web server know who is talking to it? The creatively named HTTP header User-Agent provides all kinds of information about the user agent who sent the request. User-Agent is not a required HTTP header, and it only appears in HTTP requests. Like other HTTP headers, it supplies a single line string that normally includes the name of the user agent and its version number. Often it will also include the operating system and windows environment in which the user agent is running. The following is the User-Agent header from an HTTP request made by Mozilla Firefox 1.5. Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv: Gecko/20060909 Firefox/ We can see that this user agent is running on a machine with the Windows operating system. The Windows NT 5.1 tells us it is actually a Windows XP machine. The language is English (en-US). The Gecko/20060909 tells us that this user agent is using the Gecko layout engine. The following is the User-Agent header from an HTTP request made by Microsoft Internet Explorer 6.0. Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727) This user agent string is a little different. We can tell the user agent is Internet Explorer 6.0 and that it is running on Windows XP. We can also see that the user agent is running on a machine that has both the .NET 1.1 and .NET 2.0 frameworks installed. In an ideal world a Web server wouldn’t care what kind of user agent it was communicating with. After all, HTTP defines how the user agent and server negotiate authentication, what media types are supported, compression methods, and more. However, in practice that is rarely the case. Web developers rarely use HTTP to provide user agents with content in different languages or to automatically degrade to different image types based on the Accept header in the user agent’s request. Web servers typically don’t automatically send different content to Web browsers on a mobile device like a cell phone or 275 CHAPTER 10 REQUEST ORIGIN ISSUES a PDA. Web servers have to tailor some of their responses based on the user agent to get around known browser bugs. The X-Pad header is used to pad out HTTP responses to get around a bug in old Netscape browsers. More recently, Web browsers have to alter the MIME type in the Content-Type response header because of Internet Explorer’s failure to properly support XHTML documents. In short, modern Web servers do care about which user agent they are communicating with. REQUEST ORIGIN UNCERTAINTY AND JAVASCRIPT We have established that Web servers do care about the kind of user agent that is accessing them mainly for the purpose of working around bugs. Let’s turn our attention to how Web servers handle HTTP requests that come from a Web browser. Specifically, can a Web server tell the difference between requests generated by a human action and requests generated by JavaScript? By request generated by a human action we mean HTTP requests the Web browser sends when the user clicks on a hyperlink or submits a form. Requests generated by JavaScript include HTTP requests made from Image objects, XMLHttpRequest objects, or other programmatic requests, such as changing window.location. AJAX REQUESTS FROM THE WEB SERVER’S POINT OF VIEW Google Suggest is an Ajax-enhanced version of the popular search engine. As a user types in a search query, Google Suggest will display a drop-down menu of possible search terms that match the letters the user has already entered. Figure 10-2 shows Google Suggest in action. In this example, the user has typed ajax into the search field. Google Suggest uses JavaScript to hook the onkeydown event for the search text box. Every time the user types a character in the search field, a JavaScript function runs that uses the XMLHttpRequest object to contact a Web service running on Google’s Web site with the letters the user has typed so far. The Web service creates a list of search suggestions and provides the number of matches that start with the letters the user has supplied.2 2 It’s also worth pointing out how Google Suggest—the Ajax application—has twice the attack surface of the traditional Google search home page. Both accept a user’s search query through an HTML form, but Google Suggest also has a Web service that can be attacked using various parameter manipulations like SQL Injection, which is discussed in Chapter 3. 276 REQUEST ORIGIN UNCERTAINTY AND JAVASCRIPT Figure 10-2 Google Suggest uses Ajax to provide search suggestions as the user types in a search query. Let’s examine what the HTTP request made to Google Suggest Web service using the XMLHttpRequest object looks like. GET /complete/search?hl=en&client=suggest&js=true&qu=aj HTTP/1.0 Accept: */* Accept-Language: en-us Referer: http://www.google.com/webhp?complete=1&hl=en User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727) Host: www.google.com Cookie: PREF=ID=9c9a605343357bb4:TM=1174362920:LM=1174362920: S=v_i6EgUE0SZuWX1L We can tell from the User-Agent header that this request was made using Internet Explorer 6. Looking at the URL’s query string, we can see the parameter qu contains the letters the user has already typed. In this case, the user has typed aj. Also notice that the browser has automatically added a cookie for Google to the outgoing HTTP request that 277 CHAPTER 10 REQUEST ORIGIN ISSUES had already been saved in the browser from a previous visit to the search engine. As we mentioned in Chapter 8 “Attacking Client-Side Storage,” when investigating HTTP cookies as a client-side storage method, the browser automatically adds a relevant cookie onto the outgoing HTTP request. As expected, the browser added a Google cookie onto our request to Google. Because Google Suggest’s auto-completing Web service uses a simple HTTP GET with a query string instead of a more complex scheme, such as an HTTP POST with a SOAP or JSON content body, we can simply put this HTTP request directly into the browser’s address bar. This allows us to examine how the browser normally sends HTTP requests and compare it to the HTTP request for the XMLHttpRequest object. Below is the HTTP request Internet Explorer 6 sends when the URL for the suggestion Web service is typed directly into the address bar. GET /complete/search?hl=en&client=suggest&js=true&qu=aj HTTP/1.0 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Accept-Language: en-us User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727) Host: www.google.com Cookie: PREF=ID=9c9a605343357bb4:TM=1174362920:LM=1174362920: S=v_i6EgUE0SZuWX1L Examining the two requests reveals they are extremely similar. They use the same HTTP method, request the same URL, and use the same HTTP version. The HTTP headers that are common to both requests appear in the same order. The values of Accept-Language, User-Agent, and Host are all the same. Both requests have the appropriate cookie information. There are, in fact only two differences between the requests. The first difference is the HTTP request made from the address bar lacks an HTTP Referer header.3 This header normally shows what Web page contained a link to the page being requested. For example, if you were on main.html for a Web site and clicked a hyperlink to faq.html, the Referer header in the HTTP request for faq.html would be the fully-qualified URL for main.html. The request generated by the XMLHttpRequest object contains a Referer header set to http://www.google.com/webhp?complete=1&hl=en, which is the main Web 3 Yes, the word referrer is misspelled. However the word referrer was spelled as referer in the HTTP specification. So, when referring to the Referer header, we must also misspell it. 278 REQUEST ORIGIN UNCERTAINTY AND JAVASCRIPT page for Google Suggest. The HTTP request generated by typing the URL directly into the address bar of Internet Explorer lacks a Referer header because it didn’t come from anywhere; there was no referring document or Web page. For the same reason, when you request a Web page from a bookmark, the HTTP request that is sent also does not contain a Referer header. For now, just be aware that the missing Referer header is simply caused by manually typing in the URL. A Referer header is always present in HTTP requests made by normal user actions such as clicking on a hyperlink or submitting a form. Thus, the absence of the Referer header in this case is an accounted for and acceptable difference. The second difference between the two requests is the value of the HTTP Accept header. It has the value */* for the request made with XMLHttpRequest and a much longer string for the HTTP request made from typing the URL into the address bar.4 These default values are automatically added to the request by the browser. JavaScript is able to change the value of almost any HTTP request headers made for XMLHttpRequest objects using the setRequestHeader() function. The following piece of code modifies the value of the Accept header for HTTP requests generated by an XMLHttpRequest object so it exactly matches the value of the Accept header for normal browser HTTP requests. var xhr = getNewXMLHttpRequest(); xhr.setRequestHeader('Accept',' image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*'); We have shown that the HTTP requests generated by an XMLHttpRequest object differ only slightly from a normal HTTP request. The presence of a Referer header is not an issue and was an artifact of our test. The Accept header for HTTP requests for XMLHttpRequest objects can be modified to match a normal browser HTTP request. This leads us to an interesting conclusion: It is possible for JavaScript to create HTTP requests that look identical to the HTTP request a browser would send when a user submits a form or clicks on a hyperlink. They both would have the same HTTP headers and values (and these headers would appear in the same order), as well as the same authentication credentials and cookies. A Web server would be unable to distinguish between requests made in response to a user action and requests made by JavaScript. 4 It is rather odd that Internet Explorer 6 sends an Accept header with a long list of allowed MIME types only to include */* at the end of the list. The */* value means accept any MIME type; so having a list such as: image/gif, image/jpeg, …, */* is silly and redundant. It makes as much sense as saying “infinity + 1.” 279 CHAPTER 10 REQUEST ORIGIN ISSUES YOURSELF, OR SOMEONE LIKE YOU We have determined that JavaScript code can use XMLHttpRequest objects to generate an HTTP request that looks identical to the HTTP request for normal user activity like submitting a form. Because JavaScript is using XMLHttpRequest objects, these HTTP requests occur transparently and asynchronously. The browser is fully responsive during this time and, short of monitoring the browser’s traffic, a user would be completely unaware that the request is occurring. On top of that, the browser automatically adds any cached authentication credentials or cookies to these invisible requests. Finally, there is no way for the server to tell whether an incoming HTTP request was sent on behalf of an XMLHttpRequest object or because a human user submitted a form. The security implications of all of this are staggering, if not immediately apparent. If requests generated by JavaScript look like normal user activity, then a malicious piece of JavaScript could send all sorts of requests and the Web server would think that they were being sent by a legitimate user These HTTP requests could be used to initiate all kinds of actions on the server, such as transferring money, sending an email, adding a friend, editing a file, or reading an address book. This means that an attacker can use a Cross-Site Scripting (XSS) vulnerability to inject code that sends authenticated requests to the Web server masquerading as you. That injection code can perform various malicious actions. In Figure 10-3, we see a how such an XSS attack using an XMLHttpRequest object would work. User’s Request Attacker’s Requests (hidden) Figure 10-3 An XSS attack leverages the XMLHttpRequest object to make hidden malicious attack requests back to the Web server using a user’s credentials. 280 REQUEST ORIGIN UNCERTAINTY AND JAVASCRIPT Here a user visits a Web site that has been injected with malicious JavaScript using an XSS vulnerability. As soon as the page containing the malicious JavaScript is returned to the user’s browser it starts executing. The XSS uses the XMLHttpRequest object to send hidden, authenticated HTTP requests back to the Web server. The Web server, thinking the requests are legitimate requests from the user, performs the desired actions. What is interesting about this type of scenario is that a user cannot prove that she didn’t make a given HTTP request. In other words, if confronted by authorities, how could a user convince the authorities that she didn’t perform an evil action or make a malicious request? The malicious request came from the user’s computer and the user’s Web browser. The malicious request contains the cookie that uniquely identifies that user. The user was actively interacting with the Web site at the time the request occurred. This follows because the JavaScript that would make the malicious request had to be loaded. Short of investigators finding JavaScript on your computer or the Web server that created these malicious requests, it would be nearly impossible to prove that you did not initiate them.5 Here are some of the requests that could be processed: • Post a message to a Web forum containing racist or hateful language • Perform searches for child pornography • Transfer money out of your online bank account • Purchase an embarrassing item from an e-commerce store How well do you think a defense like, “I didn’t do it, JavaScript did.” will play out in a court of law? This problem is known as request origin uncertainty because the Web server is incapable of determining whether a request is receives was sent by malicious JavaScript code or by a legitimate user action. Request origin uncertainty is a huge problem. Did a virus transfer that money or send that email? Or did a person? While request origin uncertainty isn’t new to Ajax, we shall see that Ajax has made it far easier for malicious script to quickly send and respond to fraudulent HTTP requests. To fully understand the scope of damage from request origin uncertainty and using the XMLHttpRequest object to send HTTP requests masquerading as legitimate user actions, it is necessary to explore two questions. First, what pre-Ajax methods could JavaScript use to send requests to imitate 5 Of course, exactly what action the combination XSS/XHR attack performs is based on the type of Web site that has the XSS vulnerability. Obviously, an XMLHttpRequest object cannot contact third party Web sites because of the Same Origin Policy. Thus the combination XSS/XHR could only send a forged email if the XSS vulnerability was in an email application. 281 CHAPTER 10 REQUEST ORIGIN ISSUES a user’s actions? And second, what was the scope of damage or the limitations of these pre-Ajax methods?6 SENDING HTTP REQUESTS WITH JAVASCRIPT There are several ways JavaScript can cause the browser to issue an HTTP request. Each method has its own pros and cons that lend themselves to be used in different situations. In this chapter we ignore using JavaScript coupled with extra technologies, such as Java applets or Flash, and focus entirely on native JavaScript objects (or objects provided through ActiveX) and the DOM environment. One method JavaScript can use to send HTTP requests is dynamically creating new HTML tags. There are all sorts of HTML tag/attribute pairs that will issue an HTTP request when rendered by the browser. There are the obvious ones like IMG/SRC and IFRAME/SRC. But, there are also obscure ones like TABLE/BACKGROUND or INPUT/SRC. When the browser interprets these tags it issues an HTTP request to fetch the resource that was specified by the URL in the tag’s attribute. This URL can point to a Web site on the Internet and is not limited by the same origin policy. However, the same origin policy does mean these generated requests are a so-called blind request. A blind request means that JavaScript is not capable of seeing the response. This method can also be used only to issue GET requests. However, GET requests generated in this manner look exactly like GET requests issued by a normal user action like clicking on a hyperlink or submitting a HTML form that uses GET as the form’s method. The correct Referer header, cookies, and authentication information are all sent. This one way mechanism—of generating a request but not seeing the response—makes blind GETs a good vector to send information to a third party by placing it in the query string of a URL. For example, JavaScript could log keystrokes by trapping the keyboard events and use blind GETs to transmit the typed keys to a third party site to collect them. However, blind GETs are limited in the amount of data they can send to a third party through the query string due to the allowed length of a URL. While there are no explicit limits for a URL defined in any RFC, any URL longer than 2K to 4K will probably fail. This is because of the internal limits various Web browsers and Web servers impose. JavaScript could be used with a timer to create multiple blind GET requests that are necessary to walk through a multistage process such as a money transfer. There are numerous ways for JavaScript to dynamically create new HTML tags to generate an HTTP request. Creating new elements 6 Technically, the XMLHttpRequest object is a pre-Ajax method because it existed before the term Ajax did. However, when we refer to pre-Ajax methods for JavaScript to send HTTP requests, we mean ways JavaScript can send HTTP requests without using the XMLHttpRequest object. 282 REQUEST ORIGIN UNCERTAINTY AND JAVASCRIPT with document.createElement() and adding them to the DOM with appendChild() is one way. Adding raw tags using an element’s innerHTML property is another. JavaScript could even use document.open(), document.write(), and document.close() to dynamically add more tags. Regardless of the method JavaScript uses to add new tags with URL attributes, the resulting HTTP requests issued by the browser will look the same. JavaScript can also dynamically create FORM and INPUT tags. The INPUT tags can be populated with the desired form data, the FORM’s ACTION attribute set to any domain on the Internet, and the FORM’s METHOD attribute set to POST. All of this coupled with the form.submit() function allows JavaScript to send blind POSTs to arbitrary domains. Similar to the blind GETs just described, these POSTs are identical to POSTs generated when a user submits a form. If the domain to which the JavaScript is POSTing is the same as the domain it came from, JavaScript is able to access the response. Blind POSTs are a handy method to use when you need JavaScript to send a very large amount of data that would exceed the amount of data you can safely put in the query string of a URL. In addition to using HTML tags, the DOM provides JavaScript with objects that can issue HTTP requests. The Image object is supplied by the DOM environment and allows JavaScript to dynamically request images from any Web server. As soon as JavaScript sets the src property on the image object, a blind GET is issued to the URL that was assigned. This tends to be a better method to send blind GETs than creating HTML tags because you avoid the overhead of instantiating DOM elements. Also, when using an Image object, the response is not entirely blind. Events can be set to trap when the image has finished loading and see what size the image is. Depending on the browser used, an HTTP request for an Image object might not look exactly the same as normal user activity. Often with Image objects the Accept header will only contain image MIME types. Other than that, the requests are usually the same. Remote scripting is a technique where JavaScript is used to dynamically create new SCRIPT tags whose SRC attribute is a URL that points to a new JavaScript file. This URL can point to any domain on the Internet and is not bound by the same origin policy. The browser issues a GET request from the JavaScript file specified in the SRC attribute of the SCRIPT tag. As with the Accept header for Image objects, the Accept header for remoting scripting requests might be different from the Accept header for normal browsing behavior like clicking on a hyperlink or submitting a form. Typically the MIME type on these remote scripting requests only contains the MIME type for JavaScript, application/x-javascript. As mentioned in Chapter 7 “Hijacking Ajax Applications,” the content returned by a remote scripting request is evaluated by the JavaScript parser. This allows JavaScript to see the response to a remote scripting request, but only if the response is valid JavaScript. This is actually a very good property from a security perceptive. If remote scripting could be used to read the response of malicious requests even if 283 CHAPTER 10 REQUEST ORIGIN ISSUES the response wasn’t JavaScript, attackers could use remote scripting to steal data from third party sites like a user’s online bank or Web-based email. Requiring that a remote script response be valid JavaScript means Web sites must explicitly create a page that returns JavaScript to be accessed by third parties. JAVASCRIPT HTTP ATTACKS IN A PRE-AJAX WORLD So far we have seen that pre-Ajax JavaScript had numerous methods to send malicious requests. For the most part, these requests looked like normal user activity and the Web browser would automatically add on authentication credentials and cookies to the outgoing requests. In almost all instances, JavaScript was unable to access the response of these malicious requests. Table 10-1 summarizes the pros and cons of different techniques JavaScript can use to send HTTP requests. Table 10-1 The pros and cons of different techniques JavaScript can use to send HTTP requests Technique HTTP Methods Dynamically GET created HTML Dynamically GET, POST built FORM tag Image Object GET Remote GET Scripting ( BillysRareBooks.com AwesomeBooks.com GET/Reviews/51932 HTTP/1.1 Book. Good! Book. Good! Alice Eve Figure 11-12 Mashups must validate data they get from the source API as well.You cannot trust that the data is not malicious. 315 CHAPTER 11 WEB MASHUPS AND AGGREGATORS In Figure 11-12, we see that Eve has supplied a rather simple book review that contains a Cross-Site Scripting (XSS) attack. The Web developers at AwesomeBooks.com did not properly validate this input and stored the entire review, complete with XSS payload, inside their book database. Later, when BillysRareBooks.com fetches a book review for Alice, they retrieve this poisoned review from AwesomeBooks.com through the API and deliver it to Alice’s browser. Eve’s XSS attack executes and Alice’s browser is compromised. We should stress that this attack has nothing to do with the API offered by AwesomeBooks.com and consumed by BillysRareBooks.com. In fact, that API can be perfectly locked down and secured. The underlying problem is that BillysRareBooks.com blindly trusts a service that happens to deliver tainted data. As a developer, you have no idea where the data you are receiving from the source API ultimately comes from, and so you cannot trust that it is safe. All input, no matter what its source, should be validated. There are no exceptions to the input validation rule! Besides protecting yourself from malicious data (as if that is not important enough), there is another reason mashups should perform input validation on content from a third-party API: It’s good for business. Suppose that AwesomeBooks.com’s author database is intermittently throwing errors. If these ODBC error messages are returned to BillysRareBooks.com, input validation will detect that the data is not a list of books or a review. Your application can return a generic error that doesn’t air AwesomeBooks.com’s dirty laundry (or dirty connections strings or database version either). You can see this flow in Figure 11-13. Detecting the bad input also protects your visitor’s user experience from a disruptive and cryptic ODBC error message. Finally, detecting the problems allows BillysRareBooks.com to notify AwesomeBooks.com there is a problem. After all, BillysRareBooks.com has already paid for access that is now unavailable. Furthermore, because some of BillysRareBooks.com’s functionality depends on AwesomeBooks.com, it is in your best interest to notify AwesomeBooks.com to resolve the problem as quickly as possible. Search: “Emerson” User: “BillysBooks” Query: “Emerson” Alice “Unknown Response” ODBC Error! Database Timeout… BillysRareBooks.com AwesomeBooks.com Figure 11-13 Input validation of data coming from external APIs allows developers to detect when a service they have already paid for is unresponsive. 316 AGGREGATE SITES AGGREGATE SITES Mashups are only one kind of Web application that consumes the public APIs or resources of different Web applications. In recent years there has been a resurgence of Ajax portals, otherwise known as Ajax desktops or simply aggregate sites. These sites provide a type of personalized homepage that allows users to add custom content, such as RSS feeds, email summaries, calendars, games, and custom JavaScript applications. All this data is aggregated together in a single page for the user. NetVibes, iGoogle, and PageFlakes are all examples of aggregate sites. Figure 11-14 shows one such page. Figure 11-14 Aggregate sites take a diverse set of content, such as news feeds and email summaries, and load them into a single, personalized home page. These sites essentially aggregate multiple pieces of data and code from diverse sources and place them all together on the same page. We have a photo widget to talk to Flickr, an RSS feed from Memestreams, a game of Space Invaders, weather from the NOAA, and a calculator from who knows where. Everything is running under the same domain. In 317 CHAPTER 11 WEB MASHUPS AND AGGREGATORS Figure 11-15, we see how data is aggregated from multiple untrustworthy sources into a single page. Memestreams.net Gmail.com M Evil.com AggregateSite.com M Figure 11-15 Aggregate sites can load content from multiple untrusted sources into the same domain, creating a security risk. 318 AGGREGATE SITES There are some interesting security issues here. First, everything is in the same security domain. That means the Flickr widget can access the HTML of the Gmail widget. Figure 11-16 shows a real world example for the aggregate site Netvibes. The authors created an evil calculator widget that is able to steal email data from the legitimate Gmail widget. Figure 11-16 Netvibes.com has loaded a malicious widget into the same security context as the Gmail widget, allowing the malicious widget to a user’s steal email summaries. Not only do widgets have access to each of the presentational aspects of other widgets (e.g., the HTML markup, DOM elements, etc), but widgets can also access each other’s programming logic. This means that malicious widgets could use all the function hijacking techniques discussed in Chapter 7 to force other widgets to perform malicious activities as well. How do aggregate sites protect their users against these types of threats? One approach is to alert users to the dangers of using untrusted widgets. For example, NetVibes is aware that a malicious person could write a widget that steals data from other widgets, such as the evil proof of concept calculator widget the authors demonstrated in Figure 11-16. As such NetVibes displays numerous warnings to let a user know 319 CHAPTER 11 WEB MASHUPS AND AGGREGATORS they are adding an untrusted and potentially dangerous widget to their personalized page. This approach essentially places the burden of security on the users themselves. Users who make the decision to use third-party widgets could be harmed and it is up to the users to decide if they want to expose themselves to that risk. Unfortunately, history shows us that end users often make the wrong choice for a variety of reasons. For example, ActiveX controls included large notices warning users that the control could perform malicious actions, and users still downloaded them without fully understanding the security repercussions. In this section, we discuss a method to protect users from malicious third-party widgets, without requiring Web site visitors with varying degrees of technical savvy to make informed decisions about computer security. USER SUPPLIED WIDGETS As you know from Chapter 4, it is extremely difficult to tell whether a piece of JavaScript will perform malicious actions. Thus, if you are developing an aggregate site or a Web site that uses user-supplied widgets, chances are someone will eventually submit a malicious widget. Regardless of how you review or screen which widgets are allowed through and which aren’t, developers must to take steps to isolate widgets from each other. This is another defense in depth strategy, because even if a malicious widget gets through your screening process, you have minimized the amount of damage it can do to other widgets. The fundamental problem with aggregate sites is that different resources for numerous and possibly untrusted or malicious sources are placed into the same security domain where they can tamper with one another. The solution to this problem is obvious: Don’t place content from multiple untrusted sources in the same security domain. If the widgets are not in the same domain, JavaScript’s Same Origin Policy prohibits them from communicating with each other. We can accomplish this using a method known as IFrame jails. With this approach, each widget is loaded inside of its own iframe tag. The SRC attribute of each iframe is a randomly generated subdomain of the aggregator site. At the time of publication, NetVibes applies IFrame jails in certain situations. In our above example, we created a widget in a scenario where NetVibes did not apply IFrame jailing. For this section we will hypothetically extend NetVibes to implement complete IFrame jailing to illustrate the common mistakes developers make when defending against various attacks. NetVibes does not actually do any of this. Figure 11-17 shows hypothetical IFrame jails applied to NetVibes.7 In this example we discuss various attacks 7 At the time of publication NetVibes only uses IFrame jailing in certain situations. 320 AGGREGATE SITES and defenses using NetVibes as the sample site. In this example each widget is in its own iframe with a random five-digit subdomain. We can see the evil calculator widget cannot access data from other widgets because of the Same Origin Policy in JavaScript. Figure 11-17 Loading widgets into iframes with random subdomains “jails” them, preventing hot interwidget access. Typically, IFrame jails are implemented with the aggregate site—for example, http://aggregate.com—pushing down a Web page with iframe tags and then loading the widget using HTML such as