On my latest project, I was faced with a challenge: build Flash widgets that displayed dynamic data and could be embedded on any web page. Phase two of the widgets called for user interaction with the widget, as opposed to simply displaying content. It seemed that Flex would be the most logical technology to use for this project.
I used the Services and AMFPHP modules for Drupal to expose content via web services for my Flex widget to consume. However, I ran into a problem with the critical piece that Flex needs in order to get remote data. As a workaround to the browser security settings that prevent cross-site scripting, Adobe chose to implement an opt-in solution in the form of a file called crossdomain.xml. With crossdomain.xml, a site owner may allow a list of domains to read its data and the client, Flash in this case, is responsible for enforcement. As the business case for the project called for the widget to be embedded on any domain, I needed to use a promiscuous crossdomain policy, allowing access from all domains:
-
<cross-domain-policy>
-
<allow-access-from domain="*"/>
-
</cross-domain-policy>
Because of this policy, the widget was allowed to read all data on the site that the user has access to, including any (authenticated) content and (session) cookies. This could easily lead to privacy violations, account takeovers, theft of sensitive data, and bypassing of CSRF protections.
Other domains in a similar predicament have simply hosted their APIs on a different domain, thus preventing access to user data on the root domain. Unfortunately, the nature of Drupal doesn't allow for splitting the Services functionality out from the rest of the platform.
My solution was to write a forwarder, which is just a really simple PHP script, that is hosted on a subdomain. This subdomain hosts the promiscuous crossdomain policy, and the policy file on the root domain is configured to only accept requests from the subdomain. The forwarder does two things: it first makes sure that only requests to /services/amfphp are allowed; if allowed, the $HTTP_RAW_POST_DATA is sent to the root domain via cURL.
-
<?php
-
// Configuration variables
-
$server = 'http://mydomain.com/services/amfphp';
-
-
if ($request_uri[0] === '/'){
-
}
-
-
// Split the uri into components
-
-
// Filter out unwanted requests
-
if(($handler != 'services') && ($protocol != 'amfphp')){
-
}
-
-
// Handle the post from the flash/flex client
-
$xml = $HTTP_RAW_POST_DATA;
-
}
-
-
// Set the headers
-
$header[] = "Content-type: text/xml";
-
-
$ch = curl_init($server);
-
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
-
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
-
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
-
-
$response = curl_exec($ch);
-
-
if (curl_errno($ch)) {
-
}
-
else {
-
curl_close($ch);
-
}
-
print $response;
-
}
-
-
-
?>
Because the subdomain doesn't host any data at all, the security risk has been removed. If you are working with Drupal and Flash remoting, you'll need to consider the risks associated with promiscuous crossdomain policy files. While my solution certainly isn't the only solution, it is pretty effective and simple.
tags: adobe, crossdomain.xml, curl, drupal, flash, flex, forwarder, policy, security, vulnerability, widget, xml
