PHP security and unique naming conventions

2019-07-30 16:42发布

问题:

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?

回答1:

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.



回答2:

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)



回答3:

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



回答4:

  • 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.



回答5:

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



回答6:

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;
}


回答7:

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



回答8:

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.