create visitor unique ID? - php

I plan to create visitor unique ID and named as log file, as existing now I use the IP visitor as log file name i.e. logs/127.0.0.1.php but I think this is not enough because some visitor using share an IP address for PC's.
The visitor log file itself as setting place of configuration of visitors itself, so I plan to add another unique ID to identify each different visitor so let's say the log file:
logs/127.0.0.0.1-t3451dq.php, -t3451dq as unique ID
so as long as visitor browsing on my website the unique log file as setting configuration for each user (because I use plain text)
Currently I use:
<?
$filename = "./logs/".$_SERVER['REMOTE_ADDR'].".php" ; //out put logs/127.0.0.1.php
$data stripcslashes($data);
// each Visitor configuration here...
// bla...bla...
/* Writing file configurations */
$buat = fopen($filename, "w+");
fwrite($buat, "$data");
fclose($buat);
?>
so I need $filename add the $unique ID as name of their log file. Any ideas how to do that?

Try uniqid.
You can store this unique ID in the users session or in a cookie.
Example (not tested)
session_start();
if(!isset($_SESSION['uniqueID']))
{
$_SESSION['uniqueID'] = uniqid();
}
$filename = "./logs/".$_SESSION['uniqueID'].$_SERVER['REMOTE_ADDR'].".php" ;
Using a session will mean that if the same user closes their browser (or the session expires) they will get a new ID, which may or may not be what you want.
If you want a more persistent tracker then you may be better using cookies, and store the ID in the cookie (create a new ID if no cookie exists).
if(!isset($_COOKIE['uniqueID']))
{
$expire=time()+60*60*24*30;//however long you want
setcookie('uniqueID', uniqid(), $expire);
}
$filename = "./logs/".$_COOKIE['uniqueID'].$_SERVER['REMOTE_ADDR'].".php" ;
If you cannot use cookies/session then you may need to pass around the ID in your URL query string e.g. mypage.php?id=35dfgdfg3434

Create something out of his IP and the first time he enters the page. That should be unique.

You have two simple options : uniqid or as you're creating a file tempnam
Tempnam example :
function log($string, $userIP = null, $filename = null){
// Check if filename exists
if(!file_exists(LOG_PATH.$filename)){
$filename = tempname(LOG_PATH, $userIP.' - ');
if(!$filename){
return false;
}
}
// write log into file
$file = file_put_contents($filename, $string);
if($file === false || $file != strlen($string)){
return false;
}
return $filename
}

using log files for this type of use is unnecessary, it's alot easier to just shunt this type of data to a database. If it's just temporary data then use Cookies and/or Sessions

Related

PHP Login System without MySQL

So, I'm trying to set up a login script without the usage of MySQL because I don't have any experience with it at all I need to wait one year until I can sign up for a course at our school.
My thoughts of approaching this:
The user needs to login with a username and a invitation key. I'd like to have a .txt (I know that's insecure as hell but whatever) where both the username and invitation key of an accepted person are stored. The PHP scripts compares the sent credentials with the ones stored in the .txt. If it matches, you're logged in, if not, then it gets aborted.
So, how can I read data from a .txt and how could I format the .txt so my script can differ between username and invitation key?
Here's what I've got so far:
if(isset($_POST['auth']) && $_SERVER['REQUEST_METHOD'] == 'POST'){
session_start();
$usr=$_POST['user'];
$invkey=$_POST['key'];
function authenticate($usr, $invkey){
}
}
Thanks in advance!
PS: I know this would be very insecure, so if you're bored you could give me tips how I could secure it. :)
You can keep usernames and keys in one string, one user per line in a txt file.
user1|user1_inv_key
user2|user2_inv_key
...
So you can use fgets() and explode()
Your code should look like this :
if(isset($_POST['auth']) && $_SERVER['REQUEST_METHOD'] == 'POST'){
session_start();
$usr=$_POST['user'];
$invkey=$_POST['key'];
function authenticate($usr, $invkey){
$fh = fopen('auth.txt', 'r');
if ($handle) {
while (($line = fgets($handle)) !== false) {
// Exploding the line, using | as separator.
$info = explode($line, '|');
$username = $info[0];
$key = $info[1];
if($usr == $username && $key == $invkey) return true;
}
} else {
// error opening the file.
}
// Return false because something went wrong or data wasn't correct.
return false;
}
}
You could use a normal csv file like
user1,key1
user2,key2
and then use the str_getcsv function to parse it.
See http://php.net/manual/de/function.str-getcsv.php for more documentation.
You would have to do something like
$txt_file = file_get_contents('path/to/file.txt');
Then you could explode $txt_file based on a delimiter for each user, or a new line or something. Then, you could add a new user to your file with something like
file_put_contents("path/to/file.txt", $user_string, FILE_APPEND);
To secure it, you could save the password for the user as the product of a one way hash, like md5.
$salt = 'whatever you want';
$securish_pw = md5($password.$salt);
Then store that. Then, someone logs in, you find their line in the txt file, put their compared password through the same md5 function and if they are the same, then you log them in. This way, you don't store a plain text password.
All said, you should just learn a bit about how to use a database. It's pretty simple if you use PHP's PDO library and there's a million and one tutorials out there explaining how to do exactly what you're trying to accomplish.

PHP and File handling process to record visitor ips and banned them

i would like to record the visitor ips in a file. then after second attempt to visit, i dont want that person to visit that website within 10 days. If he visits again then i want banned message to be produced. i can do this with mysql database but i want to do through file handling. Its urgent
I think following code snippet will be helpfull for you:
define('IP_FILE', 'ip.php');//where to save IP data
define('LIMIT_SECONDS', 10*24*3600); // 10 days
//loading IP data
function loadIPFile() {
if (file_exists(IP_FILE)) {
require_once IP_FILE;
if (isset($ipArray) and is_array($ipArray)) {
return $ipArray;
}
}
return array();
}
//saving IP data
function saveIPFile($ipArray) {
$fp = fopen(IP_FILE, 'wb');
fwrite($fp, '<?php'.PHP_EOL);
fwrite($fp, '$ipArray = '.var_export($ipArray, true).';'.PHP_EOL);
fwrite($fp, '?>');
fclose($fp);
}
$ipArray = loadIPFile();//load info into array
$ip = $_SERVER['REMOTE_ADDR'];//visitor ip
//if such ip already exists and 10 days are not behind us redirect visitor
if (isset($ipArray[$ip]) and time() - $ipArray[$ip] < LIMIT_SECONDS) {
header('Location: banned_page.php');
exit;
}
else {
//else record new ip or new time
$ipArray[$ip] = $time;
}
//save IP information
saveIPFile($ipArray);
If you know how to do this with the database, why do you want to do it with a file? A database is basically a file with functionality sitting on top to help you access the data. By using files to directly to store this sort of data you are having to rewrite the minimal access functionality that comes out of the box with MySQL. Why reinvent the wheel?
If for some reason you don't have access to MySQL, then perhaps try SQLite http://php.net/manual/en/book.sqlite.php, gives you a lot of sql type functionality, but based on a file in your local directory rather than sql server.

Securely serving files from Amazon S3

I have an app that uploads user files to S3. At the moment, the ACL for the folders and files is set to private.
I have created a db table (called docs) that stores the following info:
id
user_id
file_name (original file as specified by the user)
hash_name (random hash used to save the file on amazon)
So, when a user wants to download a file, I first check in the db table that they have access to file. I'd prefer to not have the file first downloaded to my server and then sent to the user - I'd like them to be able to grab the file directly from Amazon.
Is it OK to rely on a very very long hashname (making it basically impossible for anyone to randomly guess a filename)? In this case, I can set the ACL for each file to public-read.
Or, are there other options that I can use to serve the files whilst keeping them private?
Remember, once the link is out there, nothing prevents a user from sharing that link with others. Then again, nothing prevents the user from saving the file elsewhere and sharing a link to the copy of the file.
The best approach depends on your specific needs.
Option 1 - Time Limited Download URL
If applicable to your scenario, you can also create expiring (time-limited) custom links to the S3 contents. That would allow the user to download content for a limited amount of time, after which they would have to obtain a new link.
http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_QSAuth.html
Option 2 - Obfuscated URL
If you value avoiding running the file through your web server over the risk that a URL, however obscure, might be intentionally shared, then use the hard-to-guess link name. This would allow a link to remain valid "forever", which means the link can be shared "forever".
Option 3 - Download through your server
If you are concerned about the link being shared and certainly want users to authenticate through your website, then serve the content through your website after verifying user credentials.
This option also allows the link to remain valid "forever" but require the user to log in (or perhaps just have an authentication cookie in the browser) to access the link.
I just want to post the PHP solution with code, if anybody has the same problem.
Here's the code I used:
$aws_access_key_id = 'AKIAIOSFODNN7EXAMPLE';
$aws_secret_key = 'YourSecretKey12345';
$aws_bucket = 'bucket';
$file_path = 'directory/image.jpg';
$timeout = '+10 minutes';
// get the URL!
$url = get_public_url($aws_access_key_id,$aws_secret_key,$aws_bucket,$file_path,$timeout);
// print the URL!
echo($url);
function get_public_url($keyID, $s3Key, $bucket, $filepath, $timeout)
{
$expires = strtotime($timeout);
$stringToSign = "GET\n\n\n{$expires}\n/{$aws_bucket}/{$file_path}";
$signature = urlencode(hex2b64(hmacsha1($s3Key, utf8_encode($stringToSign))));
$url = "https://{$bucket}.s3.amazonaws.com/{$file_path}?AWSAccessKeyId={$keyID}&Signature={$signature}&Expires={$expires}";
return $url;
}
function hmacsha1($key,$data)
{
$blocksize=64;
$hashfunc='sha1';
if (strlen($key)>$blocksize)
$key=pack('H*', $hashfunc($key));
$key=str_pad($key,$blocksize,chr(0x00));
$ipad=str_repeat(chr(0x36),$blocksize);
$opad=str_repeat(chr(0x5c),$blocksize);
$hmac = pack(
'H*',$hashfunc(
($key^$opad).pack(
'H*',$hashfunc(
($key^$ipad).$data
)
)
)
);
return bin2hex($hmac);
}
function hex2b64($str)
{
$raw = '';
for ($i=0; $i < strlen($str); $i+=2)
{
$raw .= chr(hexdec(substr($str, $i, 2)));
}
return base64_encode($raw);
}

