|By Jackson Moore||
|October 4, 2001 12:00 AM EDT||
To secure your Web-based application you must close all known holes in your hardware and software as well as those you inadvertently open in your application's code.
This article addresses possible holes in your ColdFusion code that result from explicitly trusting the data your code accepts from URL parameters, form fields, cookies, browser variables, databases, or other external data sources. You must take measures to ensure that data from these sources won't cause your application to display improperly, crash, permit a security breach, or allow unintended server-side operations to be performed.
Although the exploits described in this article aren't specific to ColdFusion and many have been around for years, we'll examine ColdFusion practices for protecting your application, including data validation, encryption, and data integrity.
Untrusted Data Sources
If you conceptualize your application as a black box, any data entering that box should be validated before being processed. For Web-based applications, common sources of input that should be validated include URL parameters, form fields, cookies, browser variables, and databases.
With URLs a user can simply add, change, or delete URL parameters in the query string. Form fields might not be as obvious, but it's a trivial exercise to save a form-based HTML page on your computer, change hidden form fields, or bypass client-side validation and resubmit the form.
Likewise, a cookie can be easily manipulated by using a text editor to make a few careful changes. CGI variables that originate in the browser ("cgi.http_referer") must also be treated as suspect since they can be spoofed or blocked.
Last, and maybe least considered, are the problems that can occur by not validating data retrieved from databases. Unless yours is the only database application, your application can't blindly trust the data in the database. What would happen if another application inserted a username containing a "<" character into the database?
These are the biggest sources of untrusted data, but you must audit your own situation carefully. Other sources of untrusted data are CFFILE, CFHTTP, COM objects, and any other means of accessing data outside the direct control of your application.
What Can Happen
Improper HTML Display
Perhaps the most benign of holes, allowing user input to affect the display of your page is not only unprofessional, but is a clue to serious hackers that little or no security precautions are in place.
If you have a search form that allows users to search your Web site, you might display a response to the user such as "Your search for 'ColdFusion' resulted in a gazillion matches." But what happens if your user searches for something like "</TD>" or "<B>" or simply a "<"? This may cause your page to display improperly - reformatting your text or omitting portions of your page altogether. Furthermore, some browsers are known to crash when parsing ill-formed HTML.
Improper display can also be caused when pulling data from a database that you don't exclusively control. A database of magazine articles may contain characters that cause display problems if you don't validate the data after you retrieve it from the database.
Bypassed Client-Side Validation
Since all client-side validation, by definition, takes place on the client, a user can bypass validation code and submit the form with nonvalidated data. Users could have scripting disabled in their browser or bypass the validation code manually after saving the form to their computer.
As mentioned, "cgi.http_referer" (or any other client-side variable) can't be relied upon to verify the origin of a form submission. "cgi.http_referer" can be spoofed to make you believe that the form was submitted from your site or blocked (by a firewall or privacy software), preventing valid users from submitting forms.
For this example let's assume you have a message board where users can read and post messages, and a user enters the following:
If you're not validating this text field, every user that views your message board will be "treated" to a new window that originates from and appears to be authorized by your site. Other potentially damaging HTML tags include <OBJECT> and <APPLET>.
Serious attackers will look for any in-formation about your server configuration that allows them to identify potential holes. Those unfriendly ColdFusion error messages (see Figure 1) that are so helpful when debugging an application are a gold mine to a hacker looking to have some fun.
As you can see from this figure, the user is presented with an error message revealing a physical path on the server.
The amount of critical information displayed will depend on the type of error, your ColdFusion server settings, server platform, and database server.
Even if you employ an authentication framework, you must still validate data received from the client to prevent unauthorized access to restricted areas of your Web site. Consider a situation where you design a Web site for a magazine that wants to restrict some of the articles to subscribers only.
- Case 1: <a href="display.cfm? article_ id=14">Cover Story</a>
- Case 2: <a href="articles/2001/july/ cover.htm">Cover Story</a>
Unintended Server Operations
Holes that allow unauthorized server-side operations to be performed are the most disastrous. Some databases, for example, support multiple SQL statements inside a CFQUERY. Building on the magazine example we might construct the following query:
<cfquery name="qArticles" datasource="testDB">
SELECT article_copy FROM articles
WHERE article_id = #url.article_id#
This works fine if the URLs submitted by the user have valid article numbers, but not URLs like this:
With Microsoft SQL Server (among others) this user just deleted every record in the "articles" table. Granted, the user must know the name of your table, but through the error messages mentioned earlier or simply guessing common table names, this isn't difficult.
You need to analyze your other server software for similar "features." For example, Allaire Security Bulletin ASB99-09 (www.allaire.com/handlers/index.cfm?id=11069&method=full) warns of an issue with Microsoft Access that allows users to append VBA commands to a SQL string.
Any server software your application interfaces with that supports scripting needs to be audited for related patches. If you validate all user input, however, you're likely to avoid these vulnerabilities.
What You Can Do
ColdFusion includes two built-in methods for validating data received from form data: server-side validation using hidden <INPUT> tags and client-side validation using CFINPUT. Both methods are designed for validating form data, but, as you know, form data is just one of the many untrusted data sources you must contend with.
That said, these techniques are useful in some cases and provide built-in validation for required fields, data types such as integer and date, as well as data values such as credit card and social security numbers. The online help provides documentation and examples for each method.
Use Server-Side Validation
Furthermore, client-side validation can't perform some data validation such as preventing duplicate database records or testing uploaded files. The advantage to client-side scripting is that it provides immediate feedback to the user without a round-trip to the server for validation.
<cfinput type="text" name="firstname" required="yes">
If you don't manually scope your variables, ColdFusion looks for them in various scopes in the following order: local variables, CGI, file, URL, form, cookie, and client variables. If you don't scope variables in your code, you won't know whether they're coming from the URL, a form, or a cookie. Scoping your variables will also make your code easier to follow and give you a slight performance boost.
To prevent the display of error messages that may reveal information about your server configuration, use structured error handling (with CFTRY/CFCATCH blocks) and/or site-wide error handlers (CFERROR). This will allow you to trap any errors and continue processing your page accordingly.
Disabling the "Display the template path in error messages" setting in the ColdFusion Administrator will prevent physical paths from being displayed in most, but not all (see Figure 1), error messages that do get through to the user.
For more information on handling errors look through your back issues of CFDJ for the "Toward Better Error Handling" series (Vol. 2, issues 10 and 12, and Vol. 3, issue 2) by Charles Arehart. These are good articles that will help even beginners get up to speed on error handling.
Data Type Validation
Perhaps the single most important validation to perform is to verify the data type of suspicious data - making sure, for example, that a variable that's supposed to be a number is actually a number. This ensures that all subsequent processing of this type of data won't throw an error by performing a date operation on a nondate value or by trying to insert a nonnumeric value into a numeric database column.
Two useful tags for this purpose are CFPARAM and CFQUERYPARAM. With CFPARAM, specify the "type" attribute with any of the following data types: array, binary, boolean, date, numeric, query, string, struct, or uuid.
<cfparam name="url.myValue" type="numeric">
If the value of "url.myValue" is anything other than numeric, ColdFusion will throw an error. Using this in a meaningful way will require the use of CFTRY/CFCATCH blocks to catch the error and handle it properly.
CFQUERYPARAM tags are nested within CFQUERY tags and will give you some control over data validation. Refer to the ColdFusion documentation for CFQUERYPARAM for a list of SQL data types that can be validated. Again, ColdFusion throws an error if the validation fails.
ColdFusion also has several built-in functions (IsDate(), IsNumeric(), etc.) that allow you to determine data types without using error handling. If you need more specific data-type validations (such as integer or hexadecimal), you'll have to write your own validation code.
Another helpful ColdFusion function for validating numeric data types is Val(). Val() will return a number from the beginning of a string or 0 if the string can't be converted to a number. This function is particularly handy when dealing with a peculiar aspect of ColdFusion's support for scientific notation.
ColdFusion treats the value "1D2" the same as "1E2" - both are scientific notations for "100". ColdFusion considers both values to be valid numeric values, but your database probably won't even if it supports scientific notation. Using the Val() function will ensure that the value is converted to the more recognized scientific notation format:
#val("1d2")# <!--- 100 --->
#val("1d13")# <!--- 1E+013 --->
For example, if your user enters his or her age into a form as "3D1" (30), you want to make sure that inserting this value into your database won't cause an error even though this is a valid number in ColdFusion.
Data Value Validation
While data-type validation should prevent errors in subsequent ColdFusion code, it doesn't ensure that the value of the data is acceptable. Consider again our example of the magazine Web site. Some articles are available to the general public, while others are available only to subscribers.
If the user manually changes "article_id" to a different (but valid) integer, should the user be presented with the article carte blanche? In this example the answer is no. For each article the user requests, we must authenticate and verify that he or she is allowed to view the requested article. You can't assume that users won't try to guess new paths or values for URL parameters, form fields, or cookies in order to access restricted portions of your Web site.
Another case where data value validation is critical is when the range of legal data values for a data type in ColdFusion differs from your database. ColdFusion, for example, considers dates as large as December 31, 9999, as valid dates. In SQL Server 7, the "datetime" data type will accept this value, but the "smalldatetime" data type won't. To prevent database errors with date data types, you need to ensure that the date value is within an acceptable range for your database column.
There are many other reasons why you need to validate the value of a piece of data, not just its data type. Preventing duplicate records in a database or making sure the length of a string value is within acceptable minimum and maximum lengths are additional examples of when the value of the data must be validated. The bottom line is that (just like data types) you can't make any assumptions about the value of data received from a client.
Defining Character Sets
Validating string data presents its own set of unique challenges since there are many special characters reserved by ColdFusion, URLs, HTML, and database software. Because ColdFusion doesn't have any built-in validation functionality like Perl's taint-checking mechanism, it's the developer's responsibility to perform all necessary string validations.
In nearly all cases you'll protect yourself better by first defining which characters to allow and then making sure that the suspect string contains only those characters. This is preferable to defining which characters are illegal (though this may seem easier to implement), because it's very easy to forget one and it reduces your vulnerability to (as yet) unknown exploits.
As an example, let's consider a form field that accepts a username field and requires all usernames to contain only letters. It's safer to verify that only letters are submitted as opposed to checking for all possible illegal characters. You can use a regular expression to verify that no other character is allowed:
<cfif refindnocase("[^a-z]",form.username) neq 0>
<!--- invalid username --->
The validation becomes more complicated for string data that has to be more flexible. Our example of the cross-site scripting exploit was due to a developer allowing <SCRIPT> tags in message board posts. This particular example could have been avoided by defining a character set that excluded the "<" and ">" characters. But what if you want to allow users to use <B>, <I>, and <U> for formatting purposes within their message post? Now you have to allow some tags but not others.
As with defining single character sets, you're far safer determining which HTML tags to allow as opposed to identifying the tags that may potentially cause problems. I take issue with some of the available custom tags that focus on which HTML tags to disallow because future additions to the HTML specification or browser-specific tags may require you to revisit your code.
If you want to allow bold, italic, and underline formatting in your message posts, allow only those and disallow all others. The following code will remove all tags except for <B></B>, <I></I>, and <U></U>:
This, of course, won't validate the HTML to ensure it's well formed. Limiting the allowable HTML tags will minimize the amount of validation needed since you don't want to have errors caused by a user who failed to provide an end tag or improperly nested certain tags. If the user leaves out a "</B>" tag, you won't have a problem if the message post is encapsulated in a table cell. However, a misplaced <DIV> or </TD> can really wreak havoc on the display of your page or crash the browser.
Another approach is to encrypt the data that appears in URLs, form fields, and cookies. Consider the earlier example of the magazine Web site: if we encrypt "article_id" our link might look like:
The new value of "article_id" makes it less obvious to the user how "article_id" is used. Curious users may still tamper with the value, but after failing once or twice will give up.
ColdFusion offers two complementary functions, Encrypt() and Decrypt(), to perform encryption. They both take a string to encrypt as well as a key to use in the encryption process.
Encrypt()'s XOR-based encryption, though useful, is weak compared to encryption methods (such as that used in 128-bit SSL) considered secure by today's standards. Encrypt() shouldn't be used for sensitive information (credit card numbers or medical information), but it can be used to prevent most attempts to casually change "nonsensitive" values in URLs, form fields, and cookies.
Strings encrypted with Encrypt() can contain many potentially damaging characters (new lines, form feeds, backspaces, greater-than signs, carriage returns, etc.):
encrypt("Macromedia","key") = *=(IX 0B@5M%$V#\!
The last character (which you can't see) is a new line character. Practical use of the Encrypt() function will generally require another encoding step such as URLEncodedFormat() to escape all illegal characters in the string. Your decryption process then becomes a two-step process as well - decode and decrypt.
Another drawback to the Encrypt() function is that there's no data integrity check to ensure that the encrypted string hasn't been tampered with. Using the above example I decrypted the encrypted string, but changed the number 5 to 2:
decrypt("*=(IX 0B@2M%$V#\! ","key") = Macromydia
As you can see, the decrypted string was successfully decrypted but resulted in a value different from the original.
At the risk of losing some objectivity, I'll mention a tag I wrote called CF_CRYP (available from the Developers Exchange, http://devex.allaire.com/developer/gallery/). CF_CRYP builds on the default encryption of the Encrypt() function, but encodes the resulting string and adds a checksum.
The result is an encrypted string containing only numbers and letters with a checksum. Changing a single character in a CF_CRYP-encrypted string can be detected because the checksum of the decrypted value won't match the checksum of the original unencrypted value. CF_CRYP also provides a return structure with error information so you can detect tampering and, potentially, block that user from further access.
Using CF_CRYP to encrypt a value of "14" with a key of "somekey" produces "0101J442509E78541". To access the subscriber-only articles, the user would have to know the encoding scheme, key, and recalculate the checksum. This is a good solution for this example because if the encryption scheme was broken, the worst that can happen is an unauthorized user views some subscriber-only articles. If this happens, you should probably give the user a free subscription to your magazine anyway!
CFCONTENT will allow you to place content outside of your Web root directory and still send the content to a user. This is preferable when the files (PDF documents, video clips, etc.) require some type of user authentication to access. Since these files exist on your server outside of the Web root, users can't access the file with any URL. To view the file they have to access a page where you should authenticate them and use CFCONTENT to deliver the file.
Since many of the most damaging server-side operations are unauthorized database operations, your database authentication framework should be audited. Carefully review the operations that users need to be able to perform and then limit the SQL operations that can be performed under that user account. Generally speaking, regular users should be restricted to SELECT statements and, in some cases, UPDATE and INSERT.
Even if a user manages to get multiple commands through to your server, only those operations authorized for this user would be allowed. You must combine this type of security with the other guidelines in this article since even a benign SELECT operation can allow a user to view unauthorized content. If you use other server-side applications (including COM objects), take advantage of any built-in user authentication.
Most databases will also allow you to perform data validation at the database level. This is a good solution when you have numerous applications inserting data into a database. By moving some data validation to the database, you can remove redundant validation routines from each application.
Most users are honest people using your application for the purpose it was designed for. However, a small group of users (some armed with homegrown HTTP clients) will attempt to exploit any holes that exist in your code. Form fields, cookies, and browser variables are just as likely as URL parameters (maybe more so since they're less obvious to inexperienced developers) to contain hack attempts.
Taken together, the techniques presented in this article will help you protect your application from any malicious data sent by a user. Whether the data is tampered with accidentally or on purpose, it's your job as the developer to close these code-level holes.
- "Security Best Practice: Validating Browser Input": www.allaire.com/handlers/index.cfm?id=14558&method=full
- "Cross-Site Scripting Vulnerability Information": www.allaire.com/handlers/index.cfm?id=14557&method=full
- "Multiple SQL Statements in Dynamic Queries":www.allaire.com/handlers/index.cfm?id=8728&method=full
- "Understanding Malicious Content Mitigation for Web Developers": www. cert.org/tech_tips/malicious_code_mitigation.html
For IoT to grow as quickly as analyst firms’ project, a lot is going to fall on developers to quickly bring applications to market. But the lack of a standard development platform threatens to slow growth and make application development more time consuming and costly, much like we’ve seen in the mobile space. In his session at @ThingsExpo, Mike Weiner, Product Manager of the Omega DevCloud with KORE Telematics Inc., discussed the evolving requirements for developers as IoT matures and conducted a live demonstration of how quickly application development can happen when the need to comply wit...
Jul. 31, 2015 08:45 AM EDT Reads: 301
Explosive growth in connected devices. Enormous amounts of data for collection and analysis. Critical use of data for split-second decision making and actionable information. All three are factors in making the Internet of Things a reality. Yet, any one factor would have an IT organization pondering its infrastructure strategy. How should your organization enhance its IT framework to enable an Internet of Things implementation? In his session at @ThingsExpo, James Kirkland, Red Hat's Chief Architect for the Internet of Things and Intelligent Systems, described how to revolutionize your archit...
Jul. 30, 2015 07:30 PM EDT Reads: 1,395
SYS-CON Events announced today that HPM Networks will exhibit at the 17th International Cloud Expo®, which will take place on November 3–5, 2015, at the Santa Clara Convention Center in Santa Clara, CA. For 20 years, HPM Networks has been integrating technology solutions that solve complex business challenges. HPM Networks has designed solutions for both SMB and enterprise customers throughout the San Francisco Bay Area.
Jul. 30, 2015 03:45 PM EDT Reads: 448
MuleSoft has announced the findings of its 2015 Connectivity Benchmark Report on the adoption and business impact of APIs. The findings suggest traditional businesses are quickly evolving into "composable enterprises" built out of hundreds of connected software services, applications and devices. Most are embracing the Internet of Things (IoT) and microservices technologies like Docker. A majority are integrating wearables, like smart watches, and more than half plan to generate revenue with APIs within the next year.
Jul. 30, 2015 02:30 PM EDT
The Internet of Everything (IoE) brings together people, process, data and things to make networked connections more relevant and valuable than ever before – transforming information into knowledge and knowledge into wisdom. IoE creates new capabilities, richer experiences, and unprecedented opportunities to improve business and government operations, decision making and mission support capabilities.
Jul. 30, 2015 01:45 PM EDT Reads: 266
Growth hacking is common for startups to make unheard-of progress in building their business. Career Hacks can help Geek Girls and those who support them (yes, that's you too, Dad!) to excel in this typically male-dominated world. Get ready to learn the facts: Is there a bias against women in the tech / developer communities? Why are women 50% of the workforce, but hold only 24% of the STEM or IT positions? Some beginnings of what to do about it! In her Opening Keynote at 16th Cloud Expo, Sandy Carter, IBM General Manager Cloud Ecosystem and Developers, and a Social Business Evangelist, d...
Jul. 30, 2015 12:00 PM EDT Reads: 2,053
In his keynote at 16th Cloud Expo, Rodney Rogers, CEO of Virtustream, discussed the evolution of the company from inception to its recent acquisition by EMC – including personal insights, lessons learned (and some WTF moments) along the way. Learn how Virtustream’s unique approach of combining the economics and elasticity of the consumer cloud model with proper performance, application automation and security into a platform became a breakout success with enterprise customers and a natural fit for the EMC Federation.
Jul. 30, 2015 09:00 AM EDT Reads: 2,158
The Internet of Things is not only adding billions of sensors and billions of terabytes to the Internet. It is also forcing a fundamental change in the way we envision Information Technology. For the first time, more data is being created by devices at the edge of the Internet rather than from centralized systems. What does this mean for today's IT professional? In this Power Panel at @ThingsExpo, moderated by Conference Chair Roger Strukhoff, panelists addressed this very serious issue of profound change in the industry.
Jul. 29, 2015 03:00 PM EDT Reads: 1,272
Discussions about cloud computing are evolving into discussions about enterprise IT in general. As enterprises increasingly migrate toward their own unique clouds, new issues such as the use of containers and microservices emerge to keep things interesting. In this Power Panel at 16th Cloud Expo, moderated by Conference Chair Roger Strukhoff, panelists addressed the state of cloud computing today, and what enterprise IT professionals need to know about how the latest topics and trends affect their organization.
Jul. 29, 2015 02:00 PM EDT Reads: 1,184
It is one thing to build single industrial IoT applications, but what will it take to build the Smart Cities and truly society-changing applications of the future? The technology won’t be the problem, it will be the number of parties that need to work together and be aligned in their motivation to succeed. In his session at @ThingsExpo, Jason Mondanaro, Director, Product Management at Metanga, discussed how you can plan to cooperate, partner, and form lasting all-star teams to change the world and it starts with business models and monetization strategies.
Jul. 28, 2015 04:30 PM EDT Reads: 1,763
Converging digital disruptions is creating a major sea change - Cisco calls this the Internet of Everything (IoE). IoE is the network connection of People, Process, Data and Things, fueled by Cloud, Mobile, Social, Analytics and Security, and it represents a $19Trillion value-at-stake over the next 10 years. In her keynote at @ThingsExpo, Manjula Talreja, VP of Cisco Consulting Services, discussed IoE and the enormous opportunities it provides to public and private firms alike. She will share what businesses must do to thrive in the IoE economy, citing examples from several industry sectors.
Jul. 28, 2015 11:00 AM EDT Reads: 2,040
There will be 150 billion connected devices by 2020. New digital businesses have already disrupted value chains across every industry. APIs are at the center of the digital business. You need to understand what assets you have that can be exposed digitally, what their digital value chain is, and how to create an effective business model around that value chain to compete in this economy. No enterprise can be complacent and not engage in the digital economy. Learn how to be the disruptor and not the disruptee.
Jul. 27, 2015 10:00 AM EDT Reads: 2,029
Akana has released Envision, an enhanced API analytics platform that helps enterprises mine critical insights across their digital eco-systems, understand their customers and partners and offer value-added personalized services. “In today’s digital economy, data-driven insights are proving to be a key differentiator for businesses. Understanding the data that is being tunneled through their APIs and how it can be used to optimize their business and operations is of paramount importance,” said Alistair Farquharson, CTO of Akana.
Jul. 27, 2015 09:00 AM EDT Reads: 323
Business as usual for IT is evolving into a "Make or Buy" decision on a service-by-service conversation with input from the LOBs. How does your organization move forward with cloud? In his general session at 16th Cloud Expo, Paul Maravei, Regional Sales Manager, Hybrid Cloud and Managed Services at Cisco, discusses how Cisco and its partners offer a market-leading portfolio and ecosystem of cloud infrastructure and application services that allow you to uniquely and securely combine cloud business applications and services across multiple cloud delivery models.
Jul. 27, 2015 08:00 AM EDT Reads: 1,901
The enterprise market will drive IoT device adoption over the next five years. In his session at @ThingsExpo, John Greenough, an analyst at BI Intelligence, division of Business Insider, analyzed how companies will adopt IoT products and the associated cost of adopting those products. John Greenough is the lead analyst covering the Internet of Things for BI Intelligence- Business Insider’s paid research service. Numerous IoT companies have cited his analysis of the IoT. Prior to joining BI Intelligence, he worked analyzing bank technology for Corporate Insight and The Clearing House Payment...
Jul. 26, 2015 09:00 PM EDT Reads: 1,573
"Optimal Design is a technology integration and product development firm that specializes in connecting devices to the cloud," stated Joe Wascow, Co-Founder & CMO of Optimal Design, in this SYS-CON.tv interview at @ThingsExpo, held June 9-11, 2015, at the Javits Center in New York City.
Jul. 25, 2015 02:00 PM EDT Reads: 388
SYS-CON Events announced today that CommVault has been named “Bronze Sponsor” of SYS-CON's 17th International Cloud Expo®, which will take place on November 3–5, 2015, at the Santa Clara Convention Center in Santa Clara, CA. A singular vision – a belief in a better way to address current and future data management needs – guides CommVault in the development of Singular Information Management® solutions for high-performance data protection, universal availability and simplified management of data on complex storage networks. CommVault's exclusive single-platform architecture gives companies unp...
Jul. 25, 2015 01:00 PM EDT Reads: 1,957
Electric Cloud and Arynga have announced a product integration partnership that will bring Continuous Delivery solutions to the automotive Internet-of-Things (IoT) market. The joint solution will help automotive manufacturers, OEMs and system integrators adopt DevOps automation and Continuous Delivery practices that reduce software build and release cycle times within the complex and specific parameters of embedded and IoT software systems.
Jul. 25, 2015 12:15 PM EDT Reads: 460
"ciqada is a combined platform of hardware modules and server products that lets people take their existing devices or new devices and lets them be accessible over the Internet for their users," noted Geoff Engelstein of ciqada, a division of Mars International, in this SYS-CON.tv interview at @ThingsExpo, held June 9-11, 2015, at the Javits Center in New York City.
Jul. 25, 2015 12:00 PM EDT Reads: 1,535
Internet of Things is moving from being a hype to a reality. Experts estimate that internet connected cars will grow to 152 million, while over 100 million internet connected wireless light bulbs and lamps will be operational by 2020. These and many other intriguing statistics highlight the importance of Internet powered devices and how market penetration is going to multiply many times over in the next few years.
Jul. 25, 2015 09:00 AM EDT Reads: 1,486