Security: Restrict plugin access to file system and network - actionscript-3

In a plugin context (a swf loaded by an another swf), is there any way to restrict access to file system and network in the same time to the loaded swf ?
Compiler option "-use-network=true|false" does not fit because you cannot restrict both file/network.
Code example :
Air App :
package
{
import flash.display.Loader;
import flash.display.Sprite;
import flash.filesystem.File;
import flash.net.URLRequest;
public class TestContentSecurity extends Sprite
{
private var l :Loader = new Loader;
public function TestContentSecurity()
{
addChild(l);
l.load(new URLRequest(File.documentsDirectory.nativePath + "/Content.swf"));
}
}
}
Loaded swf :
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.text.TextField;
public class Content extends Sprite
{
private var _log : TextField = new TextField;
private var l: URLLoader;
public function Content()
{
addChild(_log)
_log.multiline = true;
_log.width = 500;
_log.height = 500;
l = new URLLoader();
l.addEventListener(Event.COMPLETE, onLoad);
l.addEventListener(IOErrorEvent.IO_ERROR, onError);
l.load(new URLRequest("c:/Windows/regedit.exe"))
}
public function onLoad(e:Event) : void{
_log.text += "SUCCESS\n" ;
}
public function onError(e:IOErrorEvent) : void{
_log.text += "ERROR\n";
}
}
}
The loaded swf is in user's document folder, outside Air app folder. Currently, the loaded swf is abble to load "c:/Windows/regedit.exe" and I don't want it (neither sending informations on the network).

I've found one solution in AIR, I don't like it but it works. The idea is to have a mini http server and to load content from this server.
I load targeted file with :
new URLRequest("http://localhost:1111/Content.swf")
By doing this, flash will load "Content.swf" as a remote file and place it in a REMOTE security sandbox. Loaded swf won't be able to access to any local files neither to network.
If anyone have a cleaner solution to get this REMOTE security sand box, I will be happy.
/**
* HTTP server original idea :
* http://coenraets.org/blog/2009/12/air-2-0-web-server-using-the-new-server-socket-api/
*/
package
{
import flash.display.Loader;
import flash.display.Sprite;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.events.ServerSocketConnectEvent;
import flash.net.ServerSocket;
import flash.net.Socket;
import flash.utils.ByteArray;
public class TestContentSecurity extends Sprite
{
private var l :Loader = new Loader;
private var serverSocket:ServerSocket;
public function TestContentSecurity()
{
init();
l.load(new URLRequest("http://localhost:1111/Content.swf"));
}
private function init():void
{
// Initialize the web server directory (in applicationStorageDirectory) with sample files
listen(1111);
}
private function listen(port : uint):void
{
try
{
serverSocket = new ServerSocket();
serverSocket.addEventListener(Event.CONNECT, socketConnectHandler);
serverSocket.bind(port, "127.0.0.1");
serverSocket.listen();
trace("Listening on port " + port + "...\n");
}
catch (error:Error)
{
trace("Port " + port +
" may be in use. Enter another port number and try again.\n(" +
error.message +")", "Error");
}
}
private function socketConnectHandler(event:ServerSocketConnectEvent):void
{
var socket:Socket = event.socket;
socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
}
private function socketDataHandler(event:ProgressEvent):void
{
try
{
var socket:Socket = event.target as Socket;
var bytes:ByteArray = new ByteArray();
socket.readBytes(bytes);
var request:String = "" + bytes;
var filePath:String = request.substring(5, request.indexOf("HTTP/") - 1);
var file:File = File.applicationDirectory.resolvePath(filePath);
if (file.exists && !file.isDirectory)
{
var stream:FileStream = new FileStream();
stream.open( file, FileMode.READ );
var content:ByteArray = new ByteArray();
stream.readBytes(content);
stream.close();
socket.writeUTFBytes("HTTP/1.1 200 OK\n");
socket.writeUTFBytes("Content-Type: application/x-shockwave-flash\n\n");
socket.writeBytes(content);
}
else
{
socket.writeUTFBytes("HTTP/1.1 404 Not Found\n");
socket.writeUTFBytes("Content-Type: text/html\n\n");
socket.writeUTFBytes("<html><body><h2>Page Not Found</h2></body></html>");
}
socket.flush();
socket.close();
}
catch (error:Error)
{
trace("Error");
}
}
}
}

Not unless you deploy as an AIR application.
You can, however, store some data in a SharedObject, even when you deploy with -use-network=true. This should work for storing game state and such.
Edit:
In AIR, security between content from different domains is regulated by using AIR sandbox bridges. This should give you all the leverage you need.

Related

URLLoader doesn't load data from URLRequest. No error messages

