7

Say I have a hypothetical PHP application which will store hashed passwords in a file in the same directory. At first, this seems like it would be very insecure, since one could guess at the name of the file and open it via a web browser (http://example.com/php-app/hashed_passwords.txt).

However, I was wondering about the level of security this would provide using a few different methods:

  • Security through obscurity: giving the text file a nonsense name that would be hard to guess (hashed_password_wefhbweifvbewuivgbwueigfvu4gf.txt)
  • Giving the text file a certain file extension (hashed_password.secret), and instructing the web server to return a 403 when it is requested
  • Encrypting the text file with a key hard-coded into the PHP application
  • Storing the information in a commented-out part of a PHP file, so it can only be read from the back end (and will return a blank file in a web browser)

Compared to a database, are any of these methods more or less secure, and could they provide a reasonable level of security (about that of a database) against attackers?

Maya
  • 167
  • 6
Radvylf Programs
  • 599
  • 4
  • 12
  • 20
    _"[...] and open it via a web browser"_ Why would you serve these files visible to the outside?! – Num Lock Sep 06 '19 at 10:38
  • 5
    _"At first, this seems like it would be very insecure, since one could guess at the name of the file and open it via a web browser ..."_ - Only if your code lives in the web root, which you should _never_ do. (Of course, PHP advocates this practice by default so yay) – marcelm Sep 06 '19 at 14:32
  • 1
    Isn't chosing to put it in a database vs. a text file for security inherently "security through obscurity"? Databases themselves are ultimately stored in files. (".sdf" and ".mdf" files on my SQL install), so the only extra security the DB tool is providing is that its file format is tougher to write a program to read. – T.E.D. Sep 06 '19 at 18:28

5 Answers5

14

The best measure to do this is to store such file on a directory outside the web root.

Basically, all of the solutions 'solve' the problem, in making the password file not available. Their differences stem on the kind of configuration error that needs to happen for it to break.

Surprisingly, such misconfigurations do happen from time to time, (e.g. when updating or reinstalling a system) and thee solutions simply vary on how robust they are to them. Also, sometimes these configurations are set in .htaccess files, but then a config changed at the server and they were not applied.

  1. Breaks if the web server generated an Index list.
  2. Fails if the piece configuring the web server not to serve .secret files was not added/applied, or if an editor left a backup file with an unprotected extension
  3. Would fail if the PHP files were not being interpreted (mod_php not loaded?) or if the password was not changed from the default.
  4. Would also fail if the PHP files were not being interpreted

Placing the file outside the web root (eg. /etc/php-app/hashed_passwords.txt) makes it harder that it gets downloaded by a malicious actor.

Obviously, nothing stops you from combining several of these methods (although at some point that would win you little).

Note: if using the .php route, you shall make sure that a malicious user cannot insert code into the generated files, leading to a code execution vulnerability. Generally this means you will die at the beginning (e.g. starting the file with <?php die(1);). You may also use __halt_compiler to ensure that no part of the rest of the file is interpreted by PHP.

The main difference I see on using files vs connecting to a database server (note you would face the same dilemma of how to name the file if you used SQlite) would be on its robustness for race conditions. Suppose that a user is performing a change password action at the same time that another one is signing up. A naive implementation using files would be vulnerable to a race condition where the recently created user would be lost when replacing the file with the new password hash, whereas a database would automatically care take of that (a naive implementation would be vulnerable to SQL injection, though).

Other differences would be at permissions, backups and scalability:

  • If the app connects to a database, the code itself could be read-only at the filesystem, whereas the files with information stored need write access there (and if you are using .php files, they need to be able to write into a file where code could be executed if written into, so a file-writing vulnerability would automatically become an execution vulnerability).
  • If there is already a system in place to backup the database server, that is easiest, rather than having to do a custom backup of a weirdly named file. On the other hand, using a file means that simply copying the directory results in a full backup, no need to dump the db separately.
  • If you may end up scaling horizontally, it is better to go with a normal database. Often simply making several nodes point to the same database make them all work (you should share the sessions, too). If you used a file database, that means you would have to share that file (by nfs, perhaps?) and have it writable by the multiple nodes (with all the added complication of race conditions, just with more nodes and a less capable filesystem layer).

And obviously, running a database server imposes certain overhead. If there isn't already a need to be using one, and the server resources are constrained, it may be preferable to use files simply to be lighter.

As you see, mostly the reasons that would lead your decision of using a file vs a database server are not about security, since you could have an equivalently secure app in both cases.

Dijkgraaf
  • 463
  • 4
  • 10
Ángel
  • 18,188
  • 3
  • 26
  • 63
  • Wouldn't you generally also give ownership of that file to the service that your app runs under--and remove read access from everyone else? – Bill K Sep 06 '19 at 16:26
  • I generally would if the server needs write access to the file. Although if it's a generic user for the webserver, such as www-data, that wouldn't help much, as the bigger risk probably comes from another web application (running as the same user) not from a shell account (I am assuming the server doesn't have normal clients connecting through ssh). On the other hand, if the application only needs read access, I would _not_ give ownership to the application, and it could be given through a group or ACL instead. – Ángel Sep 07 '19 at 20:50
3

In an arbitrary sense a database is a file - in Unix systems it definitely is. I'm not sure why however, if you want to store anything in a file that you don't want accessible, you aren't storing it outside of web root. Doing that immediately renders bullet point one and two useless (plus there is no such thing as security through obscurity).

Bullet point 4 fails if an attacker can get the file to dump without executing. Number 3 gives an aspect of a solution.

One advantage of a database is full control of users that can read and write the database. These exist in a sense with file permissions but not nearly with the same degree of granularity.

This is all ignoring other advantages with a database, such as the speed of a lookup in a database; storing 1000 users in a file makes look ups a lot harder...backups would also become messy, and all the tools you need to secirely interact with a database are already there. Let's say you separate users and passwords with a colon; what if a user submits a name with a colon in?

A file containing one or two keys, outside web root, encrypted (without the key being hard-coded in the application) and readable only by root and PHP, where a database isn't available, might be justifiable and ok from a security point of view, but your listed solutions aren't useful. I've done this for systems where I am demonstrating an early PoC of how such s thing might work although never for any production service.

LTPCGO
  • 995
  • 1
  • 6
  • 23
  • If you can get a file to dump without executing, doesn't that render most database-based PHP scripts useless, since the database creds are usually in the source code? – Radvylf Programs Sep 06 '19 at 00:29
  • @RedwolfPrograms: only if the attacker has connectivity to the database server. – Ángel Sep 06 '19 at 00:33
  • 1
    @RedwolfPrograms To clarify Angel's comment, it is easy - and depending on the server, often the default - to set up a database server so it can only be accessed within the local network (as opposed to the public internet) or even just within the same machine. – manassehkatz-Moving 2 Codidact Sep 06 '19 at 03:03
  • Wouldn't backing up files be easier? Just copy them over to another drive, and you're done. – Radvylf Programs Sep 07 '19 at 14:18
2

The problems

Security through obscurity: giving the text file a nonsense name that would be hard to guess (hashed_password_wefhbweifvbewuivgbwueigfvu4gf.txt)

This is only safe as long as nobody turns on the directory listing. If you are working with Apache + PHP it is a good rule of thumb that your PHP should be secure even for bad configurations of the Apache. Because in the end, god knows how the Apache will end up configured...

Also, there is the risk of accidental source code disclosure (see the third point).

Giving the text file a certain file extension (hashed_password.secret), and instructing the web server to return a 403 when it is requested

Same problem as above. I would say this is an improvement, though.

Encrypting the text file with a key hard-coded into the PHP application

Slightly better, but still not good. You still have the risk of accidental source code disclosure, which can easily happend with a misconfiguration or just a commit to a public git repo.

Encrypting the text file and storing the encryption key elsewhere (see this question for alternatives) would be much better.

Storing the information in a commented-out part of a PHP file, so it can only be read from the back end (and will return a blank file in a web browser)

Same issues as above, plus the very serious code execution issue Ángel writes about. I would absolutely avoid this.

The solution

Put the file outside of the web root! There it is less likely to be served accidentally. Hopefully it will also exclude it from all your git repos (it shouldn't be in them).

You could combine this with some combination of your first three suggestions. Do not use the forth!

Anders
  • 65,052
  • 24
  • 180
  • 218
0
  1. no. just no.

  2. -4. Pay extra extra care to your webserver configuration, make sure you put warning comments in the config files dealing with it and document it well. In practice, that will prolong the average time window until some clown accidentally disables the relevant configuration (notorious: fallbacks that will return source files when an interpreter fails!).

rackandboneman
  • 975
  • 4
  • 9
0

Your core problem is that if the file is accessible to your PHP app, then anything that exploits the PHP app can also access it. No way around that.

Making things a bit harder for the attacker is a good idea, but don't do half measures. Put it outside the web root and be done with it, why wouldn't you?

A database is almost always more secure than a file. Depending on which DB you are using, you could have row-level access controls available, for example.

Even more important is how you store your passwords. If you properly hash and salt them, and maybe even add a bit of pepper as described in this answer: Password Hashing: add salt + pepper or is salt enough?

Tom
  • 10,201
  • 19
  • 51