Re: Re: [Fwd: (SRADV00001) Arbitrary file disclosurethrough PHP file upload]

From: Date: Mon, 04 Sep 2000 23:44:41 +0000
Subject: Re: Re: [Fwd: (SRADV00001) Arbitrary file disclosurethrough PHP file upload]
References: 1  Groups: php.dev php.general 
Request: Send a blank email to php-dev+get-31998@lists.php.net to get a copy of this message
Stanislav Malyshev wrote: > RC>> Uh, any program which allows user input *must be checked* for the validity > RC>> of that input, and the same goes for returned output based on that input. > True. But! PHP gives you no good means to check if that variable > $uploaded_file is from valid user upload or not. *That's* the problem. If you are relying on user input to be safe, and sane, then it's a problem, regardless of source. I code for files that could be _anything_ improper. I agree that it's inconvenient to code all of your uploads for checking the actual data (rather than assuming a good file based on name), but if coding is done with some decent forethought of possible attacks (and just plain incompetance), it's not much of an issue compared to rebuilding a compromised server. Here's an exmaple: <?php //check-master.inc /* pgsessionid contains the unique session number so rename the file with that to keep everything connected */ $fixname = $pgsessionid; $xchgdir = "/var/spool/uploads"; $newfileorig = "$xchgdir/$fixname".".orig" ; /* start out by making a backup copy, never work on live files */ copy ("$userfile", "$newfileorig"); /* the tempfile is the starting file for record reads, clean records are then put into a brand new file */ $tempfile = "$xchgdir/$fixname" . ".temp"; $newfile = "$xchgdir/$fixname"; /* open temp for read,make newfile for write */ $fp = fopen($tempfile, "r") or die; $fp2 = fopen($newfile, "w+") or die; $buffer = fgets($fp, 1000); $data = split($tab, $buffer); $fields = count($data); rewind($fp); /* Go through all records to count number of rows in file, and make sure all rows have the same number of fields.... get record zero to start the comparison */ $buffer = fgets($fp, 1000); $data = split($tab, $buffer); $fields = count($data); rewind($fp); /* now count the rows and compare the fields to the first one */ while(!feof($fp)) { $buffer = fgets($fp, 1000); $data = split($tab, $buffer); $fieldnewcount = count($data); if ($fieldnewcount != $fields ) { $row = $row++; /*increase one row to make up for PHP counting from zero */ print "<B>The file you sent contains an inconsistant number of fields "; print "with the header.</B>\n"; /* nuke the temp and new files. Note the .orig is kept for diagnosis*/ fclose($fp) or die; fclose($fp2) or die; unlink("$tempfile"); unlink("$newfile"); exit; } $row++; } ... This same script also checks to make sure that fields in the file are of a maximum length, that the file uses the proper delimiters for data, indeed, the entire structure of the file is _determined and evaluated_. In such a case, it wouldn't matter if they fed server paths to the scripts, because a given file would have to match, exactly, the structure of the *expected* file. This is a standard security model, everything which is not explicitly allowed is forbidden. The coding style of *every* programmer must include basic security handling, shouldn't it? > The > only way now to check if the filename is prefixed by your temp upload > directory, which means you should remember that, update it on all your > scripts every time it changes and generally requires a lot of PITA work. Well, if you _assume_ the name is meaningless in the first place, and dont rely on it, and check your files for valid _data_, it doesn't matter what they feed to it, server based or local user file. I am talking about a security model where it is assumed that the server files themselves may be uploaded. > RC>> Repeat after me: > RC>> A PUBLIC WEBSERVER IS ALREADY 0WN3D BY THE NET. > Wrong. I won't go into detailed explanation, but note that "every file > readable by httpd user is publicly accessible to the web" is just > wrong. This is more about mindset than it is about today's current software practices. On a web-server level, sure, you can "request" files to not be readable, but on Unix systems, all security is file-level, _regardless_ of the application running on top of it. If a file is readable by "o" users, that file is readable by _anybody_. The applications which act as public interfaces can help to obscure the access to it, can prevent different ways of getting at it, but do not completely deny access to it. This is the security balance inherent in web servers.. > Think about it, if you still disagree, I can explain it to you > privately. No need. We're talking about different things. I understand that there are application level access-control layers built in. BUT: There's a reason why standard two-tier firewalling is used. The machines in the DMZ already have accounts accessable by anyone on the net. Those accounts can be secured and obscured to a greater level, but they are already at severe risk. I know that it's not supposed to be easy. :-) But that wasn't my point. My point was to combat the FUD of "anybody can get to /etc/passwd" with a rousing "well, duh, any user who has a login can, too...". Sealing off an easy route to it is *always* a good thing, but the mindset needed when dealing with systems that may be compromised by any one of a thousand software bugs is to approach the problem at *all* levels, rather than just assuming that "It's a secure program" because an application/process has added some access control, or tried to code around a bad OS security model. The access control of any *nix program could be circumvented all the way down to the file level, which is where the buck stops. (And then they have to gain physical access). It's just plain good practice to assume that such a thing *may* happen, intentionally or otherwise, and code for it as much as possible. > RC>> And you sure as hell don't allow your webserver user access to > RC>> anything important, regardless of how hard your webserver > /etc/passwd is important to you? Not on a server designed for public use. Everything you could find in there is already well known (a few standard system users). If you're mixing safety zones, for whatever reason, you have increased your risk. This is not rocket science, it's just plain good practice to not put any secure accounts on the same box as your public accounts, as your public services. Let's take, for example, a machine running BIND, SMTP (sendmail), Apache+PHP3, and has 10K users logging in via telnet or ssh. Each one of those accounts, or account sets, has access to all of your world readable files. If you break the application level security (indeed, this is the most *commonly* reported break), then you are down to the security provided by your machine's filesystem itself. If you code for this, your system is much less vulnerable. > How about your > webserver-protected files (like, private user directories)? Private users? On a public server? Er.... there's no privacy in public, for a reason. The application-level security overlay is not a fool-proof means of protecting files. (Locks keep honest people honest, so hide the entire house, lock it up, and don't use a public street address...) I guess I'm arguing for better coding practices in the first place, so when minor bugs are found, their impact is primarlily cosmetic until they are fixed. > How about > various passwords in user scripts? Putting passwords in user scripts is *creating additional risk*. Period. How one gets at them is a minor detail... it's like hiding the key to your front door under the mat, over the stoop, under a rock. You are putting the keys in a publicly readable file. > I remember the (righteuos) wave of rage against Microsoft when it was > discovered that you can steal any ASP file source. Now you telling me it's > small change? I was amused by the tirade because MS was selling people on a secure "product". Products, in and of themselves, are *not* secure. They have locks, but locks don't stop theft. > We have real problem here, and we need to think what to do with > it - or telling "PHP is not secure here, you need to go additional mile > for it" or doing changes. Just putting your head in the sand while > incantating "variables mucst be checked" won't do you any good. We should be doing _both_. Spreading FUD isn't helping (well, maybe it's helping folks to realize that you can't *buy* a secure, hackproof, server, nor can you build one.), and for the new coder, trying to rapidly learn security is not an easy thing.... so we should try to teach them not accedentally shoot themselves in the foot (by using defaults). > RC>> What next, a "security hole" where a PHP hosting site has > RC>> allowed their users access to /etc/passwd, and users can run > RC>> fread()? > This hole allows _script user_, not PHP developer, to get any file. Please > read bug report before going into flames. Script users, on the file level, have access to everything that httpd does, as that is the user they access the machine with. httpd has access to /etc/passwd. Whether coder or user, that file is available to them. If you are using a webserver that will deny access to certain files, as apache has done, it helps a little. -Bop -- Brought to you from iBop the iMac, a MacOS, Win95, Win98, LinuxPPC machine, which is currently in MacOS land. Your bopping may vary.

« previous php.dev (#31998) next »