Friday, November 14, 2008

Time Logging & Cookie Data Encryption

One of the projects I'm working on is to log how long a visitor is on our website viewing a video and use that to enable a service. Yeah... Who would of thought?

Anyway this post is both about the time logging approach utilized as well as cookie encryption. Let me give you a little background before we dive in.

The Conditions.
The goal is a little tricky because of the set up. I'm writing it in Classic ASP, (not exactly my favorite language). Also normally one would think of storing time logging data on a SQL server, but I didn't have access to one. Another tricky part about logging the time a visitor is viewing the video is that the video being viewed can be upwards of an hour.

The Challenge.
Using session to track the time data isn't a full solution here since the session will timeout unless I increase that, (which right now isn't an option). If I am to use sessions as a way to keep track of the time spent on the site, I need to continually refresh/access the page to update time logging data as well as to keep the session alive.

The Chosen Approach.
The idea was making an AJAX call asynchronously "in the background" every 10 mins while the user is watching the video. This would keep the session alive, (which I'm finding out it does not for some reason), and would allow me to track with a 10 min accuracy if the user was on our video page or not, (hopefully watching the video). The advantage here is if that if the user moves away from the page, the updates stop effectively defining when the user began and finished viewing the video.

What Actually Happened...
As much as I hate to say it, I have to admit- this system will at least partly depend on JavaScript and I can't think of any other practical way to avoid this. I find comfort in the fact that we can thank the major adoption of .NET-driven applications for JavaScript becoming a necessity such that a developer can count on a fairly large user base supporting it.

Anyway, not having access to a database, I chose to store time logging information on a cookie. I decided I would store the session ID and the time when the request was made. Of course, this could lead to easy tampering and so I chose to encrypt it.

The encrypting algorithm I used was RC4. I found it easy to implement, thanks to After reading up on it a little I also felt it was rather secure- provided a strong key is used- especially for this purpose.

Little did I know the headaches I would run into. I would encrypt the data and store it in the cookie- but when I read it back, some of the data would be missing. It was very bizarre. I was trying all these ways to make sure I was reading/writing the cookie data correctly.

Before long I realized that it probably had to do with the way cookies are stored and just exactly what is and is not legal character-wise. I tried URL-encoding them. This bloated the data and I still had data missing.

I threw the whole URL-encoding idea out the window (for the moment) and tried filtering for illegal characters myself. I did some research, but all I could find was that <>:= were the illegal characters and nothing else. So I wrote some filters for that. Still missing data. Tried URL encoding as well as the filtering. Still missing data.

Finally I decided I would just write a function to go through each character in a string of encrypted data, and find any characters outside of a safe US-ASCII code range and escape them through a custom method.

As I was finishing up I ran into the Escape() and Unescape() methods in Classic ASP- so I gave them a shot before even trying my custom functions. They worked wonderfully. On the other hand, my idea of grouping time data by session ID and then using that to calculate time spent on each session to figure out a total of time spent on the site didn't work out so well, so I am choosing another method. I might write on that later.

Lessons Learned:
When writing binary[-like] data to cookies, stick to a known safe subset of US-ASCII characters. I still don't know exactly which character it was that caused the problem, but I'm thinking maybe some odd escape character or something along those lines.

At the time of this post I'm still in the process of writing the application, so I'm open to any ideas or suggestions on how anyone else would do it.


No comments: