This is a broad topic, one that is the subject of innumerable mailing lists and that has spawned innumerable consulting firms. I will hardly be able to treat the topic thoroughly in this article. I can, however, give some advice that can knock out the most common and devastating vulnerabilities in web applications. This short list covers some of the biggest non-SQL related vulnerabilities. SQL vulnerabilities and attack prevention are very important, as well, and are a topic for a future article.
Solution: Validate your data! Strip all HTML tags from your user input. If you must, allow a well-defined subset of harmless HTML tags (text formatting only, like bold, italics, etc.), and strip the rest.
Cross-Site Request Forgeries (CSRF) or "session riding" - Attackers can fool your unwitting users who have logged in to your application to execute arbitrary site functionality. For instance, say someone has logged into your web forum. Later, he receives a malicious email containing a link like this:
<a href="http://yourwebforum.tld/post.php?body=I'm%20a%20loser!">Check out this funny picture!</a>
The user wouldn't have to click on the above example--only open the email--for the request to be made. In this example, a post could be made using a user's credentials without his knowledge.
Solution: There are several easy things you can do to mitigate the chance that an attack like this would be successful, like using POST instead of GET or making user credentials expire quickly. However, to thoroughly prevent this type of attack, the solution requires even more. It helps to associate a unique key with each form submission, a key that the attacker could never know. The key could be generated randomly for the form when it is displayed to the user and saved to match to the future POST when the form is submitted back to the server.
"Session hijacking" - An attacker can discover the ID of a user's session and send the session ID in the URL or in the HTTP headers as his own, thereby "hijacking" the user's credentials. This gives any access your user had to the attacker.
Solution: You can try associating the user's IP address with the session, which you would check against the client's IP every time a request were made. This, though, can knock out valid users whose ISPs use load-balancing proxies for internet traffic; their IP addresses may change with every request. It could also allow an attacker behind the same router as the user to hijack a session undetected. Instead, you can set a cookie on the client's machine that contains nothing important or private, say, a unique key generated when the session is created. This key is also saved in the session, and is checked against the cookie at every request.
Lack of URL access restriction - Sometimes a web application will only authenticate a user when trying to access an entry point to a restricted portion of the site. For instance, if a user tries to view his personal dashboard, his credentials are checked and he is redirected to a login page before he can continue. However, if this user were to type in a direct URL to a page other than the dashboard, he would bypass the login procedure altogether. This is a glaring oversight in many web applications. All an attacker has to discover is the the URLs to non-entry pages, to which he would have full access.
Solution: Validate credentials on every page that should have restricted access.
A good rule of thumb when it comes to web application security: Don't ever assume that an attacker won't try or won't figure out how. Ideally, you should be comfortable allowing an attacker to see your source code. It should be apparent that your security is so air-tight that he is forced to play by the rules.
Of course, there are many other types of attacks, and, as I mentioned, I will do another article on SQL vulnerabilities and exploits, which are also very important. For more information on the topics in this article, here are some great resources: