Log in

View Full Version : push array into array in a different file



baconDelta
01-03-2012, 06:14 AM
so i'm making a cms for some mods. i'm trying to make them a handy ip banning tool. rather than them having to deal with htaccess i wanted to block ips via php.

from what i found out so far, i figure i'll make a form for the ip to be put on the ban list, then when submitted it will add the ip to the 'ban array'.
this is what i found for checking to see if someone is on the banned list, i'm going to try and mod this and work with it:


$banned = array('129.168.1.1');
if(in_array($_SERVER['REMOTE_ADDR'], $banned))
{
die();
}

i can just include a mod of this on every page i want the chosen ips banned from.

for organization i want to have the array of banned ips in it's own php file. i can do the form and all, but my question is how can i push an array of the chosen ip into an array that's in a different file?

i made the array in banned.php like so:


<?php
$banned = array();
?>

and the tool in a seperate file looks like this:


<?php

include("banned.php");

$ban_this = $_POST["ip"]; //ip being the name of the text field from the form
$stack = array($ban_this);
array_push($banned, $stack);
?>


but when it uploads, the banned file is the same, empty :/ what am i doing wrong? should i put the tool's code in banned.php and just direct the form there? i feel like it's possible with this setup though.
can anyone nudge me in the right direction?

baconDelta
01-03-2012, 06:39 AM
hm well i got it to work with a text file with this:


if(!empty( $_POST )){
$ban_this = $_POST["ip"];
$file = 'banned.txt';
$iplist = file_get_contents($file);
$iplist .= $ban_this;
file_put_contents($file, $iplist);
}

but the next ip is just mooshed together with the previous one. too bad <br /> doesn't work in a text file x.x
edit: k now i got them seperated by a space :/ still not very organized.

so now the issues would be making the ip checker cross reference the user's ip with that text file :/ here goes nothing.

djr33
01-03-2012, 06:49 AM
There are two problems with this method:
1. If you get a very long list it will become inefficient and slow, then it will take up memory later. At the very least, using unset($banned) at the end of that file* would make things run a little faster later. .htaccess is better in this way.
(If you need to include the array into the editor file so modify it, then you could put this in a conditional statement that is triggered by default but not by that editor, such as by adding a dummy variable ($editor=1) or a constant.)
2. .htaccess is more powerful than PHP. If you're trying to block users from accessing PHP-based things, then PHP can work; but .htaccess can block access to the whole server including other files and potentially stop these users from wasting bandwidth/processing power with excessive requests. Your method won't do anything except not allow access to PHP-based content. It will work, but not as well as .htaccess.

You could try to generate a .htaccess file with PHP, although you should be very cautious about this and make sure it's perfect before you use it actively or it will disable your whole site until you fix it manually. A typo in .htaccess often breaks the whole site, and that's not an exaggeration.


Currently, you're not modifying the file, or anything along those lines at all. You're just using its contents. When you USE a PHP file, you're just using it; you're never modifying the original data in it. You could need to use fopen() and fwrite() to rewrite the contents of the PHP file. It's actually not that hard, though. Here's a quick (untested) example:

$phpfile = '"<?php
$banned = array(" . implode("','"$banned) ."');
if(in_array($_SERVER['REMOTE_ADDR'], $banned)) { die(); }
?>"The only thing tricky in there is that I manually added the first and last quotes rather than using array_map to do it in an extra step; implode() adds all the quotes (and commas) in the middle.


Note: whenever you're working with files, you may run into file permission errors. Use chmod to make the files 755 or whatever gives PHP access to modify them. It's technically best to only do this for the files you will actually use, so that for security reasons other files can't be changed.



EDIT: We posted at about the same time. This post is still relevant though.

As for getting a line break in a file (which is irrelevant in PHP code, although it can make it easier to read), you can use one of the following methods:
Use a REAL line break in the quotes:

$var = 'line
break';
Use a \n special character:

$var = "line\nbreak";
Note that for \n to work, it MUST be in double quotes. In single quotes it will be ignored. And if you want literal \+n inside the double quotes, then you can use \\n.

baconDelta
01-03-2012, 07:05 AM
oh sank you dan :D you are most wise

you make it sound as though after a couple thousand blocked ips in this text file it'll start to really bog things down D:

so i guess i'll shoot for making a tool that modifies the htaccess file. it just scares me to think a mod will put in something other than an ip and it'll mess up the whole site. i'll have to put a bunch of checks and balances in :/ that's why i wanted to do it this way, seemed safer. but if you say htaccess is the way to go then i will do so.

the other thing is i tried looking up how to write a tool to add ips to the htaccess file, but couldn't find any so i figured it was a no go. i will try my hardest to make it though >B(

i didn't know the htaccess file is that much more efficient(capable of handling thousands or tens of thousands of blocked ips quickly?) ^_^ sank you once again dan.

edit: the other thing is i wouldn't want to ban the user from the entire site, just the page where they can upload something. i hope i wouldn't have to generate a whole new htaccess file everytime a new ip needs to be banned. i figured i could keep the ip ban list at the bottom of htaccess, and just append in the new one whenever.

djr33
01-03-2012, 07:18 AM
.htaccess applies to a certain directory. So you could block them from /uploads but not the rest of the site.

I'm not sure how efficient banning thousands of IPs would be, but it would be better in .htaccess than PHP. If you're doing that, it might be better to use a database, or to ban in ranges (if that applies you can ban 1.2.*.* rather than 1.2.3.4 and 1.2.3.5, etc).

As for your mods making a mistake, that's a big problem. But luckily you can prevent that. Check the submitted IP using regex to be sure it's a valid IP pattern. If it is, continue. If not, just ignore it (and maybe give them an error/warning message).
The third example on this page is an example of an IP:
http://www.regular-expressions.info/examples.html
For the PHP function to use this, check here:
http://php.net/manual/en/function.preg-match.php

baconDelta
01-03-2012, 07:34 AM
oh snap hellz yeah those will come in handy. you are ze man! ^_^

well i made zis just now, doesn't do anything though, perhaps in needs permissions or something...


<?php
if(!empty( $_POST )){
$ban_this = $_POST["ip"];
$ban_this = "deny from ".$ban_this;
$oh_god = fopen("../.htaccess", "a");
fwrite($oh_god, $ban_this);
fclose($oh_god);
}
?>


and i thought about a db too..... you mean a db working with htaccess? sounds hard D:

edit 2: oh snap nevermind it worked! :D well for now i'll just use this, and will setup the db asap. i do want the mods to see which ips are banned and all that jazz. wouldn't want them to have to see all the other crap in htaccess. i shall start working on the db. thanks a bunch dan ^_^ without your advice i'm sure this site would end up looking like swiss cheese, and running slow like some fatty with asthma.

djr33
01-03-2012, 09:27 AM
No, a database would not work with .htaccess because .htaccess must be a "flat" text file-- can't be dynamically generated "on the fly" by PHP. So it would be like your original version, and that would be ok because you only want to block access to a certain page-- PHP can block PHP based content, just not everything else-- fine for what you're doing.
I still suggest .htaccess if you can do it, but if you'd rather have the flexibility of a database (especially for 1000s of IPs), then look into a db.

baconDelta
01-04-2012, 12:35 AM
ok so far i've got this:



<?php

if(!empty( $_POST )){
$time = date("y/m/d : H:i:s", time());
$ban_this = $_POST["ip"];
$sql="INSERT INTO banned (IP, TIME)
VALUES('$ban_this', '$time')";
if (!mysql_query($sql,$connection))
{
die('<br />Error: ' . mysql_error());
}
echo "<br /><br />Successfully added!<br />";
}

?>

<?php

$query = "SELECT *
FROM banned
ORDER BY TIME DESC";
$result = mysql_query($query, $connection);
if (!$result) {
die("Database query failed: " . mysql_error());
}

while ($row = mysql_fetch_array($result)){
echo $row["IP"]." ----------- ".$row["TIME"]."<br />";
}


?>


it can push an ip to the db, and read from it. and i'm working on getting preg_match to reject an attempted duplicate entry, although it seems like the wrong function for the job since it saves matches and stuff, i just need something to return true or false. i'll keep working on it perhaps i can do without a special function for that. i'm thinking i should use the fetch function to get the data from the row, then compare and approve or reject. my IP column is 'text'. i read somewhere that the choice of 'text' takes up the most space. but when i set it to 'int' it saves the ip without the periods, and not the entire ip.

you think i'll be ok with using 'text'?

djr33
01-04-2012, 04:06 AM
You could use text or blob. I think they take up the same space. Text is probably best. Int is just for numbers. An IP is not a number because it contains dots, and removing the dots is not possible-- you could have 1.23.4.5 or 12.3.4.5 and those would be different IPs. You could use varchar if you knew exactly how long each IP would be, but they can differ in length, so that won't help. So, yes, use text.

preg_match is to check for a pattern in the input. It won't match one input compared to another (at least not how you're doing it).

What you want to do is try to search for an existing IP in the database that is the same as the new input. If you find it, then you should skip putting the new one in. If you don't find it, then you can input the new one.