I'm trying to load data from a website in my AS3 program. However, urlLoader.load() never finishes, yet the program runs without any errors.
Here's my code:
Main.as:
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import ttTextField;
public class Main extends Sprite
{
private var textField:ttTextField = new ttTextField("", false);
public function Main():void
{
var urlRequest:URLRequest = new URLRequest("http://itch.io/api/1/API_KEY_REMOVED_FOR_SECURITY/my-games");
urlRequest.method = URLRequestMethod.GET;
var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
urlLoader.addEventListener(Event.COMPLETE, completeHandler);
urlLoader.load(urlRequest);
stage.addChild(textField);
}
private function completeHandler(event:Event):void
{
textField.text = event.target.data;
}
}
ttTextField.as:
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormatAlign;
import flash.text.TextFormat;
public class ttTextField extends TextField
{
[Embed(source = "../files/Fixedsys500c.ttf", embedAsCFF = "false", fontName = "fixedSys")] private var fontClass:Class;
private var textFormat:TextFormat = new TextFormat("fixedSys", 24);
public function ttTextField(string:String, centered:Boolean)
{
if (centered)
{
textFormat.align = TextFormatAlign.CENTER;
}
else
{
textFormat.align = TextFormatAlign.LEFT;
}
string = string;
this.autoSize = TextFieldAutoSize.LEFT;
this.mouseEnabled = false;
this.embedFonts = true;
this.textColor = 0xFFFFFF;
this.defaultTextFormat = textFormat;
this.text = string;
}
}
ttTextField can display regular strings, so it doesn't seem like this class is the issue. I just included it for completion's sake.
I'm also building and running using Sublime Text 2. My sublime-build file is as follows:
{
"cmd": ["C:\\Users\\Dan\\Documents\\flex_sdk_4.6\\bin\\mxmlc.exe", "-output=C:\\Users\\Dan\\TTTT\\TTTT.swf", "-default-background-color=#00FF00", "-default-size=800,600", "C:\\Users\\Dan\\TTTT\\src\\Main.as", "-static-link-runtime-shared-libraries=true"],
"file_regex": "(.*)[(](\\d+)[)]:(?: col: (?:\\d+))? *Error: (.*)",
"selector": "source.actionscript",
"variants":
[
{
"name": "Run",
"shell": true,
"cmd": ["C:\\Users\\Dan\\TTTT\\TTTT.swf"]
}
]
}
I've tried running the program in both Flash Player and Google Chrome. Every time the output is just a blank screen.
How would I get my URLLoader to actually load the URL? I can provide more information if needed. Thank you for reading.
I think that simply your Main class should extend from MovieClip and not Sprite :
public class Main extends MovieClip
{
// ...
}
And to debug your project in the browser, you can use a flash player debug version which you can download from here.
Hope that can help.

AS3 URLLoader POST aborted in browser

