5

I am programming a website where you can post stuff. This works with the following jQuery ajax:

    $.ajax({
        type: 'POST',
        url: 'action/post.php',
        data: 'posttext='+posttext+'&imageurl='+imageurl,
        success: function(feedback){
            $('#feedback').val(feedback);
        }
    });

Now I wonder: anyone could write his own ajax to post something to the site and do this over and over again. How do I prevent this? I am sure I would need some kind of security check in post.php - I already heard about http referer, but that can be modified so it's not really trustworthy.

Also I would like to add a timer in post.php that makes sure that a post from the same ip address can only be posted once every x seconds, and resets the timer if the post is sent below x seconds (kind of like stack overflow does it with the comments).

Does anyone know how to secure the ajax and also how set the timer? Or any other ideas how to secure the posting mechanism?

Thank you!

Dennis

Dennis Hackethal
  • 13,662
  • 12
  • 66
  • 115
  • Only way is to create unique identifier of user, and check this on the server side. Rely on data in DB, as if your code in PHP is good, there will be no mess. Everything in ajax is just for UX – Marek Sebera May 08 '12 at 19:51
  • Could you elaborate further? UX? – Dennis Hackethal May 08 '12 at 19:52
  • Thanks. Still, I think a timer would be really helpful - how can I set one in php? – Dennis Hackethal May 08 '12 at 19:53
  • The solution to this question is quite complex. Do you plan on opening your API for users to embed with ajax? Or do you plan on having them call it from their server side? Do you want to limit it on the host sending it? or the users of your clients sites? – Daniel Moses May 08 '12 at 19:54
  • If you're not trusting the user (or their browser) to send non-tampered referrers, it seems silly to expect the ajax to remain non-tampered as well. Absolutely anything sent from or received by a browser can be tampered with. – Mr. Llama May 08 '12 at 19:54
  • There's not a lot you can do from the browser to prevent people from editing your code and bypassing stuff. Minimizing/obfuscating your code is the only obvious option. Security in these cases is mostly done on the server side. – elclanrs May 08 '12 at 19:54
  • You really shouldn't use a string for `data` but rather an object: `data: {posttext: posttext, imageurl: imageurl}` - otherwise *you* have to take care of properly escaping/encoding the string! – ThiefMaster May 08 '12 at 19:56
  • I escape it properly on the server side, no worries. I think I am on the safe side regarding injections, but still vulnerable to penetration tests. – Dennis Hackethal May 08 '12 at 19:57

2 Answers2

1

Your best approach would be to store your information in a database. You could have 4 fields in a table:

ipAddress, submitDate, postText, imageUrl

Upon submission, check if there's an entry in the DB for the current IP Address. If so, compare the entry's submit date with the current date and if it is over your threshold allow the submission. Otherwise, issue an error message and redirect the user back.

This still isn't foolproof however, since the IP Address can also be spoofed or the user can be hiding behind a proxy.

Jeff Lambert
  • 24,395
  • 4
  • 69
  • 96
  • 1
    Also note that if your users are all from a university dorm and NATed behind one IP address you would block two people from posting at the same time. – Daniel Moses May 08 '12 at 19:55
  • I could use a combination of the ip and the user agent - would that work? – Dennis Hackethal May 08 '12 at 19:56
  • @DMoses Good point. OP specifically stated IP Address, but to solve this you could either request an email address as well or do a full-fledged user login. Big hammers for big nails. – Jeff Lambert May 08 '12 at 19:56
  • I think I will take the database approach and save both ip and user agent in columns and then compare with each new request. Thanks for your help! – Dennis Hackethal May 08 '12 at 20:54
  • @Dennis adding the user agent may help, but they're still not unique. Depends a lot on who your target audience is. Best of luck! – Jeff Lambert May 08 '12 at 20:55
  • @watcher I don't have a specific target audience - anyone can post something. A combination of ip and user agent is not unique? What would be unique then? – Dennis Hackethal May 08 '12 at 20:56
  • @Dennis having a full fledged username/password is your best bet. There have been studies about fingerprinting browsers, but I personally wouldn't trust any of those methods yet. – Jeff Lambert May 08 '12 at 21:00
1

just store the IP and the request time in a log file. Then check the log file on each request for existance of that IP and compare the time stored.

Here's a simple script which only allows a request from the same IP after 10 seconds:

$waitSeconds = 10;
if (allowRequest($waitSeconds)) {
    // allowed
    echo "Welcome.";
} else {
    // not allowed
    echo "Please wait at least $waitSeconds after your last request.";
}
echo '<hr /><a href="#" onclick="location.reload(true);return false">try again</a>';

function getLastRequestTimeDiff($ip = null, $logFile = null)
{
    if ($ip === null) {
        // no specific ip provided, grab vom $_SERVER array
        $ip = $_SERVER["REMOTE_ADDR"];
    }
    if ($logFile === null) {
        // no specific log file taken
        $logFile = "./lookup.log";
    }
    if (!is_file($logFile)) {
        // touch
        file_put_contents($logFile, serialize(array()));
    }
    // read content
    $logContent = file_get_contents($logFile);
    // unserialize, check manual
    $lookup = unserialize($logContent);
    // default diff (f.e. for first request)
    $diff = 0;
    // current timestamp
    $now = time();
    if (array_key_exists($ip, $lookup)) {
        // we know the ip, retrieve the timestamp and calculate the diff
        $diff = $now - $lookup[$ip];
    }
    // set the new request time
    $lookup[$ip] = $now;
    // serialize the content
    $logContent = serialize($lookup);
    // and write it back to our log file
    file_put_contents($logFile, $logContent);
    // return diff (in seconds)
    return $diff;
}

// encapsulate our function in a more simple function (allow yes/no)
function allowRequest($allowed = 10, $ip = null, $logFile = null)
{
    $timeDiff = getLastRequestTimeDiff($ip, $logFile);
    return $timeDiff >= $allowed;
}
MonkeyMonkey
  • 826
  • 1
  • 6
  • 19