Silverlight and socket support - what were they thinking?
With beta 2 release of Silverlight 2 (SL2-B2), Microsoft decided to change the socket implementation to require a socket policy server, which basically is a TCP server responding with an XML document if you send it a certain string.
Prior to SL2-B2, the only restriction for socket connections was that the connection had to be done against the same hostname/IP-address that served the Silverlight application, and that the TCP port needed to be in the range 4502-4534.
Release candidate 0 (SL2-RC0) and the final “Release-To-Web” release (SL2-RTW) didn’t introduce any new changes.
So, the restrictions are now:
- Connections can only be made to TCP ports in the range 4502-4534
- A socket policy server must be running on port 943 on the same server
- The policy returned by the policy server must match with the application performing the request
The arguments for introducing the policy server seems to be:
- Added security
- A server hosting a socket server is not likely to also run a web server
Some related links:
- http://timheuer.com/blog/archive/2008/06/06/silverlight-sockets-requires-policy-server-beta-2.aspx
- http://silverlight.net/blogs/msnow/archive/2008/06/26/full-implementation-of-a-silverlight-policy-server.aspx
Added security?
The first point is of course a partially valid one. If you have a socket service, you might want to limit who has access to the service in order to prevent unauthorized use of it, or prevent so-called Cross-Site Request Forgery (XSRF) attacks.
Basically this is what’s going on:
- The user goes to http://server1.example.org/foo.html and is served a HTML page with an embedded Silverlight application
- The Silverlight app tries to do a connection against server2.example.org:4502
- Silverlight intercepts the request and does a connection against server2.silverlight.example.org:943 and requests the policy XML
- Silverlight checks if the policy file accepts connections to server2.example.org:4502 from server1.example.org/foo.html or server1.example.org or *.example.org or some kind of match
- The socket request is then either allowed or disallowed, based on the outcome of if a policy server answered, and gave an XML allowing the request
This also is more or less the same process with web service calls or web requests (http/https) from within a Silverlight application, with the change being that the access policy file is then read from http://<same-server-and-port-as-the-app-was-served-from>/ClientAccessPolicy.xml (or the Flash crossdomain.xml file).
However, the limitation of what services Silverlight applications are able to call is implemented in the Silverlight plugin itself! If you make an external application (i.e. not in Silverlight) in <your favorite programming language> that does socket, http/https or web service calls against these services, these are of course not checked against the access policy files first.
In other words: It leaves these services wide open to any other non-restricted applications doing calls against them. This only protects against XSRF attacks, and nothing else.
For the services to be really secure and limit requests, these access checks need to be implemented in the socket services or in the web services etc themselves. You simply cannot rely on that some policy file has been checked in advance. Of course, in a socket server you can’t figure out which web page the application originated from* - only what remote IP and port the caller has - so the policy files do still make a little bit of sense there. (* You could maybe record the IP address in an ASPX page serving the Silverlight app and do something smart there and check this again in the socket)
“A server hosting a socket server is not likely to also run a web server”
Now, I don’t know where this idea came from, or what the reasoning behind it was. My reactions are:
- Why not also do a request against and HTTP service on the same server, in case there is a web server running there already?
- Why not allow the policy server port to be configurable? If someone is already running a socket server there in the 4502-4534 range, surely they will be able to set up a service running on port 943 also. (Yes, some OSes limit listening sockets below port 1024 or 4096 to the root/administrator user, but how likely is that?). Also, it’s not like this will be directly insecure either, as the requesting client can’t affect which socket servers are running on the remote server anyhow, AND the policy file needs to be checked. I guess one argument could be that this allows for an administrator to manage what goes on, instead of permitting the individual socket server developers to do that.
- The policy file is a standard XML file. It is served by a client connecting to a service, issuing a request, and getting the file back. Does this sound vaguely familiar with another, very common protocol? Why insist on creating a new, custom protocol for this, instead of just basing this on HTTP? (see #1)
(Regarding #1, this makes me think of another weird decision: leaving out double-click mouse events or mouse-wheel support just because some interfaces might not have anything equivalent. It’s like limiting the screen resolution to 640×480 just in case some users don’t have a monitor capable of a higher resolution, or leaving out mouse support all-together in case some devices might only have keyboard, and no joystick/mouse/touch screen etc. I don’t get it — it reduces the user experience at the cost of a hypothetical uncommon scenario)
Firewalls
Most non-standard ports will normally be blocked by most “secure” firewall settings, and if you’re behind a corporate firewall, chances are you’ll experience even more restrictive policies with only a very few ports being open, e.g. port 80 and 443 for http/https.
Adding both a limit of a policy server running on port 943, AND the request being limited to ports 4502-4534 will make this virtually impossible to get to work in most corporate settings, and also other restrictive firewall setups.
It can be argued in reverse also; enforcing this range makes it easier to standardize the port range for access lists. Yes, but; from my experience with firewall administrators and IT departments, they are pretty sensitive about what they will put into their rules. They will most likely only open up the range to a select destination IP or range, and then they could just as well have done that for another port range. Opening up 4502-4534 to all target destinations is not likely to happen.
The common work-around for this problem, is for people to just set up e.g. a non-HTTP service on a the common HTTP port, or something similar. As long as the firewall isn’t inspecting the traffic and doing protocol analysis, this usually works fine. If you have packet inspection, you’ll probably be screwed anyhow.
Secondly, it limits calls against standard services, such as chat servers, web servers, ftp servers, etc since these do not typically run in the 4502-4534 range, but usually have their own defined ports. If you want to be able to connect to such services, you now need to either reconfigure them to use this port range, or write a proxy server that proxies/tunnels connections and traffic to the correct ports.
My conclusion
My conclusion to this, is that Microsoft weighed the security aspects of this to be much more imporant that offering developers flexibility and possibilities, a choice I can clearly understand and respect. However, in that case I think they really outdid themselves in adding obstacles.
Or; they just didn’t carefully think this through before rushing out SL2-RTW.. (because let’s admit it; one RC, ~2 weeks, hardly any changes?)
What I feel would have sufficed:
- Access policy file must be present either on either a provided port (default 943 if they insist), or on http/https on the same server.
- Use something equivalent to HTTP instead of that custom protocol. It could perfectly well be VERY limited (”GET /ClientAccessPolicy.xml\n\n” + dummy headers + XML response).
- No port range restriction, other than what is enforced through the access policy file. (Or at least offer the possibility of requesting “elevated privileges” from the user in form of a dialog or something for using non-SL-range ports, e.g. like Java does).