I've re-built an SWF audio uploader, and it was working fine for a few days. However, I just did a run-through of all the things I've built onto this project, and I notice that the POST request to the server to save the audio file is being Aborted about 10 seconds into the request. At 20 seconds, everything stops because of a timeout limitation that is implemented. I don't know much about AS3 or how it makes requests, but I do know that the PHP handler file is solid because no changes were made to it from the point which it worked last. The permissions are 755 and ownerships are correct on both the SWF and the handler file. I can also re-submit the request via Firebug, and it works with no issue whatsoever.
I'm not sure where the problem is stemming from exactly; whether it be browser, server or code issue. I was reading about Aborted requests and I've ensured that there are no other active requests before trying to upload. I should also add that other POST/GET requests have no issues, it's just this one request from the SWF.
Again, Flash/ActionScript is not a strength of mine, so if there are ways to improve what I'm doing, or if anyone can tell me what I'm doing wrong, please tell me.
package{
import flash.display.Sprite;
import flash.media.Microphone;
import flash.system.Security;
import org.bytearray.micrecorder.*;
import org.bytearray.micrecorder.events.RecordingEvent;
import org.bytearray.micrecorder.encoder.WaveEncoder;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.events.ActivityEvent;
import fl.transitions.Tween;
import fl.transitions.easing.Strong;
import flash.net.FileReference;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.display.LoaderInfo;
import flash.external.ExternalInterface;
import flash.media.Sound;
import org.as3wavsound.WavSound;
import org.as3wavsound.WavSoundChannel;
import com.adobe.serialization.json.JSON;
import com.adobe.serialization.json.JSONDecoder;
public class Main extends Sprite{
private var mic:Microphone;
private var requestor:URLLoader;
private var waveEncoder:WaveEncoder = new WaveEncoder();
private var recorder:MicRecorder = new MicRecorder(waveEncoder);
private var recBar:RecBar = new RecBar();
private var maxTime:Number = 30;
private var tween:Tween;
private var fileReference:FileReference = new FileReference();
private var tts:WavSound;
public function Main():void{
trace('recoding');
recButton.visible = false;
activity.visible = false ;
godText.visible = false;
recBar.visible = false;
mic = Microphone.getMicrophone();
mic.setSilenceLevel(5);
mic.gain = 50;
mic.setLoopBack(false);
mic.setUseEchoSuppression(true);
Security.showSettings("2");
requestor = new URLLoader();
addListeners();
}
private function addListeners():void{
recorder.addEventListener(RecordingEvent.RECORDING, recording);
recorder.addEventListener(Event.COMPLETE, recordComplete);
activity.addEventListener(Event.ENTER_FRAME, updateMeter);
//accept call from javascript to start recording
ExternalInterface.addCallback("startRecording", startRecording);
ExternalInterface.addCallback("stopRecording", stopRecording);
ExternalInterface.addCallback("sendFileToServer", sendFileToServer);
}
//external java script function call to start record
public function startRecording(max_time):void{
maxTime = max_time;
if(mic != null){
recorder.record();
ExternalInterface.call("$.audioRec.callback_started_recording");
}else{
ExternalInterface.call("$.audioRec.callback_error_recording", 0);
}
}
//external javascript function to trigger stop recording
public function stopRecording():void{
recorder.stop();
mic.setLoopBack(false);
ExternalInterface.call("$.audioRec.callback_stopped_recording");
}
public function sendFileToServer():void{
finalize_recording();
}
public function stopPreview():void{
//no function is currently available;
}
private function updateMeter(e:Event):void{
ExternalInterface.call("$.audioRec.callback_activityLevel", mic.activityLevel);
}
private function recording(e:RecordingEvent):void{
var currentTime:int = Math.floor(e.time / 1000);
ExternalInterface.call("$.audioRec.callback_activityTime", String(currentTime));
if(currentTime == maxTime ){
stopRecording();
}
}
private function recordComplete(e:Event):void{
preview_recording();
}
private function preview_recording():void{
tts = new WavSound(recorder.output);
tts.play();
ExternalInterface.call("$.audioRec.callback_started_preview");
}
//function send data to server
private function finalize_recording():void{
var _var1:String= '';
var globalParam = LoaderInfo(this.root.loaderInfo).parameters;
for(var element:String in globalParam){
if(element == 'host'){
_var1 = globalParam[element];
}
}
ExternalInterface.call("$.audioRec.callback_finished_recording");
if(_var1 != ''){
ExternalInterface.call("$.audioRec.callback_started_sending");
var req:URLRequest = new URLRequest(_var1);
req.contentType = 'application/octet-stream';
req.method = URLRequestMethod.POST;
req.data = recorder.output;
requestor.addEventListener(Event.COMPLETE, requestCompleteHandler);
requestor.load(req);
}
}
private function requestCompleteHandler(event:Event){
ExternalInterface.call("$.audioRec.callback_finished_sending", requestor.data);
}
private function getFlashVars():Object{
return Object(LoaderInfo(this.loaderInfo).parameters);
}
}
}

"SecurityError: Error #2122" on loading content from a redirected image

This happens on many occasions when loading content from the web, but for us, it's most common loading images through Facebook's graph shortcut call.
Something as simple as:
package
{
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.LoaderContext;
public class RedirectTestFail extends Sprite
{
private const url:String = 'https://graph.facebook.com/4/picture';
private const context:LoaderContext = new LoaderContext(true);
public function RedirectTestFail()
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest(this.url), this.context);
}
protected function onComplete(event:Event):void
{
this.addChild((event.target as LoaderInfo).content);
}
}
}
Gives a dreaded "SecurityError: Error #2122" error.
Despite other answers suggesting something as simple as:
Security.loadPolicyFile("https://fbcdn-profile-a.akamaihd.net/crossdomain.xml");
This isn't clear, nor comprehensive enough. Facebook have different image servers, that I've which I've been caught with before. This could be deemed a Flash Player Bug, which I'd accept, but as a security concern, I can understand them not allowing a redirect by default, as in you should handle it yourself.
I now use below. You try to do your normal behaviour, but wrap it in a try/catch for SecurityError. If one is thrown, catch it, and if the domain of the loaderInfo is different to the domain you requested, you run a 'Security.allowDomain' and 'Security.loadPolicyFile' on it, and attempt to load it one more times. This works perfectly in practise, with only a tiny amount of overhead.
package
{
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.system.Security;
public class RedirectTest extends Sprite
{
private const url:String = 'https://graph.facebook.com/4/picture';
private const context:LoaderContext = new LoaderContext(true);
public function RedirectTest()
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest(this.url), this.context);
}
protected function onComplete(event:Event):void
{
try
{
this.addChild((event.target as LoaderInfo).content);
}
catch(error:SecurityError)
{
trace(error);
var loaderInfo:LoaderInfo = (event.target as LoaderInfo);
var loaderDomain:String = loaderInfo.loader.contentLoaderInfo.url;
if(-1 == this.url.indexOf(loaderDomain))
{
Security.loadPolicyFile(loaderDomain + 'crossdomain.xml');
if( 0 == loaderDomain.indexOf('https') )
{
Security.allowDomain(loaderDomain);
}
else
{
Security.allowInsecureDomain(loaderDomain)
}
loaderInfo.loader.load(new URLRequest(this.url), this.context);
return;
}
throw error;
}
}
}
}
if you no need to manipulate pixels with loaded image BitmapData object, then you can just remove context from the loader.load
but without context.checkPolicyFile = true you will cant add smoothing to image