$q = mysql_query("SELECT * FROM `banned` WHERE `IP`='$newip'");
if (mysql_num_rows($q)==0) {

baconDelta
01-05-2012, 07:38 AM
hm that worked! but i tried using the same style for a 'delete from the banned list' form and it's acting weird.



if(!empty( $_POST['release'])) {
$release_this = $_POST["rel_ip"];
$take = "DELETE FROM `banned` WHERE `IP`='$release_this'";
$taken = mysql_query($take, $connection);
if (mysql_num_rows($taken)==0)
{
echo "the ip has been released successfully.<br /><br />";
}
else{
echo "that IP is not on the banned list. <br /><br />";
}
}


it removed the ip, but it also displays the echo. even if the ip is not there, it will say it was successfully removed. i don't quite understand the ==0 part of that mysql_num_rows line. i thought that is saying if no rows are returned(meaning the requested ip is NOT in the ban list), it would echo the first statement(i initially had 'ip is not found' there). but it echos that statement when it's successful...and even when the ip isn't on the ban list.

edit: so bizarre, when i change that to ==1 it always echos the else statement.
http://t3.gstatic.com/images?q=tbn:ANd9GcTjd1P1IgGmN-ed3efyhJFEZ2pq296wVN2O1J5UANnbg-Tlvfe1ktWVZnVPaQ

edit2: alright since that's confusing as hell i just used mysql_affected_rows()


if(!empty( $_POST['release'])) {
$release_this = $_POST["rel_ip"];
$take = "DELETE FROM `banned` WHERE `IP`='$release_this'";
$taken = mysql_query($take, $connection);
echo "IPs released: ".mysql_affected_rows()."<br />";
}


edit3: so yeah now it does what i want it to, does this look right? the reason i ask is because sometimes i'll write something that works, but it'll be completely unorthodox or outdated.


if(!empty( $_POST['release'])) {
$release_this = $_POST["rel_ip"];
$take = "DELETE FROM `banned` WHERE `IP`='$release_this'";
$taken = mysql_query($take, $connection);
if(mysql_affected_rows()==1){
echo "the IP has been successfully released.<br />";
}
else{
echo "that IP does not exist in the banned list.<br />";
}

djr33
01-05-2012, 07:43 AM
There's a weird property of MySQL with deletion: mysql_num_rows() isn't accurate. I can't remember exactly how it works, but there's an alternative: mysql_affected_rows(), and that will work the same way. You have to use those two alternatively since one works some times and the other for the other times. It's confusing.

Your script should be working just fine (except the respond messages are wrong, but it's still "working"). Realistically, you don't even need to check if it was deleted since it's always the same result: any/every instance of an IP in the DB that matches will be deleted. Therefore after this there will be no more entries with that IP, so it is removed. It might have been there before or not, but it will still be "gone" even if it wasn't there. In other words, in the rare case that someone tries to delete an IP that wasn't in the DB, they'll still be getting what they wanted.

One important piece of information is that you should always be escaping the data in the database. At best, it might sometimes break the query and give an error, and at worst it might actually allow someone to hack the database. If this is just your mods who can do it, that's a little safer, but still it's best to be careful. Just apply this function to any user-input that is going into the database:
$var = mysql_real_escape_string($var);

In this case, do that for the IP.

baconDelta
01-05-2012, 10:59 PM
naw those echos are working correctly since it checks for affected rows ==1 not ==0. yeah i hear what you're saying but i figure it should be accurate since maybe in the future this will be useful. for instance if someone is having trouble accessing the site we can check the ban list and know for sure there is not an entry. i'm just picky like that i guess.

alright i used real escape. i just shoved it in right before the call to the db:



$time = date("y/m/d : H:i:s", time());
$ban_this = $_POST["ip"];
$why = $_POST["why"];
$ban_this = mysql_real_escape_string($ban_this);
$sql="INSERT INTO banned (IP, TIME, REASON)
VALUES('$ban_this', '$time', '$why')";


i'm not sure how to test if it's working though since i've never hacked a db lol. hopefully that's right. thanks for all your help dan :D

djr33
01-06-2012, 01:05 AM
That escaping is fine. But do the same for $why also. You don't need to worry for $time since that's generated internally and unless you made a mistake, it won't cause any MySQL parse errors (and certainly won't hack the database)-- it won't hurt, though, just in case there's ever a weird character that shows up in there.

As long as the deleting is working, that's fine I guess. Post the final code if you want me to take a look. mysql_num_rows() will probably be inaccurate (at least in some cases) with a DELETE query, though, so be aware of that.