views:

38

answers:

3

Hi all! I am building a survey page where users have a limited time to answer all their questions. The time given is stored in the model as @test.time_allowed, which is an integer representing seconds.

I need to have a simple and non-user-tamperable way to get a timer to display on the view and execute a controller action when it winds down to 0. How can I accomplish this?

I'm a relative beginner so any specific answers would be really helpful. Thank you.

---UPDATE---

@Bryan:
I assume there is a tamper proof way if the timing is done server side? For example, there might be a javascript timer on the client side as you suggested, but upon submission can't the submission time be checked against the time of the window's initial load?

A: 

There's no 100% tamper proof way of implementing this since you would need to do this using JavaScript which can be turned off or manipulated by a sufficiently malicious user.

However if you aren't concerned about these issues you could simply set a timeout on the page to submit the form after the number of seconds have elapsed. To do this you would need something similar to the follow. Obviously timeInMilliseconds would need to be generated into the page from the template on the server side.

window.setTimeout(function() {
                     document.forms['survey_form'].submit();
                  },
                  timeInMilliseconds);
Bryan Kyle
While this suggestion addresses the UI on the client browser, it doesn't do any verification on the server. The server should never trust any data coming back from the client and should assume it may be forged.
fullware
+2  A: 

Since data coming back from the client can never be fully trusted, the server must somehow know what the timestamp of the originally generated form was. This could be done by saving variables in the session or database, but this is problematic. Instead, the server can place a timestamp in the form, either encrypted, or signed, to ensure the client has not altered it. The server can then reject the submission as necessary. On the client, separate logic can handle the UI portion, giving the user feedback on the time limit, but ultimately this only loosely coupled to the server handling.

Details:

The server should generate two form fields: one with the system timestamp time = Time.now.to_i to track when the form was generated, and another with a signature Digest::MD5.hexdigest(time.to_s.concat('Some-secret-string-1234')) (note using the same time value here for the timestamp form field and signature form field). This validates that the form is submitted with a server-generated timestamp that has not been altered by the client.

You might also send another form field with the time limit.

On the client, read the timestamp, use setTimeout and the time limit to generate a countdown or whatever you want to do on the front end.

When the form is submitted, authenticate the timestamp submitted with the form by regenerating the MD5 signature using the same method as before. Make sure it matches the signature submitted by the form. Then, add the timestamp to the timeout, and make sure it's later than the current server time. If so, you have a valid submission, within your time constraint.

You probably will need to give a little more leeway on the timeout at the server than on the client, maybe a few seconds, to account for network delays, otherwise the user might see one second remaining, click submit, and then by the time the server request is received, it will seem like the timer has expired.

Be sure to add require 'digest/md5' to get access to MD5.

Using MD5 signatures like this is a great way to verify that a client has not altered key parameters in a form in a stateless manner, whatever you would like them to be. A good addition to your bag of tricks.

Good luck!

fullware
Signatures can be done with protect_from_forgery method for free, no need to handle them by hand. Only server-side part of your solution will protect from sending invalid surveys. Signature does not stop malicious user from altering page contents.
samuil
protect_from_forgery loosely verifies that the form was in fact generated by the server. To ensure that a valid timestamp is retained, it either needs to be stored in the session (messy when there are multiple requests out potentially) or it needs to be encrypted/signed in the browser.
fullware
A: 

Create model for ongoing surveys, and add after_create filter that will set deadline to Time.now + survey_duration. Keep logic that will deny late sending of answers in model.

samuil