Default Deny

11 November 2010
When reviewing code it is often easy to find flaws when developers attempt to filter input by excluding values that they suspect may be bad. This default accept policy, that it to accept everything by default, then deny specific dangerous inputs, is especially problematic for several reasons. Firewall designers have know about the wisdom of a default deny policy for decades, but such knowledge seems to come slow to programmers intent on pushing boundaries often at the expense of lessons from history. By employing a default allow policy, developers become locked into enumerating every single dangerous input. This may work in small situations, but as systems grow, and especially over time, this can be dangerous. Systems that denied the upload of Microsoft Active Server Pages (ASP) by blocking files with .asp extensions were safe until the introduction of dotNet and now ASP.Net files contain an extension of .aspx. The developers of systems could never anticipate this new extension would be supported in the future. Thus the task of cataloguing all the bad extensions is an impossible one. Instead, designers of systems should restrict to only good filetypes. This is a default deny policy. The core rule is that everything will be rejected. The only input to be accepted is the one that meets with strictly defined developer criteria. This allows systems to accept only the data they are designed to handle, rather than having to cope with a broad array of unknown inputs. For instance, an image upload utility should restrict files to those of image types, and probably to only GIF, JPEG, and PNG image formats. Other uploads might only allow users to upload text documents, or only certain word processing formats, or only PDF files. Restricting in this manner is not simply limited to uploads, but also to other forms of user input. Form processing should restrict input to certain types. Form fields designed to hold integers should only process values if they are integers. This checking should always take place server side, outside of the hands of end users, to provide confidence in the filtering system. Some systems designed with a default allow policy can, in fact, be safe, but it is much harder and it is impossible to maintain the safety of such a system over time. For a good example of this type of system in practice you need look no farther than includes/ in Drupal-6.19 on line 588: // Rename potentially executable files, to help prevent exploits. if (preg_match('/\.(php|pl|py|cgi|asp|js)$/i', $file->filename) && (substr($file->filename, -4) != '.txt')) { $file->filemime = 'text/plain'; $file->filepath .= '.txt'; $file->filename .= '.txt'; // As the file may be named example.php.txt, we need to munge again to // convert to example.php_.txt, then create the correct destination. $file->filename = file_munge_filename($file->filename, $extensions); $file->destination = file_destination(file_create_path($dest .'/'. $file->filename), $replace); } This stanza is clearly designed to prevent files from getting onto the server that might be executed by the web server. Looking at the extension list though you can see this is a default allow policy. One can clearly see that legacy PHP extensions supported by many servers such as .php3 and .phtml could bypass this filter. Similarly .aspx files for new ASP.Net files evade this filter. By creating a default allow the upload system makes it much easier for attackers to find flaws in the system. Be careful when reviewing code to look for these sorts of situations. Any list of "disallowed" extensions or file types just begs for a hacker to try and end run said list. In order to make system security robust over time it is much better to allow only input that developers feel is safe, rather than trying to catalogue all the unsafe inputs that could emerge.