views:

110

answers:

5

I write a piece of software that runs inside banner ads which generates millions of session IDs every day. For a long time I've known that the random number generator in Flash is't random enough to generate sufficiently unique IDs, so I've employed a number of tricks to get even more random numbers. However, in ActionScript 2.0 it's not easy, and I'm seeing more and more collisions, so I wonder if there is something I've overlooked.

As far as I can tell the problem with Math.random() is that it's seeded by the system time, and when you have sufficient numbers of simultaneous attempts there are quite a few collisions.

In ActionScript 3.0 I use the value returned by System.totalMemory as a bit of extra randomness, but there's no equivalent in ActionScript 2.0. AS3 also has Font.enumerateFonts, and a few other things that are different from system to system.

What I need isn't something perfectly random, just something that is random enough to dilute the randomness I get from Math.random(). Think of it this way: there is a certain chance that two people will generate the same random number sequence using only Math.random(), but the chance of two people generating the same sequence and having, say, the exact same list of fonts is significantly lower.

I cannot rely on having sufficient script access to use ExternalInterface to get hold of things like the user agent, or the URL of the page. I don't need suggestions of how to do it in AS3, or any other system, or server side, only AS2 -- using only what's available in the standard APIs.

On the server side I also add the IP address to the session ID, but even that isn't enough (for example, many large companies use a single proxy server and that means that thousands of people all have the same IP -- and since they tend to look at the same sites, with the same ads, roughly at the same time, there are many session ID collisions). Doing more on the server side is, for various reasons, not practical. I can, for example, not generate random numbers on the server side and send them to the client, even if I very much would like to be able to solve it that way.

The best I've come up with so far is to use the list of microphones (Microphone.names), but I've also tried to make some fingerprinting using some of the properties in System.capabilities, I'm not sure how much randomness I can get out of that though so I'm not using that at the moment. I hope I've overlooked something.

I'm sorry to come off as arrogant in my comments to your answers, but please, if you don't know ActionScript 2.0, or know how a pseudo random number generator works, don't try to answer, it won't help and I will give downvotes. I really appreciate proper answers, though.

A: 

If I understand your question correctly, you don't want a random number but a unique id for each pc. Have you considered the MAC address of the network card ?

I don't know if you can get it in AS2, but that will give you a unique id for each machine.

Maybe? another idea would be

Math.random() * Math.random()

? Or

Math.random().toString() + (Math.random() * Math.random()).toString()

don't know, just an idea ...

Run CMD
-1 for not understanding random numbers. Multiplying random numbers is completely useless because of how they are generated. If two people seed their generators with the same number they will generate the same sequences, and that is what's happening here. That means that multiplying two numbers in the sequence will still mean that they end up with the same result. I would give you another -1 if I could for the MAC address suggestion, I mean, if it was that simple I wouldn't have asked the question, would I?
Theo
sorry for trying to help ... I didn't know you were so smart ...
Run CMD
besides, you can't get "Even more random" because your clock resolution is too low. It's like trying to display 2000 pixels on a 1000 pixel screen.
Run CMD
That is true, and that is precisely why your answer doesn't work. Getting more randomness from _another_ source that is not dependent on `Math.random()` or the system time (since that is the seed) would give me more total randomness though -- that would be like adding another screen so that the 2000 pixels fit 1:1.
Theo
:-) ... I'll keep it in the back of my head .. maybe something will pop up ...
Run CMD
Well, Math.random().ToString() + Math.random().toString() would work if you could seed it 2 times with a different value.Say the chance 2 users generate the same value on the first random is one on a million, then the chance 2 users generate 2 times the same random value would be a million times a million. I still think you don't need more randomness but more possibilities ...
Run CMD
yeah, but the random number generator is 1) not seedable in ActionScript 2.0, and 2) unless I have a separate source of randomness to seed it with it doesn't do much good, and if I have a separate source of randomness I could just as well use it directly
Theo
+2  A: 

I think you are over thinking it and trying to be too "tricky"!

What you are after is a GUID (globally unique identifier). Basically you need a longer number, and it would help if used letters as well.

There are already a few classes and scripts out there for generating a GUID in AS2, but Adi Reddy Mora's one from www.designscripting.com is really good.

P.S Lets do the math! Math.random spits out a float with an precision of 15 decimal places. That is a huge number. That works out to a one in one thousand billion chance of two people getting the same number (if it was a truly random number, but even half of that is a pretty slim chance). On the other hand, any of the other methods you mentioned would have considerably less variation. Such as basing it off the users font-list. What are the odds that two people have the same fonts install? I would say close to 1:20, because most people use windows and only have the default fonts installed!