PHP security and unique naming conventions

Is anything wrong with naming images uploaded by users (for example avatars) like that:
/user_id-username.jpg
Example:
/1-feont.jpg
All images has unique name (becouse user_id is primary key), but what about security? Has that any bad influence? If has, what conventions should I do instead?
Make sure that the username is appropriately sanitized before using it as part of the filename. User id should be system generated so that should not cause any problems.
The name that you give the images is purely conventional. I do not think it is a security issue for revealing the usernames of your users. (If it is, then you better check your CMS right away!) However, if your website is not completely secure a hacker can make use of SQL injection to access your user data.
But the idea is really far-fetched. You can go ahead with using usernames. :-)
IMO, just name the images as user-user_id.jpg (Here, "user" being a normal string followed by the integer - user_id)
Generally it is fine but some users may not like their name to be displayed and the user ID being a primary key could have vulnerability if your site isn't completely secure or if a PHP vulnerability is found in the future that is currently unknown.
For this sort of thing I tend to use the following code
$createName = date('YmdHis');
$fileType = '.jpg';
$imgName = $createName.$fileType;
This should give a string like 20110702155513.jpg - this is the full date and time the image was named and is unique.
If you really want to be safe then you can write a call back function that if there was a failure due to the file name not being unique (generally because there were 2 requests in the same second - unlikely but possible), then you can use a fall back adding the user ID in the middle of the string or use the fall back as your primary naming method, so for example
if($imgName == 'inuse'){
$createName1 = date('Ym');
$createName2 = date('dHis');
$fileType = '.jpg';
$imgName = $createName1.$userId.$createName2.$fileType;
}
This allows the user ID to be hidden but entirely unique.
*Edit - * another option is to use the existing format and create an MD5 hash, the code would be something like
$user_id = 'user_id';
$username = 'username';
$fileType = '.jpg';
$fileName = md5($user_id).'-'.md5($username).$fileType;
I hope this helps
Using the date as Ryan suggests fails when you have users that upload at the same time
Using the username fails when users may change their username. "Fail" is a bit hard here, but you have to move the files around which is something that's not needed when using another solution
Using the id solves all concurrency problems if the ID is an autogenerated auto-increment ID from your database. Opposed to Kerrek SB, I don't see a problem that this makes a connection between a unique identifier and an image on your file system. I mean it's unique and you can use it.
Using the ID also makes it easy for your users to find/link their image if you constantly use the ID publicly. If you on the other hand have only the user names in URLs - like /profile/$username, then I'd use the username in the image file name, too - to be consistent.
About security: If you sanitize the username, all is fine. Just make sure that it's unique, so people can't overwrite each other's names (which means that you need to use the same sanitation rules for usernames in database as you use for usernames in image file names).
Having the username as image name also makes it "easier" to find an image if you manually need to find one, but that probably happens not often enough to be of worth for you.
To summarize, I'd go with the ID.
there is no huge risk to naming files like the way you use in your first example but to make it more secure why don't you use something better like
function Naming($username,$imagename)
{
$uniq = time() ;
$name = $uniq.'-'.$username.'-'.$imagename ;
return $name ;
}
i think its better to avoid using users ID's
Unique user_id is enough, you don't need "username".
Problem isn't here, but in count of files (in one folder).
Split them by 1000 in folder (or by 100): take first, second and third symbols of ID string and put in separate dirs:
ID = 10524.jpg
filename = 1/0/5/10524.jpg
If it's hard to write algorithm, you can try this function:
function getStorePath($filename, $base_dir)
{
//file should have extension
$ext_pos = strrpos($filename, '.');
if ($ext_pos===false) return false;
//extension will be sanitized (filtered actually)
$ext = preg_replace("|\W+|", "", substr($filename, $ext_pos+1));
if (empty($ext)) return false;
if (in_array($ext, array('php', 'shtml', 'cgi', 'inc', 'module', 'sh', 'sql', 'class'))) return false;
//filename will be filtered
$filename = preg_replace("|\W+|", "", substr($filename, 0, $ext_pos));
if (empty($filename)) $filename = mt_rand(100000, 999999).round(microtime(true)*1000000);
//let's create path to the file.
//we will take first 3 symbols of filename as names of folders
$d = realpath($base_dir).'/';
//first symbol
$d .= $filename[0].'/';
if (!file_exists($d))
{
$md = mkdir($d, 0755);
if ($md===false && !file_exists($d)) return false;
}
//second symbol
if (isset($filename[1]))
{
$d .= $filename[1].'/';
if (!file_exists($d))
{
$md = mkdir($d, 0755);
if ($md===false && !file_exists($d)) return false;
}
}
//and third symbol
if (isset($filename[2]))
{
$d .= $filename[2].'/';
if (!file_exists($d))
{
$md = mkdir($d, 0755);
if ($md===false && !file_exists($d)) return false;
}
}
if (!file_exists($d.$filename.'.'.$ext)) return $d.$filename.'.'.$ext;
else return false;
}
I typically name my images a random string, then store it in the database attached to the uploaders id. You can also store other data about the image this way.
$filename = uniqid('', true) . '.jpg';
Image Table
id | user_id | filename | etc
Just my own two cents.
If you are gonna add more avatars for users to choose from, you're better off using a function like uniqid() instead of the user of. Else, you can do that all you like.
I use an MD5 hash of the userid and time() though. Just a matter of preference.

