Occasionally, someone who is learning about security will ask insightful questions like “how long should passwords be?” or “how long should it be before a user is logged out for inactivity?” The correct answer, of course, is “it depends” followed by a smoke bomb and a disappearing act. If, however, you are cornered with a question like this, I think there are some reasonable guidelines everyone can agree on.
There are things you can get 100% right (like eliminating cross-site scripting) and there are some things that you get for free (like using a strong session ID token from a framework). But, there can be big tradeoffs in performance or usability for certain security mechanisms. The appropriate tradeoff varies with the risk profile of the site. Let’s take a look at a few example security requirements, viewed through the light of two site risk profiles.
Weak: Something with no financial or private data. Forums, wikis, twitter, etc.
Strong: Anything with financial or private data. Banks, webmail, web hosting accounts, etc.
Is this too coarse to describe every site in the world? Yes, but it’s a start.
Use of SSL
Weak: SSL is optional for everything but the login page (and the form it’s served on).
Strong: HTTP redirects to HTTPS. SSL only.
Account Lockout
Weak: Either no brute force detection or basic request rate throttling.
Strong: Time-based lockout with increasing durations (5 minutes after 5 bad attempts, 10 minutes after 10 bad attempts, 20 minutes after 15 bad attempts, etc).
Session Timeout
Weak: A few weeks. Seriously! Look at GMail.
Strong: After ten minutes of inactivity, and no longer than a few hours, even with activity.
Password Strength
Weak: Six characters, and your password cannot be “password” (seriously)
Strong: At least eight characters, at least three character sets (upper, lower, numeric, special)
Password Reset
Weak: Email-based reset.
Strong: Email plus secret question/answer based reset.
Page Caching
Weak: All pages can be locally cached
Strong: No pages are locally cached
Username Enumeration
Weak: After a failed login, tell the user if the username they entered is valid.
Strong: After a failed login (or successful password reset, etc), do not disclose if the username the use user entered is valid.
Your requirements may vary, but I think this covers some of the common policy-based ones. Wouldn’t it be a happier web application security world if everyone followed these guidelines?
I’ve recently been accepted to the Masters program at the iSchool at UC Berkeley. I'm not starting until the Fall, but I’m already experiencing some life changes. I’ve moved out of my house in Long Beach, and I’m currently staying with friends and family throughout Southern California. I’m stopping work at Foundstone on July 1st, and moving up to Berkeley sometime in August. Hopefully, that will give me a little summer vacation to relax while I prepare to go back to school.
I completed my undergraduate work at Berkeley, so this will be a homecoming. I visited the iSchool during the open house and my expectations were exceeded by the students, both prospective and current, and the staff. It’s a “post-disciplinary” program that is centered on information and technology, but branches into fields like psychology, security, usability, economics, business, and sociology. I am not set on what I want to study and research, but I know that I’ll have plenty of inspiration from like-minded peers – check out the cool final projects from this year. The school itself is located in South Hall, the oldest building in the UC system.
I’ll continue to keep this blog updated. I hope to find some time to increase the content and improve the functionality of the site, as well as work on my other projects during the break. To be continued!
Update:
Arshan Dabirsiaghi noted that if you use cmd.exe, /bin/sh, or any other equivalently chainable executable loader, you can still introduce command injection vulnerabilities in .NET in Java. See the Webgoat lesson for an example of this.
Rudolph Araujo pointed out an interesting security note on MSDN about the CreateProcess function (which is called by Process.Start()) regarding the fact that unintended executables may be executed if there is whitespace in the path.
Dan Cornell made two really cool test projects for Java and .NET. Both of them demonstrated that a healthy set of shell meta-characters are ineffective at introducing new commands into system calls in Java and .NET.
System calls should probably be avoided in any case, since the executables aren't constrained by the security features of managed languages. However, it certainly seems like this threat is pretty well mitigated in these platforms, at least until someone finds a vulnerability.
Original:
Command injection (also called shell injection) can be a huge problem. Recently, I was working on an old Perl/CGI system that allowed command injection through the system command that ultimately led to a reverse shell on the web server.
I was trying to develop some examples of command injection in .NET, using the Process.Start method. The Process.Start method takes a file and arguments as parameters. You can't specify additional commands through meta-characters like semi-colons (;) and pipes (|). Java seems to be similarly invulnerable - according to this OWASP page:
There are many sites that will tell you that Java's Runtime.exec is exactly the same as C's system function. This is not true. Both allow you to invoke a new program/process. However, C's system function passes its arguments to the shell (/bin/sh) to be parsed, whereas Runtime.exec tries to split the string into an array of words, then executes the first word in the array with the rest of the words as parameters. Runtime.exec does NOT try to invoke the shell at any point. The key difference is that much of the functionality provided by the shell that could be used for mischief (chaining commands using "&", "&&", "|", "||", etc, redirecting input and output) would simply end up as a parameter being passed to the first command, and likely causing a syntax error, or being thrown out as an invalid parameter.
Obviously, if you let the user specify the first argument (the file name) then you are in trouble, but that's not really injection. Additionally, if you get into native/unmanaged code in your app you lose any protection. Those caveats aside, are Java and .NET impervious to command injection?
URL shortening is definitely on the "hot this spring" list - the list of services is growing, there's news of a $2M funding, and all the cool content-constrained tweets and Facebook status updates are sporting bit.ly, is.gd, and ewerl.com links. It seems like a simple enough service, but what about the security implications? A quick Google search finds a discussion of drive-by attacks and phishing. It's true that we don't know the domain of a tiny URL, and may get sent to something decidedly unfriendly. Many of the URL shortening sites have implemented previews to help out here.
What about cross-site scripting? I know that I suggested using tinyurl for a reflected XSS attack demo a while ago. As a simple proof of concept, here's a reflected XSS vulnerability on xssed.com and the payload for both is.gd and bit.ly. Are there any URL shortening services out that incorporate a NoScript-style anti-XSS approach?
What about cross-site request forgery? Bit.ly allows you to preview the full URL if you download their Firefox add-on, which may help you avoid obvious attacks.
Maybe a Site Advisor-like blacklist to prevent links to known bad guy sites?
Does anyone care? Do I need to write my own secure URL shortening service so I can raise $3M in funding and buy a really long foosball table for the break room?
When you're dealing with users in a web environment, invariably you'll want to know who's who. Because HTTP is stateless, web applications expect some random and difficult-to-guess piece of data with each request that's unique for each user. In most modern web frameworks, this data is stored by the browser as a cookie. This correlates all requests from the same browser with a server-side session, which can be used to stash data relevant to only that user. Let's call these session mechanisms: server-side caches that are mapped through a session identifier. Authentication is handled by checking if the session identifier in the request has been used previously to submit a valid username/password. There is another approach - persistent authentication - which doesn't use a server side cache. Instead of a session identifier, it expects an authentication token. This authentication token is parsed and validated for each request. The value, while it may be difficult to guess, is not random - it's based on the user's identity.
Think of HTTP basic authentication as a canonical example of persistent authentication. The authentication token is a username and password which are Base64 encoded and sent in a request header. This is basically like sending a password with each request - an attacker can simply replay a captured value, forever.
There are a host of homegrown web applications that pass some form of credentials in each request, rather than a temporary, random session identifier. The tell-tale sign is a cookie or hidden field that is the same for each user each time they log in. I've seen this design flaw a few times recently, and it's difficult to patch because it gets so baked into the application code. Here are some of the potential problems with a persistent authentication mechanism:
1) You can't implement server-side logouts effectively, because the token will always be valid.
2) You can't implement server-side session timeouts. Once the persistent authentication token is known, it's good forever.
3) You can't lockout brute force attacks against the persistent authentication token.
4) If someone can reverse-engineer the method for creating an authentication token, you are hosed.
These flaws are a package deal and there's no good way to fix them without changing the entire authentication design. That being said, persistent authentication mechanisms can be more resilient in some cases:
1) Session fixation attacks are less likely because the token is only available to authenticated users.
2) Cross-site request forgery attacks can be made more difficult in some cases. See how the Twitter API uses HTTP basic authentication and prevents CSRF attacks.
Forms authentication in ASP.NET uses a forms authentication ticket - it's a signed and encrypted username, with some extra things thrown in. Forms authentication tickets are not exactly persistent because they time out, but they suffer from one of the same issues - you can't easily invalidate a forms authentication ticket at the server. You can only clear the client-side cookie. Either you have to wait for it to expire or keep a record of invalid tokens. This was described in a Foundstone whitepaper.
Neither architectural mechanism is perfectly designed. However, session mechanisms tend to be more commonly built into frameworks, and thus more vetted, so any sign of persistent authentication should raise a red flag.
More Posts
Next page »