I want to delete a file by using PHP. I have used the unlink() function, but I was wondering about the security of unlink. Is the file completely deleted from the server? I want to make sure that there is no way to get the file back and the file is completely removed from the server.
open the file in binary mode for writing, write 1's over the entire file, close the file, and then unlink it. overwrites any data within the file so it cannot be recovered.
Personally i would say use 1's instead of 0's as 1's are actual data and will always write, where as 0's may not write, depending on several factors.
Edit: After some thought, and reading of comments, i would go with a hybrid approach, depending on "how deleted" you want the file to be, if you simply wish to make it so the data cannot be recovered, overwrite the entire files length with 1's as this is fast, and destroys the data, the problem with this, is it leaves a set length of uniform data on the disk which infers a file USED to be there and gives away the files length, giving vital pieces of forensic information. Simply writing random data will not avoid this also, as if all the drive sectors around this file are untouched, this will also leave a forensic trace.
The best solution factoring in forensic deletion, obfuscation and plausible deniability (again, this is overkill, but im adding it for the sake of adding it), overwrite the entire length of the file with 1's and then, for HALF the length of the file in bytes, write from mt_rand in random length sizes, from random starting points, leaving the impression that many files of varying lengths used to be in this area, thus creating a false trail. (again, this is completely overkill and is generally only needed by serial killers and the CIA, but im adding it for the sake of doing so).
the US government used to recommend a seven step wipe, for disks.
1) all '1's
2) all '0's
3) the pattern '01'
4) the pattern '10'
5) a random pattern
6) all '1'
7) a random pattern,
re the code sample, using a language like PHP is wrong for this type of wipe as your relaying on the OS really wipeing the file and not doing something cleaver like only wipeing it the last time or just unlinking it, however...
(untested)
$filename = "/usr/local/something.txt";
$size = filesize($filename);
$pat1 = chr(0);
$pat2 = chr(255);
$pat3 = chr(170);
$pat4 = chr(85);
$mask = str_repeat($pat1, $size);
file_put_contents($filename, $mask);
$mask = str_repeat($pat2, $size);
file_put_contents($filename, $mask);
$mask = str_repeat($pat3, $size);
file_put_contents($filename, $mask);
$mask = str_repeat($pat4, $size);
file_put_contents($filename, $mask);
This might not answer HOW to perfectly delete a file "with PHP", but it answers your question: "Is the file completely deleted from the server ?"
In some cases, No! (on UNIX/POSIX OS).
According to the highest voted comment on the offical PHP unlink() manual page, the unlink function does not really delete the file, it's deleting the system link to the file's content ! As files can have several files names (!) [symlinks?] the file will only be deleted when ALL file names are unlinked. So, if your file has 2 names, then unlink() will not really delete the file unless you unlink() both file names. Dear linux guys, please correct me here if necessary.
This might be why the function is called unLINK() and not delete() !!!
Here a full quote of the excellent comment:
Deleted a large file but seeing no increase in free space or decrease of disk usage? Using UNIX or other POSIX OS? The unlink() is not about removing file, it's about removing a file name. The manpage says: `unlink - delete a name and possibly the file it refers to''. Most of the time a file has just one name -- removing it will also remove (free, deallocate) thebody' of file (with one caveat, see below). That's the simple, usual case.
However, it's perfectly fine for a file to have several names (see the link() function), in the same or different directories. All the names will refer to the file body and keep it alive', so to say. Only when all the names are removed, the body of file actually is freed. The caveat: A file's body may *also* bekept alive' (still using diskspace) by a process holding the file open. The body will not be deallocated (will not free disk space) as long as the process holds it open. In fact, there's a fancy way of resurrecting a file removed by a mistake but still held open by a process...
Have a look on unlink()'s sister function link() here.
The (imo) best way to delete a file via PHP:
The way to go to really delete a file with PHP (in linux) is to use the exec() function, which executes real bash commands (doing things with linux bash feel correct btw). In this case, the file test.jpg would be deleted by doing:
exec("rm test.jpg);
More info on how to use rm (remove) correctly can be found for example here. Please note: PHP needs the right to delete the file!
UPDATE: Unfortunatly, the linux rm command ALSO does not really delete the file if it has two names/links. Look here for more info.
I'll have a deeper research on that and give feedback...
It is possible that because of some fragmentation on the disk some parts of file will stay, even if the file is totally overwritten.
The other way is to run (by shell_exec()) external program, that is system specific. Here is an example (for Windows), however I have not tested it.
You should do multiple passes of overwriting to deminish traces. For instance using the US DoD 5220-22.M : "Overwrite all addressable locations with a character, its complement, then a random character and verify" (from killdisk site)
Here's what the EFF recommends to permanently remove a file http://ssd.eff.org/tech/deletion.
In my embedded Ubuntu device, I use: echo exec('rm /usr/share/subdirectory/subdirectory/filename'); This works for me.
if you use rm -f (--force) then linux will
ignore nonexistent files and arguments, never prompt
rm -d will
remove empty directories
If you enter rm --help at the prompt you get the help screen. The last lines read:
Note that if you use rm to remove a file, it might be possible to recover some of its contents, given sufficient expertise and/or time. For greater assurance that the contents are truly unrecoverable, consider using shred.
Since my system is a "closed" system then I'm not concerned about violating security issues. My logic being that one must have the system password to SSH into the OS and the only user interface is via web pages.
#Sliq's comments are still true to date. You need to decide for your case.
Related
I found this simple function that simply clears the contents of a file:
function createEmptyFile($filename)
{
//
$fp = fopen($filename, 'w');
//
fclose($fp);
}
For example, resets the log, or something else.
But I have a question whether this function is really 100% safe, that is, its behavior is uniquely determined, and the result will be as expected.
I have a strange feeling that there is a possibility that the file: may not be cleaned, or may be damaged, or filled with random data in some case.
I understand that perhaps this is all prejudice, but I would like to know exactly how right it is. In particular, I am confused by the absence of any command between opening and closing the handle.
For example, I assume that if the PHP interpreter will perform optimization, then due to the lack of a command, between opening and closing the handle, it may skip this block, or something like that.
Sorry if I create a panic in vain.
I am confused by the absence of any command between opening and closing the handle.
Let me put something to the front:
The left '{' and the right '}' bracket are flow-controls and need commands in between. fopen() and fclose() are not. Though these are only 2 commands they inherit many more.
That said lets look into the tasks of them: fopen() does ...
(a) look for the specific file,
(b) if the file is not existing it creates the file
(c) opens the file for writing only,
(d) redirects the file-pointer to the beginning of the file,
(e) if the file is longer then zero it will truncate it to zero length.
In particular, I am confused by the absence of any command between opening and closing the handle.
You see there are many "commands" in between. So don't worry.
I have a strange feeling that there is a possibility that the file: may not be cleaned
To be exact, the file is not cleared, it is truncated to zero length. The earlier data of this file is still in the memory of your data-storage. But this is subject to your operation-system. There exist programs to delete these data-blocks entirely.
[...], or may be damaged
I don't understand the question. You are going to delete a file - what more damage do you expect?
[...], or filled with random data in some case.
That is C-style for creating variables by setting up a pointer to free memory but not clearing the data earlier was in it and giving this duty to you. But here it is just a truncate and not a redirecting of the file pointer. This fear could be ignored.
But I have a question whether this function is really 100% safe, that is, its behavior is uniquely determined, and the result will be as expected.
Yes, normally the behavior is uniquely determined. But you have to expect some side-effects:
if the file does not exist it will be created. You need to have write-access to the directory. (else it will come back with false)
if you have no write-access to the file it will come back with false.
if your php-environment uses "safe mode" the possible difference of owner and user leads to a fail of fopen(). You need to be sure that the file is worked only by you.
It can happen that you just write fopen() and don't check the return-parameter. That may cause a problem and lead to: not 100% safe if you don't react correctly.
So - yes fopen() and fclose() are sufficient, correct and inform you if the work is not done properly.
I am writing some json results in files in PHP on shared hosting (fwrite).
Then I read those files to extract json results (file_get_contents).
It happens some times (maybe one out of more than one thousand) that when I read this file it appears truncated: I can only read a multiple of the first 32768 bytes of the file.
I added some code to copy/paste the file I am reading in case the json string is not valid, and I then get 2 different files: the original one was correctly written as it contains a valid json string and the copied one contains only the beginning of the original one and has a size of x*32768 bytes.
Would you have any idea of what could be the problem and how to solve this? (I don't know how to investigate further)
Thank you
Without example code it is impossible to give a 'fix my code' answer, but when doing file write/read sort of programming, you should follow a simple process (which, from the description, is missing one fairly critical step!)
First, write to a TEMP file (you are writing to a file, but it is important here to write to a TEMP file - otherwise, you could have race conditions....... ;);
an easy way to do that in php
$yourData = "whateverYourDataIs....";
$goodfilename = 'whateverYourGoodFileNameIsForYourData.json';
$tempfilename = 'tempfile' . time(); // MANY ways to do this (lots of SO posts on it - just get a unique name every time you write ('unique' may not be needed if you only occasionally do a write, but it is a good safety measure to avoid collisions and time() works for many programs.)
// Now, use $tempfilename in your fwrite.
$fwrite = fwrite($tempfilename,$yourData);
if ($fwrite === false) {
// the write failed, so do whatever 'error' function you may need
// since it failed, there should be no file, but not a bad idea to attempt to delete it
unlink ($tempfile);
}
else {
// the write succeeded, so let's do a 'sanity check' on the file to make sure it is good JSON (this is a 'paranoid' check, but "better safe than sorry", right?)
if(json_decode($tempfile)){
// we know the file is good JSON, so now RENAME (this is really fast, so collisions are almost impossible) NOTE: see http://php.net/manual/en/function.rename.php comments for some potential challenges and workarounds if you have trouble with rename.
rename($tempfilename,$goodfilename);
}
// Now, the GOOD file will contain your new data - and those read issues are gone! (though never say 'never' - it may be possible, but very unlikely!)
}
This may/not be your issue directly and you will have to suit this to fit your code, but as a safety factor - and a good way to avoid collisions, it should give you ~100% read success, which I believe is what you are after!)
If this doesn't help, then some direct code will be needed to provide a more complete answer.
As suggested by #UlrichEckhardt comment, it was due to read / write concurrency problem. I was trying to read a file that was being writen. I solved this by just waiting before trying to read the file again
I need to download a file via PHP ftp_get(), but the foolish provider is using directories and file names contaning whitespace.. The file path I'm dealing with is similar to /product info/more stuff/inventory and stuff.csv
The spaces in the path and in the filename itself is making it difficult to retrieve anything. I already tried the following without success:
$path = "/product\ info/more\ stuff/inventory\ and\ stuff.csv";
$path = "/product%20info/more%20stuff/inventory%20and%20stuff.csv";
$path = '"/product info/more stuff/inventory and stuff.csv"';
Thanks again for taking the time to help me out.
Your third attempt, quoting the complete path, was already the recommended approach. Though it very much depends on the actual server implementation.
FTP per RFC859 is comprised of a terminal session and a data transfer channel. Basically the FTP server provides a mini-shell on the command port. As such, typical shell string escaping rules do apply. URL encoding can be ruled out here.
I'd advise first to use single quotes however. Preferrably use escapeshellarg() to apply them. And try ftp_nb_get() while at it.
$path = "/foo foo/bar bar/baz baz.csv";
ftp_nb_get($con, "save.csv", escapeshellarg($path), 2);
If that doesn't work, further debugging is necessary. While all ftp_* function arguments are left unprocessed, you could as well try to send a ftp_raw request. This won't actually activate the data channel reading, but might return a more concrete error response.
print_r(ftp_raw($con, "RETR '/path to/some file.csv'\r\n"));
And I'm just gonna say it, if you're still getting a file not found error then; it's entirely possible that the file really doesn't exist at the presumed location. In that case manually traverse the directory structure with ftp_nlist and ftp_rawlist with var_dump (in case of extra trailing spaces for subdirs).
Alternatively just use PHPs ftp:// stream wrapper (which also supports PASV mode). Whose implementation is distinct from that of the ext/ftp functions. Here funnily enough, URL encoding is again the correct approach, but quoting still necessary (ftp_fopen_wrapper.c does not quote itself):
= file_get_contents("ftp://user:pw#example.org/'path%20to/file%20and.csv'");
// Inline quotes may likely trip up some FTP server implementations..
A much better alternative though is just using cURL.
// You'll have to use the long-winded PHP curl functions of course.
print curl("ftp://.../file with spaces.csv")->exec();
Last option is just resorting to calling a Unixland client. (If not wget, than a plain ftp client.)
$url = escapeshellarg("ftp://user:pw#ftp.example.org/$path");
$file = `wget $url`;
If you still can't retrieve any files, you'll have to look for an alternative FTP client in PHP for further debugging. Guess who wrote one.
To get a list of files or folders with spaces in the path.
ftp_chdir($conn, $path);
$children = ftp_rawlist($conn,'-a .');
Source
I have a file that I'm using to log IP addresses for a client. They want to keep the last 500 lines of the file. It is on a Linux system with PHP4 (oh no!).
I was going to add to the file one line at a time with new IP addresses. We don't have access to cron so I would probably need to make this function do the line-limit cleanup as well.
I was thinking either using like exec('tail [some params]') or maybe reading the file in with PHP, exploding it on newlines into an array, getting the last 1000 elements, and writing it back. Seems kind of memory intensive though.
What's a better way to do this?
Update:
Per #meagar's comment below, if I wanted to use the zip functionality, how would I do that within my PHP script? (no access to cron)
if(rand(0,10) == 10){
shell_exec("find . logfile.txt [where size > 1mb] -exec zip {} \;")
}
Will zip enumerate the files automatically if there is an existing file or do I need to do that manually?
The fastest way is probably, as you suggested, to use tail:
passthru("tail -n 500 $filename");
(passthru does the same as exec only it outputs the entire program output to stdout. You can capture the output using an output buffer)
[edit]
I agree with a previous comment that a log rotate would be infinitely better... but you did state that you don't have access to cron so I'm assuming you can't do logrotate either.
logrotate
This would be the "proper" answer, and it's not difficult to set this up either.
You may get the number of lines using count(explode("\n", file_get_contents("log.txt"))) and if it is equal to 1000, get the substring starting from the first \n to the end, add the new IP address and write the whole file again.
It's almost the same as writing the new IP by opening the file in a+ mode.
Sorry for my bad English.
I must to check 2 csv files, if strings with one id is different, must write to file.
If there is no string with id from 1st file in second file, must write this to file too.
it works, but with element (id=47) i have got a trouble. it into to files, but script sad, that there is only in one.
download script you can from here
http://sil-design.ru/uploads/script.zip
If you do a echo $str1[0].' - '.$str2[0].'<br />'; you will see that the two 47's are never compared. Also I am not sure what the t is in: $f2 = fopen($fileurl, 'rt');.
If you open your backup.csv in notepad and place your cursor after the 47;XL and hold delete to delete anything after it and save. Then try your script again, it should work. It seems that the backup.csv was created in a weird way, I am guessing PHP is getting an EOF before the file has even ended!