Session Share Across Multiple Domains On Same Server

I heard the best method to share session across multiple domains on same server is to use custom php session handler. (ie, domain name different like abc.com, xyz.com but single application.)
But after i tried it, even custom php session handler that using SAME DATABASE ON 1 SERVER can't share session, when i tried to read cookie value from different domain.
Here's my custom session handler, Please kindly check or fix if something missing here. because i've tried it for a week now. can't get it to work
P.S. To get previous session id, i use link such as: newdomain.com/?ssid=[SESSION_ID]
SESSION_INCLUDE.PHP
<?php
// config
$m_host = "localhost"; //MySQL Host
$m_user = "db_user"; //MySQL User
$m_pass = "db_pass"; //MySQL Pass
$m_db = "db_name"; //MySQL Database
$table = "sess_data";
$session_expire = 600; // Session expire time, in seconds (minutes * 60 = seconds)
$gc_probability = 100; // Probability that the garbage collection function will be called. 50% chance by default
ini_set("session.gc_probability",$gc_probability);
/* Open function; Opens/starts session
Opens a connection to the database and stays open until specifically closed
This function is called first and with each page load */
function open ($s,$n) // do not modify function parameters
{
global $session_connection, $m_host, $m_user, $m_pass, $m_db;
$session_connection = mysql_pconnect($m_host,$m_user,$m_pass);
mysql_select_db($m_db,$session_connection);
return true;
}
/* Read function; downloads data from repository to current session
Queries the mysql database, unencrypts data, and returns it.
This function is called after 'open' with each page load. */
function read ($id) // do not modify function parameters
{
global $session_connection,$session_read,$table;
$query = "SELECT data FROM `$table` WHERE id=\"{$id}\"";
$res = mysql_query($query,$session_connection);
if(mysql_num_rows($res) != 1) return ""; // must return string, not 'false'
else
{
$session_read = mysql_fetch_assoc($res);
$session_read["data"] = base64_decode($session_read["data"]);
return $session_read["data"];
}
}
function write ($id,$data) // do not modify function parameters
{
if(!$data) { return false; }
global $session_connection, $session_read, $session_expire, $table;
$expire = time() + $session_expire;
$data = mysql_real_escape_string(base64_encode($data));
if($session_read) $query = "UPDATE `$table` SET data=\"{$data}\", expire=\"{$expire}\" WHERE id=\"{$id}\"";
else $query = "INSERT INTO sess_data SET id=\"{$id}\", data=\"{$data}\", expire=\"{$expire}\"";
mysql_query($query,$session_connection);
return true;
}
function close ()
{
global $session_connection;
mysql_close($session_connection);
return true;
}
function destroy ($id) // do not modify function parameters
{
global $session_connection,$table;
$query = "DELETE FROM `$table` WHERE id=\"{$id}\"";
mysql_query($query,$session_connection);
return true;
}
function gc ($expire)
{
global $session_connection,$table;
$query = "DELETE FROM `$table` WHERE expire < ".time();
mysql_query($query,$session_connection);
}
// Set custom handlers
session_set_save_handler ("open", "close", "read", "write", "destroy", "gc");
// Start session
session_start();
?>
MySQL Database Description
create table sess_data (
id2 int not null auto_increment,
id text not null,
data text,
expire int not null,
primary key(id2)
);
You can't read cookies from one domain in another domain. That's a security thing implemented in the browser. Using a database for sessions allows you to have multiple servers share sessions on the same domain, but does not allow for multiple domains on the same server to share sessions.
If you want to share sessions between domains, you would need to implement some sort of session transfer method when you switch domains. The simplest way to do this would involve passing the session id as a GET parameter from a page on one domain to a page on the other. Then, on the other domain, you would pick up the session id and create a new session using that ID.
While that is a simple way to do it, it isn't very secure and allows for session hijacking. A better way would be to use the database to create a record with the session id in it, set a short timeout on it, and pass the ID of that record to the other domain. The other domain would then pick up the record from the database and create a session with it. If the record in the database is past it's expiration, it wouldn't pick up the session. This would provide better protection against session hijacking.
This is the purpose of session_name(). Assign a different name to each application's session to avoid collisions between $_SESSION keys. The name will be used as the session cookie's name so although both session cookies will be passed to both applications, only the one matching the application's session_name() will be used to populate $_SESSION.
// App 1
session_name('app1');
session_start();
// App 2
session_name('app2');
session_start();
You really should look into SSO (single sign-on). One option for SSO is to use OpenID (as used on SO), and using it will make your life a lot easier.
Here's an article on it : http://devzone.zend.com/article/3581
the cookies and their visibility is a problem. The browser accessing the new site would not send the session id of the old site to the server.
I think your read() does not use the ssid parameter you provide as session id but as the browser has no session with this domain the system generates one with new id as $id. Have a look if $_REQUEST['ssid'] exist in the database.
Custom session handler might a bit big for this job. You could just check if $_REQUEST['ssid'] exist in the session database and rewrite $_SESSION with it.
I was wondering if anyone could give some suggestions on my method for sharing sessions between domains on same server (same cookie storage folder).
In each pages HEAD tag on all my sites, I call the following PHP code
if(!isset($_SESSION['sso'])) {
require_once('database.php');
$sites = array('http://site1', 'http://site2');
session_regenerate_id(); //Make new session id that will be shared
$session_id = session_id();
foreach($sites as $site) {
if($site != CURRENT_SITE) {
$sesh_key = md5(SALT.$site.$session_id);
$database->insertSessionId($sesh_key, $session_id);
$url = sprintf('%s/sso_set.php?k=%s', $site, $sesh_key);
echo('<link type="text/css" rel="stylesheet" href="'.$url.'" />');
}
}
$_SESSION['sso'] = 'SET';
}
Then on each site I have a file called 'sso_set.php' which contains
<?php
session_start();
if(!isset($_SESSION['sso'])) {
require_once('database.php');
$key = $_GET['k'];
$session_id = $database->getSessionId($key);
if($session_id) {
session_destroy();
session_id($session_id);
session_start();
$database->deleteSessionId($key);
$_SESSION['sso'] = 'SET';
}
}
Is using a text/css link a good idea?
I figured this is always called even if Javascript or Images are disabled?
This code basically makes the first site out of all my sites that gets opened by the user sets the Session ID, and then passes it on to the other sites.
Seems to work pretty well.
You get a slight delay the very first time any of the sites opened and the ID is passed to the sites. But, you could do this via AJAX so the page loads fast. But, then you rely on Javascript being enabled.
Thoughts?

Categories