Quick and easy flood protection? - php

I have a site where a user submits a message using AJAX to a file called like.php. In this file the users message is submitted to a database and it then sends a link back to the user. In my Javascript code I disabled the text box the user types into when they submit the AJAX request.
The only problem is, a malicious user can just constantly send POST requests to like.php and flood my database. So I would like to implement simple flood protection.
I don't really want the hassle of another database table logging users IPs and such... as if they are flooding my site there will be a lot of database read/writes slowing it down. I thought about using sessions, like have a session that contains a timestamp that gets checked every time they send data to like.php, and if the current time is before the timestamp let them add data to the database, otherwise send out an error and block them. If they are allowed to enter something into the database, update their session with a new timestamp.
What do you think? Would this be the best way to go about it or are there easier alternatives?
Thanks for any help. :)

Session is the easiest to do this, and has the least overhead as well. You can store two bits of data in the session, timestamp of last post, and the ip the post is comming from. Here is how you check legitimacy then:
session_start();
if(isset($_SESSION['ip']) && $_SESSION['last_post'] + MININTERVAL < time()) die('too early');
$_SESSION['last_post'] = time();
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
// store the message

Use a token. You generate the token and add it to the page originating the request. In like.php you verify that the request contains a valid token, which means it comes from your page instead of an external one POSTing directly.

You don't need to go through the whole record file. Instead:
<?php
define("FLOODPOOL", ".");
define("FLOODPOOL_LIMIT", 30);
define("FLOODPOOL_DURATION", 60 * 60 * 24);
define("FLOODPOOL_AUTOCLEAN", true);
// Record and check flood.
// Return true for hit.
function floodpool_check($id){
$fp = fopen(FLOODPOOL . DIRECTORY_SEPARATOR . 'fp_' . basename($id), 'a+');
fwrite($fp, pack('L', time()));
if(fseek($fp, -4 * FLOODPOOL_LIMIT, SEEK_END) === -1) {
return false;
}
$time = reset(unpack('L', fread($fp, 4)));
fclose($fp);
if(time() - $time < FLOODPOOL_DURATION) {
if(FLOODPOOL_AUTOCLEAN){
#floodpool_clean();
}
return true;
}
return false;
}
// Clean the pool.
function floodpool_clean(){
$handle = opendir(FLOODPOOL);
while(false!==($entry=readdir($handle))){
$filename = FLOODPOOL . DIRECTORY_SEPARATOR . $entry;
if(time() - filectime($filename) > FLOODPOOL_DURATION && substr($entry, 0, 3) === 'fp_'){
unlink($filename);
}
}
closedir($handle);
}
Usage example:
if(floodpool_check($_SERVER['REMOTE_ADDR'])){
header("HTTP/1.1 429 Too Many Requests");
exit("Hit some *");
}

Another way to do this is to write a hidden form input to the page (that calls like.php) using jQuery. A bot won't be using javascript so your hidden form field won't exist.
Check for the hidden field (assign it a value and a name) and if it exists, then hit the database with the request.
Another way; code a hidden element into the page (<input style='display:none;' name='nospam' value='' />). A bot will auto-fill every field in the form, so you just check if this field is populated - a user can't see it so you know it's a bot if you've got content there.
Set the style (display:none;) using jQuery tho... again, a bot won't see the jQuery, so it will think this is a legit form input.
You may want to specify a 'this page requires javascript to run' notice somewhere for the user. Some alternative suggestions. After all - you said 'simple' ;)

Well I made a script to handle it for core requests only (no session requests or other requests who aren't calling the core). If you have a look to google you'll find scripts/classes which will kill your server because of high loads every time. The fact, that many use SESSIONs and maybe ALSO SQL/Database will let you get a flooding protection as a server-killer. Also the fact that SESSIONs need a Cookie (or a GET SID) so you can manipulate SESSIONs easy to get a new SESSION ID.
My function is text-based and do a simple handling. The bad thing is that you maybe have to use a CronJob to delete ips from time to time. Comparing to other scripts its about 10* faster (and more save than sessions).
I don't know if its really useful at all. ;)
You maybe like to change the rpm value to less or/and also the 200 req. My setting is a ban for a bot doing interval requests in <=6 seconds.
<?php
function ht_request_limiter() {
if (!isset($_SERVER['REMOTE_ADDR'])) { return; } // Maybe its impossible, however we check it first
if (empty($_SERVER['REMOTE_ADDR'])) { return; } // Maybe its impossible, however we check it first
$path = '/your/path/ipsec/'; // I use a function to validate a path first and return if false...
$path = $path.$_SERVER['REMOTE_ADDR'].'.txt'; // Real file path (filename = <ip>.txt)
$now = time(); // Current timestamp
if (!file_exists($path)) { // If first request or new request after 1 hour / 24 hour ban, new file with <timestamp>|<counter>
if ($handle = fopen($path, 'w+')) {
if (fwrite($handle, $now.'|0')) { chmod($path, 0700); } // Chmod to prevent access via web
fclose($handle);
}
}
else if (($content = file_get_contents($path)) !== false) { // Load existing file
$content = explode('|',$content); // Create paraset [0] -> timestamp [1] -> counter
$diff = (int)$now-(int)$content[0]; // Time difference in seconds from first request to now
if ($content[1] == 'ban') { // If [1] = ban we check if it was less than 24 hours and die if so
if ($diff>86400) { unlink($path); } // 24 hours in seconds.. if more delete ip file
else {
header("HTTP/1.1 503 Service Unavailable");
exit("Your IP is banned for 24 hours, because of too many requests.");
}
}
else if ($diff>3600) { unlink($path); } // If first request was more than 1 hour, new ip file
else {
$current = ((int)$content[1])+1; // Counter + 1
if ($current>200) { // We check rpm (request per minute) after 200 request to get a good ~value
$rpm = ($current/($diff/60));
if ($rpm>10) { // If there was more than 10 rpm -> ban (if you have a request all 5 secs. you will be banned after ~17 minutes)
if ($handle = fopen($path, 'w+')) {
fwrite($handle, $content[0].'|ban');
fclose($handle);
// Maybe you like to log the ip once -> die after next request
}
return;
}
}
if ($handle = fopen($path, 'w+')) { // else write counter
fwrite($handle, $content[0].'|'.$current .'');
fclose($handle);
}
}
}
}
Edit: My way to test the request time was with microtime and simulate 10'000 users. I ask google and tested (as example) http://technitip.net/simple-php-flood-protection-class
So I don't know what should be simple there? You have about 3 SQL Requests at one time like:
$this -> user_in_db($ip))
$this->user_flooding($ip);
$this->remove_old_users();
It maybe supply more functions, but all legit users use servertime for nothing. ;)

If you want to stop flooding a search page you can try it like this way:
$flood_protection_interval = 2;
session_start();
if(
isset($_SESSION['ip']) &&
$_SESSION['counter'] > 10 &&
$_SESSION['last_post'] + $flood_protection_interval > time()
){
// $_SESSION['counter'] = 0; // Use this if you want to reset counter
die("<pre>\n\n\n\t<b>FLOOD PROTECTION</b>");
}
$_SESSION['counter']++;
$_SESSION['last_post'] = time();
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
So if your visitor search 10 times under e.g. 2 seconds he will be stopped!

I thought about using sessions, like
have a session that contains a
timestamp that gets checked every time
they send data to like.php
This won't stop bots as they can receive and send the same cookies that users do.
You should really have users logging into such a system. Seems to be worth protecting access. You could also consider limiting posts per minute per ip but multiple bots could still send many spam messages.
If you don't want to implement a login then many sites use captcha to try and cut down on such attempts.
http://www.phpcaptcha.org/

