I am building my first website. It is an Online Real Estate Agency. Users can create themselves a profile and then insert an ad and upload pictures.
I was told that I should detect multiple logging attempts to protect against Brute Force attacks. Well, with the following code I detect the IP's :
if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{ $ip=$_SERVER['HTTP_X_FORWARDED_FOR'];} else
{ $ip=$_SERVER['REMOTE_ADDR'];}
The system counts missed logging attempts within a certain delay and holds a ban list in a DB.
It works great ... at least when a I test it myself !
Then as I was told 'Beware of piracy through false IP's ', I get the impression my protection system mentionned above is made uneffective.
There are :
1) sofwares available to pirats that encompass a Proxy which can hide their real IP
2) proxies on the web that can also hide real IP's.
What 's the difference between 1) and 2) ?
I would like to know how proxies can be used and what they are able to do in term of illicit practices
Can sombody change at will it's Ip ?
Can somebody in China or in Russia 'simulate' a Western Europe or US ip ?
Can I do more than what I've done to detect any suspicious activity ?
Thanks a lot.
Anyone can change ip, proxy, vpn....
I use this function to detect REAL IP address if it's valid:
function getrealip() {
if (getenv('HTTP_CLIENT_IP') && long2ip(ip2long(getenv('HTTP_CLIENT_IP')))==getenv('HTTP_CLIENT_IP') && validip(getenv('HTTP_CLIENT_IP')))
return getenv('HTTP_CLIENT_IP');
if (getenv('HTTP_X_FORWARDED_FOR') && long2ip(ip2long(getenv('HTTP_X_FORWARDED_FOR')))==getenv('HTTP_X_FORWARDED_FOR') && validip(getenv('HTTP_X_FORWARDED_FOR')))
return getenv('HTTP_X_FORWARDED_FOR');
if (getenv('HTTP_X_FORWARDED') && long2ip(ip2long(getenv('HTTP_X_FORWARDED')))==getenv('HTTP_X_FORWARDED') && validip(getenv('HTTP_X_FORWARDED')))
return getenv('HTTP_X_FORWARDED');
if (getenv('HTTP_FORWARDED_FOR') && long2ip(ip2long(getenv('HTTP_FORWARDED_FOR')))==getenv('HTTP_FORWARDED_FOR') && validip(getenv('HTTP_FORWARDED_FOR')))
return getenv('HTTP_FORWARDED_FOR');
if (getenv('HTTP_FORWARDED') && long2ip(ip2long(getenv('HTTP_FORWARDED')))==getenv('HTTP_FORWARDED') && validip(getenv('HTTP_FORWARDED')))
return getenv('HTTP_FORWARDED');
$ip = htmlspecialchars($_SERVER['REMOTE_ADDR']);
/* Added support for IPv6 connections. otherwise ip returns null */
if (strpos($ip, '::') === 0) {
$ip = substr($ip, strrpos($ip, ':')+1);
}
return long2ip(ip2long($ip));
}
More info for X-Forwarded
Proxy is a server that can mask your ip. It will send your request as if it was its and then send you back response that got.
Can sombody change at will it's Ip ?
No, they can't just change their ip to whatever they like to. But they can mask it.
Can somebody in China or in Russia 'simulate' a Western Europe or US ip ?
Yes
Can I do more than what I've done to detect any suspicious activity ?
If you detect that some user name is logging in with wrong password too many times using brute force techniques, you could slow down him by using sleep function. This technique you wouldn't cut off users that are using the proxy without bad intends and you will slow the brute force hacking.
if($wrongAttempts > 5) sleep(3000);
if($password == $_GET[pass])
{
// ...
}
You could also start including captcha images to raise security or block the account for some time.
As Dagon says, IP address is a pretty weak way of identifying users, and hackers will almost certainly not use their own IP address, but rather a stolen machine, or a botnet; on the other hand, many corporate users may appear to all come from the same IP address, and you could easily end up blocking every user from that building/company if someone forgets their password.
The first defense against a brute force attack is to have a strong password policy; commonly, this is assumed to be at least 7 characters, with at least one number and one punctuation mark. This often annoys users, and makes them hate your site.
The next defense - if you think you're really at risk - is CAPTCHA; this makes users hate you even more.
The bottom line is: if you are building your first website, I'd look at an off-the-shelf framework, rather than inventing everything yourself. Consider PEAR:auth.
Related
My every coding & algorithm set up are ok, but required your advice, that if I'm going into the direction or not. I have two concerns where advice/review is required.
Concern I: Algorithm
In the login page, we don't use google recaptcha. But we have following logic to control flood & block ip:
For each non user failed login attempt, we store his IP on mysql "flood" table with IP & time.
This way we store & check maximum 20 attempts in last 2 hours. If it occurs, then we delete all flood records related to that IP & add that IP into "block_ip" mysql table.
For each of our PHP pages(30+) we call following IP block function to redirected blocked IP visitors into the "block.php" page.
function block_ip(){
$mysqli_2 = new mysqli(HOST, USER, PASSWORD, DATABASE);
if ($mysqli_2->connect_error) {
header("Location: ../error.php?err=Unable to connect to MySQL");
exit();
}
$stmt_block_ip= $mysqli_2->prepare("SELECT b_ip FROM block_ip");
$stmt_block_ip->execute();
$stmt_block_ip->store_result();
$stmt_block_ip->bind_result($block_iip);
while( $stmt_block_ip->fetch()){
if(strpos($_SERVER['REMOTE_ADDR'],$block_iip) === 0)
{
header("Location: ../block.php");
exit();
}
}
$stmt_block_ip->close();
}
Next admin plan is, if the total number of block IP goes > 50, then clear the table by copying all blocked IP & asking the server service provider to add it into the .htaccess to redirect them.
Is this idea is good? or should I keep it on blocked IP table, but
that might slow the page loading, isn't it? as for a long list of blocked
IP's? Any better idea?
Concern II: Algorithm
On forgot-username.php & forgot-password.php page we have google recaptcha. So we didn't add any flood control algo here, simply for non-user attempted we just redirect them to register page.
For the security concern is this approach is okay?
My 1st concern got solved as it seems with the WillParky Suggestion: the code below IP block checking should be done faster. I guess I don't need to use .htaccess & transfer blocked IP here and there.
function block_ip(){
$viewer_ip = $_SERVER['REMOTE_ADDR'];
$mysqli_2 = new mysqli(HOST, USER, PASSWORD, DATABASE);
if ($mysqli_2->connect_error) {
header("Location: ../error.php?err=Unable to connect to MySQL");
exit();
}
$stmt_block_ip= $mysqli_2->prepare("SELECT b_ip FROM block_ip WHERE b_ip =
?");
$stmt_block_ip->bind_param('s',$viewer_ip);
$stmt_block_ip->execute();
$stmt_block_ip->store_result();
if($stmt_block_ip->num_rows == 1){
{
header("Location: ../block.php");
exit();
}
}
$stmt_block_ip->close();
}
I've created an application using PHP and I'm going to sell it to my local market. I will personally be going to their locations to install/configure Apache & MySQL as well as installing my own code.
I would like a security system so that if anyone attempts to copy my code to an unauthorized machine, it won't run.
I know no one can prevent reverse engineering an application. even .exe (binary) files are cracked and with PHP (source code) anyone can do.
In my country those reverse engineers are really hard to find, so I would like to propose minimal security options like:
1) Create class (say, Navigation) which identifies system information like CPU ID, Computer name or any combination of hardware ID to make a UNIQUE_ID and matches with my given UNIQUE_ID (to the individual to whom I sold the application). If it's valid, it returns the navigation menu. Otherwise it will simply destroy the database and halt the execution by throwing an exception, maybe like:
class Navigation {
public function d() {
return current system UNIQUE_ID;
}
public function get() {
$a = file_get_contents('hash');
$c = $this->d();
if (crypt($c) != $a) {
//destory database
throw new Exception('');
} else {
return "<ul><li><a>home</a></li></ul>"; //navigation menu
}
}
}
2) Then during the installation process I'll change system UNIQUE_ID in "hash" file, create an object, and save it into a file (nav.obj):
(install.php)
<?php
$a=new Navigation;
$out=serialize($a);
file_put_contents('nav.obj', $out);
3) in header.php (which gets included in every file):
<?php
$menu=file_get_contents('nav.obj');
$menu=unserialize($a);
echo $menu->get();
?>
I know this method isn't full proof, but I'm pretty sure that around 60% of PHP developers won't be able to crack it!
Now I only need to get current system UNIQUE_ID.
I have created this function to get an unique ID based on hardware (Hard disk UUID). It is possible to use different resources like machine names, domains or even hard disk size to get a better approach depending on your needs.
function UniqueMachineID($salt = "") {
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$temp = sys_get_temp_dir().DIRECTORY_SEPARATOR."diskpartscript.txt";
if(!file_exists($temp) && !is_file($temp)) file_put_contents($temp, "select disk 0\ndetail disk");
$output = shell_exec("diskpart /s ".$temp);
$lines = explode("\n",$output);
$result = array_filter($lines,function($line) {
return stripos($line,"ID:")!==false;
});
if(count($result)>0) {
$result = array_shift(array_values($result));
$result = explode(":",$result);
$result = trim(end($result));
} else $result = $output;
} else {
$result = shell_exec("blkid -o value -s UUID");
if(stripos($result,"blkid")!==false) {
$result = $_SERVER['HTTP_HOST'];
}
}
return md5($salt.md5($result));
}
echo UniqueMachineID();
As per http://man7.org/linux/man-pages/man5/machine-id.5.html
$machineId = trim(shell_exec('cat /etc/machine-id 2>/dev/null'));
EDIT for Tito:
[ekerner#**** ~]$ ls -l /etc/machine-id
-r--r--r--. 1 root root 33 Jul 8 2016 /etc/machine-id
EDIT 2 for Tito: Some things to consider and scenarios:
Is the user allowed to get a new machine? Id guess yes.
Or run on multiple devices?
Sounds like the machine could be irrelevant in your case?
If its user only (no machine restrictions) then Id go for a licencing service (relies on network).
There are many services for this:
Google Play (for Android apps) is a good example: https://developer.android.com/google/play/licensing/index.html
MS and Apple have similar services.
However just search the web for the term "Software Licensing Service" or "Cloud Based Software Licensing Service".
If its user + single device, then youll need to pass up the device id to whatever service you use or make, then allow the machine id to be updated, but not allow revert to previous machine id (would mean multiple devices).
However said services will give you the client code which should take care of that if its a requirement.
Two scenarios from experience:
1: User on any device: we simply made an API in the cloud (in a website) and a login screen in the app, when the user logged in it authenticated via the API and kept a token, and whenever the device was connected to the net the app would query the API and update the login and/or token.
You could alternatively have the login screen in the purchase (like maybe they already logged into a site to purchase), generate a key and pack it with or bind it into the app.
2: User plus machine:
Same thing except when the API is queried the machine id is passed up. The machine ID can change as many times as the user updates their device, but we kept a record of machine ids and made to ban rule on: if we saw an old (previously used) machine id then a certain amount of time had to have passed. Thus allowed the user to break their machine and pull out an old one.
Also to consider if you make one, how will you stop the app from working? Ppl are pretty clever it will need to be core compiled.
However that all being said, the various licensing services are pro at this and can cater for most needs. Plus in their experience theyve already overcome the security pitfalls. Id name one that I like except its yours to search out.
Nice if you can come on back with and positive or negative outcomes from your trails.
function getMachineId() {
$fingerprint = [php_uname(), disk_total_space('.'), filectime('/'), phpversion()];
return hash('sha256', json_encode($fingerprint));
}
This will get a probably-unique id based on a hash of:
The server's OS, OS version, hostname, and architecture.
The total space (not free space) on the drive where the php script is.
The Unix timestamp creation time of the computer's root file system.
The currently installed PHP version.
Unlike the other answers it doesn't depend on shell_exec() being enabled.
I want to reward users if they refer a friend. I've been using the following code to do it, but I'm worried that it might not be secure (users make fake accounts to game it). Can I improve this code? Are there any other alternative scripts that do this better?
if (isset($_GET['refer']) || isset($_GET['r'])) {
global $database, $session;
if (!$session->logged_in) {
$username = mysql_safe($_GET['refer']);
if($database->usernameTaken($username)) {
$userip= getRealIP();
$q="SELECT uname FROM " . TBL_USERS . " WHERE ipad = '$userip'";
$result=mysql_query($q, $database->connection);
$result = mysql_numrows($result);
if ($result == 0) {
$_SESSION['referer'] = $username;
}
}
}
function getRealIP()
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) //check ip from share internet
{
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) //to check ip is pass from proxy
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$ip=$_SERVER['REMOTE_ADDR'];
}
return $ip;
}
It depends on what level of abuse you're expecting.
Non-technically:
Are rewards transferable, are they tangible or not? Can I just create a bunch of acounts, then use ALL of them to refer a bunch of other accounts, and reap rewards on my fake accounts and send them to my main? If I create 20 accounts, and use each to refer once, do I wind up with 20x the rewards (spread across my fake accounts).
I can create fake accounts and log in from different places, easily.
Options: make it harder to claim the reward. If the user just has to create an account, it's trivial. If they have to log in and then do X, Y, and Z, it's harder to do, and you'll see less fakes.
Technically:
First off: you're relying on headers for IPs (X_FORWARDED_FOR, etc.) which can be spoofed, fairly easily. So if you're trying to limit it to one-per-IP, this is one flaw.
Second, while you're sanitizing the username, it appears, you do not appear to be sanitizing the IP before using it in a query. If you're going to do manual sanitization, do it consistently, or you have gaping holes. In this case, you can spoof the IP string - I don't know what PHP will do with a bogus string, but if it doesn't barf on it, you're asking for attacks.
Thirdly: I can come from an array of sites. If I hard reset my DSL, I get a new IP most of the time. I can log in from work. I can log in from my webserver box. All have unique IPs. I can find proxies which may or may not actually set those fields.
You can look at other identification. Simplest: cookies. Crazily more complex: things like this: https://panopticlick.eff.org/index.php?action=log&js=yes
i'm trying to use sessions to store the amount of login attempts. When the maximum of login attempts is reached i'm storing the client's ip address in a blacklist table.
Some things i've taken into account, you might need to know about:
I'm using session_regenerate_id(); after i set a session value.
I'm not using any cookies apart from the session since this is not necessary and not 2012 :p
The users ip is blacklisted until i mannually delete his row from the blacklist table.
The SESSION_MAX_ATTEMPTS is a defined constant and set to 5.
The index.php?module=login&task=blacklist page is just showing the user a message that it's blacklisted. This page does not have any functionallity.
i'm using a custom build php framework, so i had to translate some OOP called method's to simplified php code.
The following function is called before a login query is executed:
private function preventAttack()
{
$blocked = getData("SELECT count(*) as blocked FROM blacklist WHERE ip = #Value0;", Array( $_SERVER['REMOTE_ADDR'] ));
if($blocked[0]["blocked"] == "1")
{
redirect("index.php?module=login&task=blacklist");
}
$old = (int)$this->session->get("login_attempts");
if(!empty($old))
{
if($old > SESSION_MAX_ATTEMPTS)
{
setData("INSERT INTO blacklist SET ip = #Value0;", Array( $_SERVER['REMOTE_ADDR'] ));
redirect("index.php?module=login&task=blacklist");
}
else
{
$old++;
$this->session->set("login_attempts",$old);
}
}
else
{
$this->session->set("login_attempts", 0);
}
}
The first if statement works including both query's but i'm stuck at what's the best way to store the amount of attempts and what's the best way to ++ it? Maybe you guys can set me in the right direction.
If you have any questions about my code, please add a comment. I know it's a bit unreadable since it's from my framework, i've translated this a bit before posting it.
Store the number of failed attempts in the database, not the session. N.B.: You probably need to keep each failure along with a timestamp in its own record (and ignore/delete anything older than a threshold).
By the way, in response to deceze's comment:
Ginormous flaw in this approach: sessions depends on the client sending a cookie. Real attackers will simply not send the cookie back. ziiiing You'll have to go by IP for everything.
The solution to this is that you don't accept login attempts that don't come with a valid session cookie, set elsewhere.
Thanks a lot guys, i've learned a lot from your comments. For users with the same problem i'll explain what i've learned and how i'm using that knowledge.
What i've learned so far:
Real attackers will simply not send a cookie back. So using cookies or sessions doesn't make any sense in this case.
If you want to blacklist attackers, use can use a firewall that does this automatically for you. It makes no sense to do this from your script, sessions are too easily circumvented and if you check IP addresses you could block out entire offices or schools on the same external IP.
Whatever you do: store the login attempts data only on the server in a database/flatfile/etc. The user or attacker will not be able to edit this data so easily.
And if you do store data on your web server for performance, store it in a file, not a DB.
If i forgot something please comment and i will edit this post.
I checked with my hosting provider and they are already blocking out a lot of these attackers using solutions like the firewall mentioned above. So i will stop trying to also do this in my scripts.
My script is fixed now and only blacklisting users and noob hackers guessing passwords:
private function preventAttack()
{
$blocked = getData("SELECT count(*) as blocked FROM blacklist WHERE ip = #Value0;", Array( $_SERVER['REMOTE_ADDR'] ));
if($blocked[0]["blocked"] == "1")
{
redirect("index.php?module=login&task=blacklist");
}
$old = (int)$this->session->get("login_attempts");
if($old > 0)
{
if(($old + 1) >= SESSION_MAX_ATTEMPTS)
{
setData("INSERT INTO blacklist SET ip = #Value0;", Array( $_SERVER['REMOTE_ADDR'] ));
$this->session->set("login_attempts", 0);
redirect("index.php?module=login&task=blacklist");
}
else
{
$old++;
$this->session->set("login_attempts",$old);
}
}
else
{
$this->session->set("login_attempts", 1);
}
}
Since i'm not quite sure how many users this platform is going to get and i don't want a possible high mysql server load i've decided not to store the login attempts in the database. Maybe in the future, who knows.
hi I just want your opinions about this code I found on a website for detect real search spiders from spammer is it good?? and do you have any recommendations for other scripts or methods for this subject
<?php
$ua = $_SERVER['HTTP_USER_AGENT'];
$spiders=array('msnbot','googlebot','yahoo');
$pattern=array("/\.google\.com$/","/search\.live\.com$/","/\.yahoo\.com$/");
for($i=0;$i < count($spiders) and $i < count($pattern);$i++)
{
if(stristr($ua, $spiders[$i])){
//it's pretending to be MSN's bot or Google's bot
$ip = $_SERVER['REMOTE_ADDR'];
$hostname = gethostbyaddr($ip);
if(!preg_match($pattern[$i], $hostname))
{
//the hostname does not belong to either live.com or googlebot.com.
//Remember the UA already said it is either MSNBot or Googlebot.
//So it's a spammer.
echo "spammer";
exit;
}
else{
//Now we have a hit that half-passes the check. One last go:
$real_ip = gethostbyname($hostname);
if($ip != $real_ip){
//spammer!
echo "Please leave Now spammr";
break;
}
else{
//real bot
}
}
}
else
{
echo "hello user";
}
}
note: it used user agent switcher with this code and it worked perfectly but am not sure if it will work in real world, so what do you think??
What would keep a spammer from simply giving an entirely correct user agent string?
I think this is fairly pointless. You would have to at least compare IP ranges (or their name servers) as well in order to get reliable results. This is possible for Google:
Google Webmaster Central: How to verify Googlebot
but even if you test for Google and Bing this way, a spambot can enter your site simply by giving a browser user-agent. Therefore, it is ultimately impossible to detect a spam-bot. They are a reality, and there is no good way to keep them out from a web site.
you can also have htaccess so that things like this will be prevented just like on this tutorial
http://perishablepress.com/press/2007/06/28/ultimate-htaccess-blacklist/