TandemAdam
-1 for not understanding how Math.random() works. It doesn't matter if Math.random() has a huge result space because it's still seeded by something like the system time. That means that if two people get the same seed they will generate the same sequence. The randomness of Math.random() in this use case is *not* down to the number of decimals, but to the chance of two people using the same seed.
Theo
besides, your reasoning about the result space of `Math.random()` is flawed. You assume that just because there are 15 decimals in the number that means that it can return any number that can be represented using those decimals. It is very unlikely that the random number generator in Flash is that good.
Theo
finally, on the point of using the list of fonts: I'm not suggesting that it's a good source of randomness, but if you read my question properly I explain that the chance that two people get the same seed, _and_ have the same list of fonts is less than them just getting the same seed. The two sources are independent, and can be combined to create a (albeit just slightly) bigger space of random numbers.
Theo
+1  A: 

I think that chances of two people ending up with the same seed for random() sequence is negligable. Adobe doesn't disclose the method used, but if they use system clock (as you suggest), that should ensure that the collisions are truly rare (number of milliseconds since January 1, 1970 0:00:000 should be pretty damn random).

If you're getting collisions anyways, I would start looking at the rest of the algorhythm that crunches the number that random() spits out and look for randomness-reducing operations there. It doesn't help that random() returns 1 in 2 billion number if your algorhythm maps it to a 1 in 10 domain, for example. If you provide some more info about what your algorhythm is doing, maybe I could be more specific.

Also, number of fonts, microphones or things like that are surely a number of orders of magnitude less random than the value returned by random().

jpop
As I mentioned in the question there are many thousands of people looking at ads containing this code more or less at the same time. If the seed is the system time in milliseconds then the chances of _any_ two people getting the same seed is quite high. I also mentioned that it doesn't matter that the number of fonts is not as random as `Math.random()`, because the chance that two people will get the same random number _and_ have the same number of fonts is still less that them just getting the same random number.
Theo
Ok, think what you will, but I would still recommend re-examining the algorhythm that uses random() as input value and spits out a sessionID. That can have potentialy far greater effect on collisions than possible seed collisions (BTW, I bet Adobe uses milliseconds since bootup as random seed or some value like that, not absolute time value).
jpop
A: 

There are, as far as I can see three possibilities:

  1. Implement and use another pseudo random number generator algorithm so that the seed can be specified (but then the question is how to seed it properly, the system time probably isn't sufficient). There's one called Mersenne Twister that is supposed to be good.
  2. Use all possible properties like screen resolution, microphone names, etc., and generate a fingerprint by adding and multiplying their characters and character positions. This will, hopefully, yield a somewhat unique number, that in combination with Math.random() be sufficient.
  3. Exploit the timing properties of the client, for example, set a timeout for X milliseconds and measure the actual delay, which will be dependent on the circumstances. It might be enough if combined with Math.random(), or it could be used as a seed for a custom generator.
Theo
+1  A: 

It's been a while since I've last coded in AS 2.0, but following the System.totalMemory idea, a possible approach could be measuring performance and / or connection speed. This have some variation from system to system, so perhaps if you combine a few of these values you can randomize Math.random further.

I'm thinking along the lines of something like this:

var init_ms:Number = getTimer();
var dummy:LoadVars = new LoadVars();
dummy.onData = function(success:Boolean):Void {
trace(init_ms);
trace(getTimer());
var elapsed_ms:Number = getTimer() - init_ms;
trace("elapsed_mc: " + elapsed_ms);
}
dummy.load("http://www.example.com/dummy");

var counter:Number = 0;
this.onEnterFrame = function():Void {
counter++;
if(counter >= 20) {
    var elapsed_ms:Number = getTimer() - init_ms;
    trace("elapsed_mc: " + elapsed_ms);
    this.onEnterFrame = null;
}
};

I don't remember exactly how cross domain policies work in AS 2.0, but I think the code above will at least try to download the crossdomain.xml; Not sure if you'll be allowed to load external content from a banner, though.

The other one could also give you some randomness, since the time elapsed between frames is not all that precise and has some variation from machine to machine and even considering the same machine.

Hope this helps.

Juan Pablo Califano
I think both your ideas would work, although I can probably not use the first because in my case time is critical and I can't wait for the round trip to the server. The second one should work though, and I've already started experimenting with a variant which uses `setInterval` (I mentioned this solution in my own answer, see #2868103). The good news is that when the Flash runtime is starting up the first few frames are often very delayed, which means that timers and measuring the frame rate should yield at least some measure of randomness.
Theo
@Theo. Hadn't seen your own answer, but yes, my second idea is basically the same as your setInterval approach. Thinking about it, instead of just using the elapsed time, you could do something like: `(new Date().valueOf()) + elapsed_ms`. This will add another source of variation.
Juan Pablo Califano