Related

Incrementing the value of the post by every repeated form submission

I have a form sender which is posting a value of 6 to another form receiver. What I'm trying to achieve is store the posted value from sender into a variable in the receiverthen increment the variable it every time the sender posts. Then print the updated variable
This is what I have tried to do
$val= $_POST['val'];
$limit = 6 + $val;
echo $limit;
Im getting the result as 12. But what I want is
After first post result = 12
After second post result = 18
On and on...
NB:$_POST['val'] = 6;
session_start();
$limit = 6;
if(!isset($_SESSION['lastLimit'])) {
$_SESSION['lastLimit'] = 0;
}
if(!empty($_POST)) {
$_SESSION['lastLimit'] = $_SESSION['lastLimit'] + $limit;
$postedValue = $_POST['val'] + $_SESSION['lastLimit'];
echo $postedValue;
}
Because the web is stateless i.e. scripts do not remember anything that happened the last time a page/form was executed the receiver script does not remember anything from the last time it was run.
But dont panic, there is a way. Its called a SESSION and you can store data in the session which will then be available the next time this user connects to your site. In PHP you use it like this. The session is linked to this specific connection to a specific user.
receiver.php
<?php
// must be run at top of script, before any output is sent to the new form
session_start();
// did the form get posted and is the variable present
// or replace POST with GET if you are using an anchor to run the script
if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['val']) {
if ( isset($_SESSION['limit'] ){
// increment the limit
$_SESSION['limit'] += (int)$_POST['val'];
} else {
// initialize the limit
$_SESSION['limit'] = (int)$_POST['val'];
}
echo 'Current value of limit is = ' $_SESSION['limit'];
} else {
// something is not right
// direct this user to some basic page like the homepage or a login
header('Location: index.php');
}
You need an intermediate layer to store the value.
Available options:
1) Global static value
2) session
3) file
4) database
I would recommend global value or session, as they data you want to store isn't that huge and would meet the requirements easily.
I would not write the syntax to store it in session as a number of people have already mentioned it. I just wanted to clarify the problem scenario and possible solutions.
You can store $limti into global varibale .
global $val;
$val += $_POST['val'];
$limit = 6 + $val;
echo $limit;

expiring php redirect on set date

I.m looking for a way in Php to have a Redirect expire on a said day.
The use for this is that I'm sharing file on a sever using the code below.
What I like is to have that redirect take them to a page that lets them know there time is up and they can see the Redirect anymore or tell I update the date of expire.
if (test){
---do something---
die("<script>location.href = 'http://file.here.com'</script>");
} else {
return false;
}
I have also try
function BeforeProcessList(&$conn, &$pageObject)
{
$target_date = new DateTime("05-01-2014");
$today = new DateTime();
if((int)$target_date->diff($today)->format('%r%a') >= 0)
{
header("Location: testview_list.php");
exit();
}
}
There are several scenarios for this case but i suggest to use session.
Use additional session to be taken once your privilege period is valid and make sure that you've already insert a date when a user get into that file along with value that indicate this user is allowed , for example,1 so you can change a value to ,for example,0 after specific period and that user will be denied automatically or redirected to another page
I got it to work.
<?php
// has this expired?
$expire_date = "2015-08-25"; // good until Aug 8/15
$now = date("Y-m-d");
if ($now>$expire_date) {
header("Location: http://yoursite.com/outoftime.html");
die();
}
// and now the unexpired part of the page ...
header("Location: http://yoursite.com/fileshare/");
die();
Thanks for the help guys

What is the best way to determine if my users can accept cookies/sessions