Flash Builder 4.6 Mobile Flex AS3: How to communicate with embedded SWF

I have a Flash Barcode scanner (camera) and want to use it in a mobile project to scan QR-Codes. It would be nice that it is possible to re-use this SWF and embedded it into a mobile Flex application. The SWF is made in Flash CS5.
So far, embedding (and add it to the stage and showing it) is successful but how do i communicate with the SWF? For example calling a function of it or by using events.
Here is a code snippet:
[Embed(source="../cam/cam.swf")]
private var cam:Class;
....
....
public const EVT_SNAPSHOT : String = "onSnapShot";
public var camera : Object;
public function onInit(e:Event) : void
{
this.camera = new cam();
this.camera.addEventListener(Event.ADDED_TO_STAGE, this.cameraInit );
this.stage.addChild( this.camera as DisplayObject );
}
private function cameraInit(e:Event):void
{
trace( 'Added to stage' );
this.stage.addEventListener( EVT_SNAPSHOT, this.cameraDoScan ); // does not bind?
trace( this.camera.hasOwnProperty('getAppInfo') ); // shows 'false'
}
private function cameraDoScan(e:MouseEvent):void
{
trace('MouseClick!');
}
Does anyone know to communicate with this 'thing'?
The most functional way to use external swf module is to load it into current ApplicationDomain, so you will have access to all classes contained in this loaded swf:
package
{
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.utils.ByteArray;
import flash.utils.getDefinitionByName;
public class astest extends Sprite
{
[Embed(source="/../assets/art.swf", mimeType="application/octet-stream")]
private static const art:Class;
public function astest()
{
var artBytes:ByteArray = new art() as ByteArray;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onArtLoaded);
loader.loadBytes(artBytes, new LoaderContext(false, ApplicationDomain.currentDomain));
}
protected function onArtLoaded(e:Event):void
{
var domain:ApplicationDomain = ApplicationDomain.currentDomain;
if(domain.hasDefinition("welcome_view"))
{
var moduleClass:Class = domain.getDefinition("welcome_view") as Class;
var module:Object = new moduleClass();
//module.moduleFunction();
addChild(module as DisplayObject);
}else
{
trace("loaded swf hasn't class 'welcome_view'");
}
}
}
}

Action Script 3 URLstream only streaming at ~25Mbps in Windows but line speed (100Mbps) on other OSes

Any reason I'm only able to stream data under windows 7 and XP (FF, Chrome and IE) at about 25Mbps while in Linux and OS X Lion, I'm hitting line speed rates of 100Mbps ?
This is my URLStream code.
I'm certain it's not my connection or server side since I'm able to telnet to the same URL / Port and recieve the same data at line speed (100Mbps)
Am I missing something obvious here?
package {
import flash.display.Sprite;
import flash.errors.*;
import flash.events.*;
import flash.net.URLRequest;
import flash.net.URLStream;
import flash.net.URLRequestHeader;
import flash.net.NetStream;
import flash.media.*;
import flash.utils.*;
public class URLStreamExample extends Sprite {
private var stream:URLStream;
private var bytes:ByteArray;
public function URLStreamExample():void {
stream = new URLStream();
bytes = new ByteArray();
var stream:NetStreamInfo = stream.info;
if (stream) Alert.show( stream.dataBufferByteLength.toString() );
var header:URLRequestHeader = new URLRequestHeader("pragma", "no-cache");
var request:URLRequest = new URLRequest("http://xxxxxx.net:7890");
request.requestHeaders.push(header);
configureListeners(stream);
try {
stream.load(request);
} catch (error:Error) {
trace("Unable to load requested URL.");
}
}
private function configureListeners(dispatcher:EventDispatcher):void {
dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler,false,0,true);
stream.addEventListener(ProgressEvent.PROGRESS, progressHandler,false,0,true);
}
private function progressHandler(e:Event):void {
//trace("progressHandler: " + event);
e.target.readBytes(bytes, 0, e.target.bytesAvailable);
bytes.clear();
}
private function securityErrorHandler(event:SecurityErrorEvent):void {
trace("securityErrorHandler: " + event);
}
private function ioErrorHandler(event:IOErrorEvent):void {
trace("ioErrorHandler: " + event);
}
}
}