GET and POST data is data directly sent from the user. You get it raw, with no checks or validation between the user and your program. Even if you were to validate the form that should originate the data, an attacker could manually craft a request with whatever data he wants. So you must always treat request data as untrusted user input.
There are a number of attacks that rely on the coder forgetting that request data is untrustworthy, but the most well-known is SQL injection. The root cause of SQL injection is building a query by manually concatenating strings, some of which are untrusted user input. This means that you are telling your database to execute untrusted user input.
The naive solution to SQL injection is to validate the inputs and then concatenate them into a query string, but this is poor form as well. You are relying on your validation logic to make the string safe, and if you misuse it -- or the logic is buggy -- then you are once again exposed to attacks.
The correct solution is to separate your query from the data it contains. Virtually all database adapters support this approach, and if yours doesn't for some reason, it's not fit for use. The most common idiom is (in no particular language):
myDB.query("select * from Stuff where id=?", [42]);
This will guarantee (in such a system) that the parameters are not executed. The query string is built from entirely trusted data, while the untrusted data is segregated. At worst, this approach applied to improper input can result in incorrect data, not an incorrect command.
This approach to avoiding SQL injection highlights the central principle that applies to all kinds of request data attacks: the request data is not yours and it's not safe. When handling any user input, including request data, always assume that it's originating from an attacker with intimate knowledge of your system. It may seem paranoid, but it keeps you safe.