I need some help with some logic for my buying process on my website.
We have a 4 step buying process: results, customer details, payment details, order confirmation.
The results page simply outputs prices to the screen based on some query string parameters.
I then save lots of information to PHP Sessions variables for later use.
On the 2nd stage, the customer stage, I want to output some of these session variables to the screen which for the most part works.
In my code, one of the first things I do is check the existence of one of the session variables I set on the results page, just to check we are in business and the customers quote info is saving properly.
I have set up warning emails to myself to notify me when a user lands on either the customer or payment stage of the booking process but apparently the first session variable does not exist. I then display a friendly error message asking if they have enabled cookies in their browser.
We seem to be getting a lot of these warnings emails, alarmingly high. It doesn't feel like an accurate statistic of how many customers could arrive without cookies enabled.
The email alerts me of the current URL, the ref URL if there was one, the users IP address, and an output of all Session Vars they have saved (always none of course!)
I'm just stumped what to do next - are these really users or bots hitting the results page without cookies enabled which means they'll fail the test on the next page or could it be something else?
I have session_start() on the top of each of these buying pages so it's nothing like that.
Here's my customer page:
<?php
require_once "../includes/common.php";
$quoteShared = new quoteShared();
// Check if this is a direct page hit
if (requestSession("sessionnumber") == "") {
echo $quoteShared->directHit();
die;
common.php has session_start() at the top.
function requestSession($xParam) {
$value = "";
if (isset($_SESSION[$xParam]))
{
if ($_SESSION[$xParam] != "") {
$value = $_SESSION[$xParam];
}
}
return $value;
}
You can do it in javascript also, this way :
function cookiesAreEnabled()
{
var cookieEnabled = (navigator.cookieEnabled) ? 1 : 0;
if (typeof navigator.cookieEnabled == "undefined" && cookieEnabled == 0){
document.cookie="testcookie";
cookieEnabled = (document.cookie.indexOf("test­cookie") != -1) ? 1 : 0;
}
return cookieEnabled == 1;
}
BEST WAY PHP
<?php
session_start();
if(!isset($_GET['testing'])){
setcookie('cookietest', 'somevalue', time()+3600);
header("location: cookie.php?testing=1");
}else{
if(isset($_COOKIE['cookietest']) && $_COOKIE['cookietest'] == 'somevalue'){
echo 'cookie enabled';
}else{
echo 'cookie not enabled';
}
}

need to refresh page to remove sessions

In this code I'm trying to ban client if he/she/it doing to much(10) login request for 3 minutes. The problem is after 3 minutes user must refresh the page 2 times. I can see the reason why it's enter into if statement but I can't find the solution. I feel like I've overcoded.
if($this->sessions->get_data("wrong_login")>10){
if(!isset($_SESSION["ban_time"])){
$this->sessions->set_data("ban_time", time());
}else
{
if(time() - $this->sessions->get_data("ban_time") > 180){ // 180 seconds
$this->sessions->remove("ban_time");
$this->sessions->remove("wrong_login");
}
}
// The message if user still banned
die("Banned for 3 minutes!");
}
I hope I can tell the problem..
EDIT: This code is the inside of the construct of register controller.
Before your IF statement, add another if statement that checks for ban_time session if the time is up, then set the wrong_login session to 0 if it is.
if($this->sessions->get_data("ban_time") < time())
{
$this->sessions->remove("ban_time");
$this->sessions->set_data("wrong_login", 0);
}
remove your else statement there.
also forgot to mention! when you set the ban time, it should be time() + 180.
if(!isset($_SESSION["ban_time"])){
$this->sessions->set_data("ban_time", time()+180);
}
use header function.
e.g.
header("Location: /path/to/some/file.php?error=Banned for 3 minutes.");
Then on the file.php you can do this:
<?php
// Parse error
$error = isset($_GET['error']) ? $_GET['error'] : '';
// Display error (if any) and stop executing the rest of the code.
if (!empty($error)) {
exit($error);
}
?>
This will not work if you already started to output...

Timer for online examination system to stores the time for each individual section

I am developing an online examination for which i need to have a countdown timer/clock to be displayed during the entire duration of the test. Also while displaying the final result the time taken to solve each individual questions should also be displayed along with the total time taken for the test.
What are the possible best approaches to implement this functionality??
<?php
$countfile = "counter.txt";
// location of site statistics.
$statsfile = "stats.txt";
// checks whether the file exist, if not then server will create it.
if (file_exists($countfile)) {
// open the counter hit file.
$fp = fopen($countfile, "r");
// reads the counter hit file and gets the size of the file.
$output = fread($fp, filesize($countfile));
// close the counter hit file.
fclose($fp);
// get the integer value of the variable.
$count = intval($output);
}
// if file is doesn't exist, the server will create the counter hit file and gives a value of zero.
else {
$count = 0;
}
// showcount function starts here.
function ShowCount() {
// declares the global variables.
global $ShowCount, $countfile, $statsfile, $count;
// get the current month.
$month = date('m');
// get the current day.
$day = date('d');
// get the current year.
$year = date('Y');
// get the current hour.
$hour = date('G');
// get the current minute.
$minute = date('i');
// get the current second.
$second = date('s');
// this is the date used in the stats file
$date = "$month/$day/$year $hour:$minute:$second";
// this is the remote IP address of the user.
$remoteip = getenv("REMOTE_ADDR");
// some of the browser details of the user.
$otherinfo = getenv("HTTP_USER_AGENT");
// retrieve the last URL where the user visited before visiting the current file.
$ref = getenv("HTTP_REFERER");
// open the statistics file.
$fp = fopen($statsfile, "a");
// put the given data into the statistics file.
fputs($fp, "Remote Address: $remoteip | ");
fputs($fp, "Information: $otherinfo | ");
fputs($fp, "Date: $date | ");
fputs($fp, "Referer: $ref\n");
// close the statistics file.
fclose($fp);
// adds 1 count to the counter hit file.
$count++;
// open the counter hit file.
$fp = fopen($countfile, "w");
// write at the counter hit file.
// if the value is 34, it will be changed to 35.
fwrite($fp, $count);
// close the counter hit file.
fclose($fp);
// showcount variable is equal to count variable.
$ShowCount = $count;
// return the value of the count variable into showcount variable.
return $ShowCount;
}
// display the value in the counter hits file.
echo showcount(), " visits";
?>
I am using this script to track the visitor stats and time of visit...it is working fine.however I am not able to track the time of individual question in each category of the exam (each exam has multiple categories having multiple questions). Need help
Using a session, you'll need to track when the user's time expires for the section
<?php
// Upon starting the section
session_start();
$_SESSION['TIMER'] = time() + 600; // Give the user Ten minutes
?>
Dont do that on page reload though because they can simply refresh the page and reset the timer
On the page, use Javascript to display a clock:
<script type="text/javascript">
var TimeLimit = new Date('<?php echo date('r', $_SESSION['TIMER']) ?>');
</script>
You can then use the variable 'TimeLimit' to display a countdown
<script type="text/javascript">
function countdownto() {
var date = Math.round((TimeLimit-new Date())/1000);
var hours = Math.floor(date/3600);
date = date - (hours*3600);
var mins = Math.floor(date/60);
date = date - (mins*60);
var secs = date;
if (hours<10) hours = '0'+hours;
if (mins<10) mins = '0'+mins;
if (secs<10) secs = '0'+secs;
document.body.innerHTML = hours+':'+mins+':'+secs;
setTimeout("countdownto()",1000);
}
countdownto();
</script>
You can achieve this by doing
When student starts the exam, store the StartTime in some store/database.
To display the count down, you can use javascript on client side. Write a function which will take 2 times, first will be the server time and second will be the StartTime. Using these you can find out how much time the candidate is solving problem, and from this you can find out time remaining. Using setInterval of javascript you can show ticking watch.
For time taken to solve question, store the duration for which question is visible to the candidate.
Add the time taken for individual question, and show total time.
I guess this is the answer you were looking for.

Categories