5Mixer/mphx

flash security sandbox

Closed this issue ยท 7 comments

Hi all,

i had some problems with this f**** flash security sandbox when i have a distant neko server and distant flash client (embbed in a html page).

exemple:
serveur : myipdomain:8000
client : https://myipdomain/games

according to :

we must :
1- write (returns) on socket 847 a flash policy files who allow domain and port (master policy files)
2 - write (returns) on socket 8000 (the port use on socket.connect(ip,port) a flash policy files
3- write (returns) on custom sockets using Security.loadPolicyfile(xmlsocket://myipdomain:customport)

So i write a simple server to get the master policy file on port 843 like this :

package;

import sys.net.Host;
import sys.net.Socket;

/**
 * yansuc
 * @author 
 */
class Main 
{
    private static var m_socket : Socket;

    private static var m_host : String = "x.x.x.x"; //my domain
    private static var m_port : Int = 843;
    private static var m_domainAllowed : String = "*";
    private static var m_toPort : String = "*";

    static function main()
    {       
        m_socket = new Socket();

        try
        {
            m_socket.bind(new Host(m_host), m_port);
            m_socket.listen(10);
        } catch (e:Dynamic)
        {
            trace("PolicyFile Server start failed on : " + m_host + ":" + m_port + " because : " + e);
            return;
        }

        while (true)
        {
            trace("PolicyFile Server start on : " + m_host + ":" + m_port);
            var cnx : Socket =  m_socket.accept();
            if (cnx!=null)
            {
                trace("===\nClient (" + cnx.peer().host + ":" + cnx.peer().port + ")try to get a policy file xml");

                //sending by default by adobe on Flash.net.socket.connect();
                if (cnx.input.readString(22) == "<policy-file-request/>")
                {
                    var result = generateXmlPolicyFile().toString();

                    trace("xml file policy : " + result + "\n===+\n");

                    cnx.output.writeString(result);
                    cnx.output.flush();
                }
                cnx.close();
            }   
        }

        m_socket.close();
    }


    private static function generateXmlPolicyFile() : StringBuf
    {
        var str : StringBuf = new StringBuf();
        str.add("<?xml version=\"1.0\"?>");
        str.add("<!DOCTYPE cross-domain-policy SYSTEM \"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\">");
        str.add("<!-- Policy file for xmlsocket://"+m_domainAllowed+" -->");
        str.add("<cross-domain-policy> " );
        str.add("<site-control permitted-cross-domain-policies=\"master-only\" />");
        str.add("\t<allow-access-from domain=\""+m_domainAllowed+"\" to-ports=\"" + m_toPort + "\" />" );
        str.add("</cross-domain-policy>");
        str.addChar(0);
        return str;
    }
}

this return on the socket a policy files like this :

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<!-- Policy file for xmlsocket://x.x.x.x -->
<cross-domain-policy>
    <site-control permitted-cross-domain-policies="master-only" />
    <allow-access-from domain="*" to-ports="*" />
</cross-domain-policy>

with the site-control permitted-cross-domain-policies set to "master-only", the flash.net.socket.connect(ip,port) must normaly don't check the step 2 and 3 i wrote before.
But not working for me., the client try to check the step 2 (policies on port 8000 for exemple) and send a security sandbox error...

So, i temporary fixed it with an ugly trick on mphx.tcp.Connection on recieve function.

    public function recieve(line:String){
        trace(line);
        //Transfer the Input data to a string

        if (line == "<policy-file-request/>")
        {
            trace("receive policyfile request");
            cnx.socket.output.writeString(generateXmlPolicyFile().toString());
            cnx.socket.output.flush();
            return; 
        }

        //Then convert the string to a Dynamic object.
        var msg = serializer.deserialize(line);
        trace(msg);
        //The message will have a propety of T
        //This is the event name/type. It is t to reduce wasted banwidth.
        //call an event called 't' with the msg data.
        events.callEvent(msg.t,msg.data,this);
    }

with this helper function :

private  function generateXmlPolicyFile() : StringBuf
    {
        var str : StringBuf = new StringBuf();
        str.add("<?xml version=\"1.0\"?>");
        str.add("<!DOCTYPE cross-domain-policy SYSTEM \"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\">");
        str.add("<!-- Policy file for xmlsocket://"+"*"+" -->");
        str.add("<cross-domain-policy> " );
        str.add("<site-control permitted-cross-domain-policies=\"master-only\" />");
        str.add("\t<allow-access-from domain=\""+"*"+"\" to-ports=\"" + "8000" + "\" />" );
        str.add("</cross-domain-policy>");
        str.addChar(0);
        return str;
    }

result :

  • 2 server running (maybe we can improve this by thread...)
  • an ugly fix on mphx lib
  • one day spend to try to fix this...

My questions :

  • Why the metadata "master-only" doesn't abort the step 2 and 3 ?
  • Can we improve this ? (by creating a "FlashConnection" extending "Iconnection" by exemple ?)

thx for reading this :)
Sorry for my bad english.

hi !
I have made some "class patch" out of the mphx library (i don't want to modify the library localy) :

  • Add a FlashConnection implements IConnection to manage the message send by adobe to get policy files ("")
  • Add a "PolicyFilesServer" to manage the automatic connection from a flash.net.socket on a 843 port and send back the master policy files
  • Add a "PolicyFilesProvider.hx" to generate the xml policy files response
  • add a "patchedServer" who extends mphx.server.Server and override the update function (to use flashConnection instead mphx.tcp.connection) and override the start function to call the update of the PolicyFileServer

Not ideal, but works :)

A package with the 4 files.
mphxpatch.zip

note : PolicyFiles server actually use Monitoring.L, it's personnal class for Log. you can replace all "L.error" or "L.verbose" by a simple "trace"

Nicely done! ๐Ÿ‘ ๐Ÿ‘
Obviosly alot of this stuff feels very hacky like, although alot of the codebase feels like that. I think after this, I should focus on cleaning it up. Anyway, I've been trying to put the files into mphx, having a few issues but I'll persist. Not avaliable next few days, but after that work will resume. Thankyou so much for your help.

Yes. i spend 6 hours to "fix" this problem. So there are many hack... sorry ^^

hi there !

i have a random problem about the security sandbox (again)
All seems to work fine except sometimes when the distant client send the policy files request on port 843 on flash.net.Socket.connect(). (the request is auto-sended on connect function).

the server who listen on port 843 accept correctly the socket, but with no data and socket.read() throw a haxe.io.Error.Blocked error. So the policy files is not send back and the connexion is not allowed.

Datas on this socket must be "" + a null byte at the end. BUT, this data is sometime write on the socket, 10-15ms later by the client .

So i put a cnx.waitForRead() on server side. It's slow down the connection (and the server) but it's more safe to manage the flash security and to resolve this problem :

PolicyFilesServer.hx :

` public function update() : Void
{

    var cnx : Socket = null;

    try
    {
        cnx = m_socket.accept();
    }catch (e : Dynamic)
    {
        cnx = null ;
    }

    if (cnx!=null)
    {

        trace("===Client (" + cnx.peer().host + ":" + cnx.peer().port + ") try to get a policy file xml");

        try
        {
            cnx.waitForRead();
            //sending by default by adobe on Flash.net.socket.connect();
            var test = cnx.input.readString(22);
            trace("test = " + test);
            if (test == "<policy-file-request/>")
            {
                var result = PolicyFilesProvider.generateXmlPolicyFile(m_domainAllowed, m_toPort, true, GameServer.global.extraAllowedHost).toString();
                trace("PolicyFileServer : xml sended : \n" + result);
                trace("===");
                cnx.output.writeString(result);
                cnx.output.flush();
            }
        }
        catch (e : Dynamic)
        {
            if (e == Error.Blocked)
                trace("[" + cnx.peer().host + ":" + cnx.peer().port + "] : BLOCKED because of no data on socket");
            else
                trace("[" + cnx.peer().host + ":" + cnx.peer().port + "] : ERROR : " + e);
        }

        cnx.close();
    }   
}`

all fixed here : #24
(need some test but i'm confident. If the pull-request is accepted, i will make more test on flash (with distant server/client) on a big project to confirm it )

can we close this issue ?

To be honest, I don't know, I really am doing very little with flash. If it works fine for you though, go ahead and close it. Clean up is an ongoing thing, and it seems that clean up is the main thing that this change needs. Thanks for your on going work with this, it really helps to have another set of hands when I don't have time. Closing it up! ๐Ÿ˜€