A new version of my white paper entitled "Best Practices for a Secure Forgot Password Feature" is available. You can download the white paper here. No significant changes were made in terms of content, but it does have fewer pages and a more pleasing look now. The link I had given out previously is no longer valid.
The white paper was used as the basis for the OWASP Forgot Password Cheat Sheet.
Saturday, September 11, 2010
Wednesday, September 8, 2010
Password Complexity Rules
A Consumer Reports blogger has been taking Facebook to task for allowing certain dictionary words to be used as passwords (he found 20 of them). I can't confirm that Facebook actually forbids the use of dictionary words like the blogger claims. The signup page did not reject the word "animal" when I tried it, and their password strength FAQ does not state that dictionary words are banned.
The flak got me to thinking about password complexity rules in general. It needs to be evaluated when assessing the security posture of a web application. I see huge variety in the password rules that are being used. That's not the problem. It makes sense that highly sensitive applications, such as financial or governmental, should enforce stricter requirements. The problem I see is that the password rules simply aren't strong enough, ever. Identifying this weakness calls for a manual test or a code review. It is not something a web app scanner like WebInspect or AppScan would flag. Maybe that's part of the problem.
Below are the password rules I normally recommend for Internet-facing web applications.
Please let me know if you have any other suggestions on this subject!
The flak got me to thinking about password complexity rules in general. It needs to be evaluated when assessing the security posture of a web application. I see huge variety in the password rules that are being used. That's not the problem. It makes sense that highly sensitive applications, such as financial or governmental, should enforce stricter requirements. The problem I see is that the password rules simply aren't strong enough, ever. Identifying this weakness calls for a manual test or a code review. It is not something a web app scanner like WebInspect or AppScan would flag. Maybe that's part of the problem.
Below are the password rules I normally recommend for Internet-facing web applications.
- Minimum password length of 7
- Allow passwords to be 50 characters or more in length
- Require at least one uppercase letter
- Require at least one lowercase letter
- Require at least one number
- Allow special characters
- Do not allow any part of username to appear in the password
- Do not allow the user's first or last name to appear in the password
- Do not allow any form of the word “password”
- Do not allow the same character three or more times in succession
Please let me know if you have any other suggestions on this subject!
Friday, May 28, 2010
Bug Editing Cookies in Web Developer Extension
Just a quick note I've been meaning to post about a small bug in Chris Pederick's Firefox Web Developer extension. It is a fantastic extension and I use it all the time, especially for working with cookies. Kudos to Mr. Pederick!
Unfortunately, what I noticed is that if you have "accept third-party cookies" unchecked in Firefox's privacy options, you can't edit cookies or add cookies. In fact it ends up deleting the cookies that you try to edit. D'oh!
This bug bit me a few times when I was trying to demonstrate session hijacking while teaching an application security class. This bug is a known issue and I'm hoping Chris can find a way to make it work in the next version. In the meantime, I will keep using the extension and trying to remember to enable third-party cookies whenever I teach the class.
Unfortunately, what I noticed is that if you have "accept third-party cookies" unchecked in Firefox's privacy options, you can't edit cookies or add cookies. In fact it ends up deleting the cookies that you try to edit. D'oh!
This bug bit me a few times when I was trying to demonstrate session hijacking while teaching an application security class. This bug is a known issue and I'm hoping Chris can find a way to make it work in the next version. In the meantime, I will keep using the extension and trying to remember to enable third-party cookies whenever I teach the class.
Monday, May 17, 2010
A Case for HttpOnly
Last month the Apache Infrastructure Team fell victim to a common web application attack. Rather than keep the information secret, they were kind enough to explain how the attack occurred. The initial attack leveraged cross-site scripting to steal users' session IDs. This is something that could have been prevented if the web app's session cookie had been marked with the HttpOnly attribute. When a web app sets a cookie, the presence of HttpOnly instructs browsers to disallow client-side script from accessing the cookie.
You will sometimes hear or read that HttpOnly helps prevent XSS attacks. That is not quite true. It helps prevent session hijacking. Specifically, it helps guard against one attack vector, namely where session cookies are stolen via XSS. HttpOnly can be used for any sensitive cookie that you don't want falling into the hands of fraudsters. (I should use "fraudster" more often - it is a fun word)
There is one caveat to using HttpOnly. It might break your application if it was written in such a way that the functionality depends on JavaScript having access to the cookie. That is fairly uncommon however.
You will sometimes hear or read that HttpOnly helps prevent XSS attacks. That is not quite true. It helps prevent session hijacking. Specifically, it helps guard against one attack vector, namely where session cookies are stolen via XSS. HttpOnly can be used for any sensitive cookie that you don't want falling into the hands of fraudsters. (I should use "fraudster" more often - it is a fun word)
There is one caveat to using HttpOnly. It might break your application if it was written in such a way that the functionality depends on JavaScript having access to the cookie. That is fairly uncommon however.
Wednesday, May 12, 2010
Dynamically-Generated PDFs
Many web applications will generate PDF files for users so they can view nicely-formatted statements, reports, etc. These dynamically-generated PDFs are typically accessed by users in two different ways.
1) The application creates an actual PDF file on the server and redirects the user's browser to the file (via 302 response code).
2) The application streams the bytes for the PDF directly to the user's browser as part of the HTTP response.
This second method is much more secure. The reason is that a PDF file sitting on a server can probably be accessed by anyone. The only information needed is the directory and the file name. That said, I have seen the first method implemented securely, but two important mitigating factors were employed. First, the application used randomized file names. A globally-unique identifier (GUID) is great for this. Second, the files were deleted within about 10 seconds of them being created. These two factors, when combined, made it almost impossible to gain access to another user's PDF file.
Testing for this vulnerability is simple. If you run across an application that allows you to download a dynamic PDF, make note of the URL when viewing the PDF. Is it a direct link to the PDF file? If so, copy the link, open a different browser (not another window of the same browser), and paste the URL. Is the PDF loaded? If so, then you have a problem, especially if the file name is guessable or follows a pattern. You might also check to see if the PDF is deleted from the server after a time.
This topic applies to other static files that your application might generate dynamically too, such as Excel files. PDFs just seem to be the most common.
1) The application creates an actual PDF file on the server and redirects the user's browser to the file (via 302 response code).
2) The application streams the bytes for the PDF directly to the user's browser as part of the HTTP response.
This second method is much more secure. The reason is that a PDF file sitting on a server can probably be accessed by anyone. The only information needed is the directory and the file name. That said, I have seen the first method implemented securely, but two important mitigating factors were employed. First, the application used randomized file names. A globally-unique identifier (GUID) is great for this. Second, the files were deleted within about 10 seconds of them being created. These two factors, when combined, made it almost impossible to gain access to another user's PDF file.
Testing for this vulnerability is simple. If you run across an application that allows you to download a dynamic PDF, make note of the URL when viewing the PDF. Is it a direct link to the PDF file? If so, copy the link, open a different browser (not another window of the same browser), and paste the URL. Is the PDF loaded? If so, then you have a problem, especially if the file name is guessable or follows a pattern. You might also check to see if the PDF is deleted from the server after a time.
This topic applies to other static files that your application might generate dynamically too, such as Excel files. PDFs just seem to be the most common.
Monday, April 19, 2010
Authentication Flaw
During an assessment of a financial web application, I found a serious flaw in authentication. The web security scanners at my disposal gave a "seal of approval" on the unauthenticated surface area of the application. Yet, in a matter of a few hours after getting the go-ahead to test the application, I had fully compromised a user's account. How? Basically, by using my brain as a hacker might. Screen shots proving my authenticated access were probably eye-opening to the customer as they had given me only the URL for the site, not any user credentials.
This was a case of flawed forgot password functionality. For users who had forgotten their password, the application worked as follows:
Step 1 - Enter username
Step 2 - Answer question to personal security question and enter email address
Step 3 - Receive email with a new, system-generated password
Can you find the flaws? First, the application asked for only one piece of data initially: the username. It is not hard to guess a valid username. I found that "jsmith" was a valid username. Second, the application asked the user to enter his email address on the second step. Um, shouldn't this be part of the user's profile and already known to the application? Why trust a value entered by the user? Third, which I didn't actually tell you, is that the application allowed an unlimited number of attempts to answer the personal security question.
It happened to be that jsmith's security question was "What was the name of your first pet?". I fired up Burp Intruder with the 100 most common dog and cat names. Very soon I received an email with jsmith's newly reset password. It turned out that his first pet was named "Rocky" (I'm assuming jsmith is a man here - would a girl naming her pet "Rocky"?).
This is an example where a scanner (and a web app firewall most likely) would not alert you to this security hole. All requests, data, and URLs were perfectly legitimate in my attack. The flaws were caused by poor design decisions.
This was a case of flawed forgot password functionality. For users who had forgotten their password, the application worked as follows:
Step 1 - Enter username
Step 2 - Answer question to personal security question and enter email address
Step 3 - Receive email with a new, system-generated password
Can you find the flaws? First, the application asked for only one piece of data initially: the username. It is not hard to guess a valid username. I found that "jsmith" was a valid username. Second, the application asked the user to enter his email address on the second step. Um, shouldn't this be part of the user's profile and already known to the application? Why trust a value entered by the user? Third, which I didn't actually tell you, is that the application allowed an unlimited number of attempts to answer the personal security question.
It happened to be that jsmith's security question was "What was the name of your first pet?". I fired up Burp Intruder with the 100 most common dog and cat names. Very soon I received an email with jsmith's newly reset password. It turned out that his first pet was named "Rocky" (I'm assuming jsmith is a man here - would a girl naming her pet "Rocky"?).
This is an example where a scanner (and a web app firewall most likely) would not alert you to this security hole. All requests, data, and URLs were perfectly legitimate in my attack. The flaws were caused by poor design decisions.
Wednesday, March 3, 2010
Don't Need No Stinkin' Coupon Print Activator
This is a real-life example of exploiting a web application security flaw. I received an email from Discover Card offering some Lowe's coupons.
I was needing to buy some stuff there anyway, so I decided to grab them.
I was soon shocked and amazed. Turns out they expect you to download an executable (something called a"Coupon Print Activator") and run it just to print the coupons.
I am not in the habit of running strange .exe's.
But, I really wanted those coupons (and perhaps sensed my hacking skillz were being challenged). I looked at the requests being made to the server, and noticed that dsppreprint.cfm had some JavaScript pointing to interesting URLs, one to "print.cfm" and one to "print_noplugin_redirect.cfm". The query strings were radically different between the two, so I decided to append the query string from print.cfm onto the other .cfm file.
This hybrid URL ended up in a round-about way returning some HTML with an "embed" tag with a bunch of attributes. One of the attributes was very interesting to me.
A request to this URL returned some raw data that appeared to be meant for consumption by the Coupon Print Activator. It also led me to discover yet another URL.
A request to this URL returned the following jpeg image (numbers masked to protect something or other):
So I got a bar code. Wonder if I could print this bar code and use it at the self-checkout at Lowe's? This would not be unethical as I was entitled to the coupon anyway. I just didn't want ro run that Activator thingy! As a simple test, I jumped over to Lowes.com and added a ceiling fan to my shopping cart. There was an input field for "Promotional Code".
Proceeding to enter the number that appeared below my bar code, I was pleased to see my $10.00 discount applied.
To sum up, coupon obtained, Activator thingy bypassed. Sorry I do not have time get into how this site could have been written more securely. Suffice it to say that exposing data, URLs, and client side logic is not good.
I was needing to buy some stuff there anyway, so I decided to grab them.
I was soon shocked and amazed. Turns out they expect you to download an executable (something called a"Coupon Print Activator") and run it just to print the coupons.
I am not in the habit of running strange .exe's.
But, I really wanted those coupons (and perhaps sensed my hacking skillz were being challenged). I looked at the requests being made to the server, and noticed that dsppreprint.cfm had some JavaScript pointing to interesting URLs, one to "print.cfm" and one to "print_noplugin_redirect.cfm". The query strings were radically different between the two, so I decided to append the query string from print.cfm onto the other .cfm file.
This hybrid URL ended up in a round-about way returning some HTML with an "embed" tag with a bunch of attributes. One of the attributes was very interesting to me.
A request to this URL returned some raw data that appeared to be meant for consumption by the Coupon Print Activator. It also led me to discover yet another URL.
A request to this URL returned the following jpeg image (numbers masked to protect something or other):
So I got a bar code. Wonder if I could print this bar code and use it at the self-checkout at Lowe's? This would not be unethical as I was entitled to the coupon anyway. I just didn't want ro run that Activator thingy! As a simple test, I jumped over to Lowes.com and added a ceiling fan to my shopping cart. There was an input field for "Promotional Code".
Proceeding to enter the number that appeared below my bar code, I was pleased to see my $10.00 discount applied.
To sum up, coupon obtained, Activator thingy bypassed. Sorry I do not have time get into how this site could have been written more securely. Suffice it to say that exposing data, URLs, and client side logic is not good.
Thursday, January 21, 2010
Counting Lines of Code
Want some interesting metrics about your code? A nice little Windows utility called LOCmetrics could be just what you are looking for. When preparing for an application source code review, I usually need an estimate for the number of lines of code involved. I've found the numbers provided by development teams can vary significantly from what we actually see when we get the code. That is why I am going to start recommending they run LocMetrics.
LocMetrics has a simple GUI and works with C#, C++, Java, and SQL code. Run LocMetrics and it will dump out physical LOC, executable-physical LOC, and executable-logical LOC. Plus, you get bonus information like number of comment lines, blank lines, cyclomatic complexity (aka McCabe VG complexity) and LOC counts broken down by directory. The most important results are shown in the GUI itself and you get a HTML file containing more detail.
Here's a screen shot of the GUI:
I ran LocMetrics on a few Java libraries for comparison sake. Here are the results.
OWASP AntiSamy 1.3
Lines of Code = 4,576
Executable physical LOC = 1,972Executable logical LOC = 1,444
Cyclomatic complexity = 259
OWASP ESAPI 1.4.2
Lines of Code = 21,263
Executable physical LOC = 8,926
Executable logical LOC = 6,172
Cyclomatic complexity = 1,547
JSE 1.6.0_17 (java.* package only)
Lines of Code = 556,018Executable physical LOC = 200,567
Executable logical LOC = 136,076Cyclomatic complexity = 38,106
LocMetrics has a simple GUI and works with C#, C++, Java, and SQL code. Run LocMetrics and it will dump out physical LOC, executable-physical LOC, and executable-logical LOC. Plus, you get bonus information like number of comment lines, blank lines, cyclomatic complexity (aka McCabe VG complexity) and LOC counts broken down by directory. The most important results are shown in the GUI itself and you get a HTML file containing more detail.
Here's a screen shot of the GUI:
I ran LocMetrics on a few Java libraries for comparison sake. Here are the results.
OWASP AntiSamy 1.3
Lines of Code = 4,576
Executable physical LOC = 1,972Executable logical LOC = 1,444
Cyclomatic complexity = 259
OWASP ESAPI 1.4.2
Lines of Code = 21,263
Executable physical LOC = 8,926
Executable logical LOC = 6,172
Cyclomatic complexity = 1,547
JSE 1.6.0_17 (java.* package only)
Lines of Code = 556,018Executable physical LOC = 200,567
Executable logical LOC = 136,076Cyclomatic complexity = 38,106
Subscribe to:
Posts (Atom)