• 0

Advise about PHP Security and so on...


Question

Hi,

I am currently teaching myself PHP and MySQL, by reading many of the tutorials, documentation, example scripts and so on available on the internet. I have now got to the stage, where I can understand what things are doing, and can apply different code to different situations, and not just copy sections of code from tutorials.

I am about half way through making a support desk script (done the user end; still need to do admin); using PHP, MySQL and Smarty, for my up and coming web company (won't be released to the public), and I have managed to write PHP code to cover all possibilities that I can find.

My biggest concern is security. While the security script isn't working with anything that needs to be ultra-secure, I was wondering if anyone has any general tips as to things that I should worry about? Certain code I should avoid, and how to stop SQL Injections. I maybe worrying without reason, but I worry because of the number of updates that are repeatedly released for Open Source scripts.

My second question is also security related, but my current plan is to use ZenCart for the shop part of my company. My concern with using ZenCart, is that it doesn't do exactly what I want. I am now feeling confident enough to attempt to program my own cart, with the features I require, and was wondering if there are any security tips regarding shopping cart systems? Any advice about which route to follow?

I will not be processing credit cards or anything like that on the shopping cart, and will be sending everything to Paypal for payment processing. That said, I would also like any advice regarding sending payments to Paypal, and using the IPN system. Any tutorials available on this subject?

Any advice is greatly appreciated. :)

Link to comment
Share on other sites

25 answers to this question

Recommended Posts

  • 0

First advice: Secure all numeric GET/POST HTTP values with intval() function.

For example if you use something like this:

$id = $HTTP_GET_VARS['id'];

change it to:

$id = intval($HTTP_GET_VARS['id']);

before using $id in an SQL query.

Second advice: for alphanumeric HTTP POST/GET values use mysql_escpape_string function. For example change:

$text = $HTTP_GET_VARS['text'];

to:

$text = mysql_escape_string($HTTP_GET_VARS['text']);

before using it in an SQL query.

Also verify that every value in an SQL query is enclosed by quotation marks. For example:

mysql_query("SELECT something FROM table WHERE id = $id"); 

is insecure, but

mysql_query("SELECT something FROM table WHERE id = '$id'"); 

is more secure.

I will come back with more tips as soon as I remember any. :)

Edited by nickg78
Link to comment
Share on other sites

  • 0

Thanks for the tips. I have already done the one with quotes in the query. I think I understand why you do it too. :)

In my support desk script, I currently use $_GET. So like:

if(isset($_GET['tid'])) {
$tid = $_GET['tid'];

etc etc...

What is the difference between $_GET and $HTTP_GET_VARS? I am assuming the HTTP one is the better, so how do I apply that to the same bit of code? Just replace $_GET or is there more to it than that? I am also using $_POST and $_SESSION in the same way as the $_GET.

Link to comment
Share on other sites

  • 0

Basic rule for security really: Don't trust anything the user gives you. Even cookies and such can be modified at the users end. Check and double check every bit of data before you do anything with it.

Link to comment
Share on other sites

  • 0
In my support desk script, I currently use $_GET. So like:

if(isset($_GET['tid'])) {
$tid = $_GET['tid'];

etc etc...

What is the difference between $_GET and $HTTP_GET_VARS? I am assuming the HTTP one is the better, so how do I apply that to the same bit of code? Just replace $_GET or is there more to it than that? I am also using $_POST and $_SESSION in the same way as the $_GET.

586726860[/snapback]

There is no difference security-wise between the short and the long superglobals, but the long versions have been deprecated in newer versions of PHP. Stick with the short variable names if you can.

Link to comment
Share on other sites

  • 0

PHP will auto-escape many user-supplied strings for you, provided that you have magic_quotes turned ON and that you receive the input from an HTTP post request. I agree with the above: never trust what the user gives you.

If at all possible, and this is how I code my systems, provide a running log of everything that has been done, should be done next, and possibly will be done in the future for a given user's session. This way, your script can intelligently detect if there is something awry with the input for your system -- i.e. you get a request to change the password of a user before they answered the secret question.

Also, don't trust any referrers except for your own domain! I know this is easy to spoof, but if you're working with forms, don't accept the input unless it's been referred from your page. Additionally, I pair form inputs with special keys that match some special value given in the form itself. This way, it is (almost) impossible for anyone to just make a plain old page and point the form to your script.

Last but not least, MySQL is more difficult to mess up than you think, but SQL injection still happens. Insert escape characters to every quote or special character in the query string before running it. It is absolutely critical that you do this. Finally, NEVER accept connections on MySQL from outside of your own host. Even if somebody was to get a hold of your MySQL password & username, then it would be impossible for them to connect to it from outside.

Link to comment
Share on other sites

  • 0

Thanks for your input all.

I ran phpinfo on my website, and it said about magic-quotes:

magic_quotes_gpc	On	On
magic_quotes_runtime	Off	Off
magic_quotes_sybase	Off	Off

Is it on then? :p (I'm useless when it comes to the server side of things).

I've heard about magic_quotes before, but not overly sure of what it does. Does it automatically add / in front of " ' in the right places on scripts, so if someone entered ' in a form, it wouldn't run it as PHP? Excuse me if im talking rubbish. I'm still learning, but learning quick now. I've learnt so much from making this support script.

I think I have quite a few things covered, but there are a few things that need sorting out. :)

Thanks all.

Link to comment
Share on other sites

  • 0
Thanks for your input all.

I ran phpinfo on my website, and it said about magic-quotes:

magic_quotes_gpc	On	On
magic_quotes_runtime	Off	Off
magic_quotes_sybase	Off	Off

Is it on then? :p (I'm useless when it comes to the server side of things).

I've heard about magic_quotes before, but not overly sure of what it does. Does it automatically add / in front of " ' in the right places on scripts, so if someone entered ' in a form, it wouldn't run it as PHP? Excuse me if im talking rubbish. I'm still learning, but learning quick now. I've learnt so much from making this support script.

I think I have quite a few things covered, but there are a few things that need sorting out. :)

Thanks all.

586727574[/snapback]

Yes, if magic_quotes_gpc is On, it is ok. :yes:

Anyway, whatever you do to protect your scripts and your database you must keep in mind that there aren't any 100% secure PHP scripts (and this is not only the case with PHP, but with ASP or any other language for dynamic content). Even in the most official and professional scripts, coded by the most experienced programmers, sometimes there are security holes discovered, several months after they become available.

No matter what you do to prevent any problems, you must always have a back-up plan and you must always be ready to restore everything from scratch, as soon as possible. A hack is something that every admin must be ready for.

If you can, set up a cron job that will back-up your database and files everyday, or several times a day (ie. every 6 hours if your site is very active), and it will save this back-up anywhere out of your public html directory.

Edited by nickg78
Link to comment
Share on other sites

  • 0

Yea, I know nothing is 100% secure. It is why it annoys me when people have a go at WinXP, and claim that OS X or Linux is 100% secure. lol

I'm wondering, if I have magic_quotes on, do I need to do that mysql_escape_string thing still? (Ive added it anyway).

I've just checked all my queries have quotes on anything fed from a form, and have got an intval on the $_GETs and $_POSTs that go to a MySQL query.

EDIT: How do you do cron jobs exactly? I've had a go at them before, but it never worked. I have options in my DirectAdmin panel to setup cron jobs...

Link to comment
Share on other sites

  • 0
Yea, I know nothing is 100% secure. It is why it annoys me when people have a go at WinXP, and claim that OS X or Linux is 100% secure. lol

586727765[/snapback]

Whos says this? I've never read an argument on any forum or newgroup where somebody made the 100% secure claim for any operating system, then again I tend to to hang out in on stupidosfanboys.com. The arguments are that OS X, Linux, BSD, etc. are more secure than Windows or?being a pessimist? all four are insecure but one is less secure than the others and therefor more likely to be compromised.

I suspect it has something to do with security not having bounds. You don't measure security on a scale from 1 to 10 and it's not a boolean property. Something isn't just "Secure" it's "secure against X to point Y". That's something we should all keep in mind when we write our applications: Security is line - it extends in either direction forever. We can do things that move our projects further to one side (making them more secure against certain things than others) or we can do things that move them in the other direction (making them less secure against any give attack than something else), but there is no magic line of code that seperates something that is secure from something that is not. The goal is always balancing functionality and interface with security considerations.

Link to comment
Share on other sites

  • 0

Another thing I just thought of, is how secure are sessions ($_SESSION), from being faked or otherwise?

I currently have a session set across my support script, so that in order for a user to gain access to view a ticket, reply to a ticket or otherwise, they have to go via the first page. On the first page, they have to supply the ticket ID, and their email. It then takes the ID and email, and checks that the email provided matches the ID given (so that people asking for support can have a bit of privacy with their tickets). Assuming all the checks are good, it sets a session as "user.xx" where xx is the ticket they requested. This then allows them to read and reply ONLY to the ticket they created. I suppose it would be easier to employ some kind of user/password system and use cookies, but I was experimenting with what I know...

Link to comment
Share on other sites

  • 0

im all so woundering how 2 make php scure :yes:

is there a way to do it so other people cant use there own forms to submit data to the submit page? :unsure:

Link to comment
Share on other sites

  • 0

Check the referer URL, if it's not an URL that belongs to your domain, discard all information and throw up an error. Else if the referer is from your domain, do what ever with it.

Link to comment
Share on other sites

  • 0

I would highly recommend NOT coding for magic quotes (one of the most inane things in PHP alongside register globals) and instead try to make your code more portable and thus more secure in the long run. Even if you're only playing about, it's best to get into good habits early. Believe me, I'm talking from experience.

What I do in all my scripts is run stripslashes on all globals and then run them through mysql_escape_string when needed. For example:

$raw_text = stripslashes( $_POST['text'] );

$clean_text = mysql_real_escape_string( $raw_text );

(in my code, I run stripslashes recursively on $_GET and $_POST. The above is just for example's sake)

This way, if ever you distribute your code, or move to a server where magic quotes isn't in, your code will be more secure.

Also it's not really necessary to put integers in queries inside quotes as long as they've been cast to int beforehand.

Link to comment
Share on other sites

  • 0
Check the referer URL, if it's not an URL that belongs to your domain, discard all information and throw up an error. Else if the referer is from your domain, do what ever with it.

586728104[/snapback]

how would i go about doing that? :whistle:

Link to comment
Share on other sites

  • 0
how would i go about doing that?  :whistle:

586728480[/snapback]

Referer headers are easily forged, and if anyone was to try and attack your site they'd know that, so it's kinda pointless.

Link to comment
Share on other sites

  • 0

Sessions are very difficult to forge and are therefore very secure. The whole point of a session is that a cookie is given to the user with a long random string as its value which corresponds to a file on your server containing the session's data. Thus, all that the user sees is a new cookie with that string -- or the end of PHP urls have ?PHP_SESSID=(longstring) or something like that tacked onto the end. All of this is handled automatically by PHP, and all you need to do is use start_session() at the beginning of your file. Sessions are automatically terminated when the user closes their browser. You can add an additional level of security to this by only allowing the IP address stored in the session data to access the session itself.

Link to comment
Share on other sites

  • 0
Referer headers are easily forged, and if anyone was to try and attack your site they'd know that, so it's kinda pointless.

586728526[/snapback]

That's very true, but it will still catch out some amateur hackers. By the time they've realised their mistake they could be banned.

soil: The $_SERVER['HTTP_REFERER'] variable should contain the referer info, but like winfx said; it can be forged. But it's still worth checking, just in case.

Link to comment
Share on other sites

  • 0
Sessions are very difficult to forge and are therefore very secure.  The whole point of a session is that a cookie is given to the user with a long random string as its value which corresponds to a file on your server containing the session's data.  Thus, all that the user sees is a new cookie with that string -- or the end of PHP urls have ?PHP_SESSID=(longstring) or something like that tacked onto the end.  All of this is handled automatically by PHP, and all you need to do is use start_session() at the beginning of your file.  Sessions are automatically terminated when the user closes their browser.  You can add an additional level of security to this by only allowing the IP address stored in the session data to access the session itself.

586728775[/snapback]

Ok, thanks for expalining that. Only one more question I can think of as I am thinking about continuing to code the entire system for my site, is...

How can i secure downloads in a folder, so they can only be downloaded after a purchase (in case you haven't gathered it already, Im going to be dealing in digital goods)?

(Rhetorically speaking...) I have a script, and a user adds the item to the cart, goes through checkout and via Paypal. Paypal then returns via IPN "Yup, it is paid for, they can have the file". The user then gets a page or an email with the download link in. Now I realise I would protect it slightly at this point, by having it load; "download.php?id=16" (or w/e). In that I would make it check that it is available for download etc, so someone can't just call that URL and pinch it. But how do I physically protect the files, so someone can't load "mysite.com/files/blah.zip" in their browser get the file?

Thanks for all your help again. :)

Link to comment
Share on other sites

  • 0

You can put the downloads in a folder that's not accesible via browser ej if your htdocs or www folder is in

/home/user/public_html, make a folder under user and have php read from that folder :)

Link to comment
Share on other sites

  • 0

The server should be properly configured.

  • Register_globals should be off. Global variables should never be used within the scripts and they should not be allowed at all. This reduces your attack surface.
  • Display_errors should be off. Hacks often use this information to help them hack scripts. Don't give hackers any information that helps them.
  • Log_errors should be on. You need to know where their are errors in your scripts and you need to learn how people are trying to attack your scripts.
  • Error_reporting should be set to E_All. All errors, beyond just uninitialized variables (though you can include this stuff as well if you're a perfectionist) and stuff, should be reported.
  • Allow_furl_open should be off. You, the coder, do not need this capability to grab files and nobody else needs it. Turn this off to reduce your attack surface.

If possible, try to store include() files above the root directory of the server. This will make it possible for your scripts to access the files but they will not be publically accessible. Essentially, reducing the attack surface.

Having a single file that handles security is a blessing. This allows security to be controlled from a single location which reduces the amount of work involved in patching security problems. This also allows the update to occur more smoothly because you do not need to replace several files. Having all of this business logic in a single location makes it easier to find security problems, or have other people search for security problems, as well.

Never rely on client-side validation. It is understandable that client-side validation is used to immediately report problems to the user so they can correct them before sending them to the server; however, you should not rely on client-side validation because it can be easily worked around. Client-side validation should be used for user convenience, not security. All information coming from the user should be validated by the server because the server cannot be worked around.

Make sure every PHP file has a .php extension (.php, .php3, .php4, etc.) Anything else is wrong (such as .inc) because it opens the door of possibility for others to view your code without authorization.

Never store or pass authentication information or security level using cookies, form submissions, or through URLs. When needing some kind of authentication system that remembers users, use cookies where the username is stored and an MD5 hash of the password (do not store the password itself!)

If your script uses some kind of modular system (index.php?action=whatever has index.php include() "whatever.php") then steps need to be taken to ensure that people are not trying to access the modules directly. You could define() a variable inside index.php and then have the module check for this defined term and exit() or something similar if this term does not exist. If someone tries to access the module itself instead of the correct way then they will be stopped because the module will know it is not being used properly.

Do not rely on referrer strings but do take advantage of them where you can. You should use referrer strings to figure out the page the user is coming from - this does not make sense for publically viewable sections of a web site but administration control panels can benefit greatly from such information. The referrer string can be easily forged and the protection easily bypassed by the experienced hacker but this should be enough to stop many amateur hackers and is a welcomed layer of additional security. Do not turn away people who have empty/nonexistant referrer strings though because some browser software and browsing devices allow the user to refuse sending this information for increased privacy (such as the Opera web browser, for example.)

All input should be filtered. Input includes $_GET, $_POST, $_COOKIE, GPC arrays, file content (for those being uploaded or submitted by the end user), database data, XML files, RSS files, web services data, etc. You should ensure that people are not trying to pass data that you would not expect that could potentially alter the way your script behaves. For example, you would not want people trying to send HTML-related information through $_GET, $_POST, or $_COOKIE so you'll want to have the input filtered using the htmlentities() function. If you are expecting particular variables to be an integer (only containing numeric characters) then you can use the intval() function. If you are expecting variables to have particular values (?name=john, ?name=jack, ?name=jill, but nothing else) then you would want to use the switch() function. There is a lot more that can be said here, I will try to clarify further if wanted.

All output should be escaped. Output includes information being sent to an external system such as the user's browser, the database, XML/RSS/Web services output, generated PDF and image files, etc. You should ensure that people are not trying to pass data that will interfere with other users of the script, are not trying to bypass database authentication, etc. If you are outputting data to the user's browser then you should use htmlentities() so that people cannot try to do things like <script>window.alert("XSS Attack!");</script> and cause users problems. If information is being sent to a Linux/Unix shell then escapeshellcmd() should be used for shell commands and escapeshellarg() should be used for shell command arguments. If information is beint sent to a database then you should use the database-specific function to escape the output (mysql_real_escape_string() for mySQL, mysqli_real_escape_string() if you're using the MySQL Improved extension, pg_escape_string() for postgreSQL, etc.)

-----

That's all that I can think of right now. If you have any questions then feel free to ask :)

Link to comment
Share on other sites

  • 0

Thanks for the very detailed advice megamanXplosion. :) I think I am almost ready to start coding my "secure as possible" shopping cart system. :)

Link to comment
Share on other sites

  • 0
Ok, thanks for expalining that. Only one more question I can think of as I am thinking about continuing to code the entire system for my site, is...

How can i secure downloads in a folder, so they can only be downloaded after a purchase (in case you haven't gathered it already, Im going to be dealing in digital goods)?

(Rhetorically speaking...) I have a script, and a user adds the item to the cart, goes through checkout and via Paypal. Paypal then returns via IPN "Yup, it is paid for, they can have the file". The user then gets a page or an email with the download link in. Now I realise I would protect it slightly at this point, by having it load; "download.php?id=16" (or w/e). In that I would make it check that it is available for download etc, so someone can't just call that URL and pinch it. But how do I physically protect the files, so someone can't load "mysite.com/files/blah.zip" in their browser get the file?

Thanks for all your help again. :)

586729171[/snapback]

id store the id in a database, and send them an email with a verification code. They go to verify it, enter the id in email and verification code, if the id and veri code match whats in the database, give them a dl link. Store the download outside of the pubic folder, and use php to let them get the file. Only give them something like 30 mins to download the file, again do this via a timestamp stored in a database, and compare with current time. Thats what i would do anyhow

Link to comment
Share on other sites

  • 0
Make sure every PHP file has a .php extension (.php, .php3, .php4, etc.) Anything else is wrong (such as .inc) because it opens the door of possibility for others to view your code without authorization.

Not necessarily the best thing to do. It would be better to change the file extension to something else, like .py or something. It still won't be viewable from the browser, but hackers will see the .py extension and assume the site is written in python. This page of the PHP manual explains in a bit more detail on how to "create" your own file extensions, that will be parsed as PHP (won't be readable in plain text) but will throw a hacker off the scent a bit.

Link to comment
Share on other sites

  • 0

I agree. I never run PHP files as, well, PHP files anymore. Their extensions rely heavily on the project that I'm working on; usually I find a three-letter acronym that I can use to summarize the site. I mean, Ars Technica uses the ".ars" file format which they made up. You can easily add more file formats to PHP and additionally you can also totally remove file formats from your files completely to do something Google-ish like www.example.com/go/here where "go" is your PHP file.

Link to comment
Share on other sites

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.