I am a web developer, and have been trying to learn a bit about security, and the thing that seems to be the biggest principal is just to trust the client with as little as possible. Is this a correct idea? What situations would the rule not cover/be incorrect for?
"Don't trust the client" means that whatever input comes from the client might have been sent maliciously, because you can't control it. So, for example, this means that authentication and authorization must be checked and enforced server-side. If you let the client validate anything and then tell you "ok", a malicious user could just skip the validation and send "ok" to the server anyway. In general this also means that any kind of input validation must be enforced server-side.
If you don't trust the client, you are basically protecting against all the vulnerabilities that are related to input validation. But that isn't enough of course. There are other types of vulnerabilities. For example, there's output sanitization, there's the confused deputy problem that might lead to CSRF (Cross Site Request Forgery) or clickjacking, there are issues related to wrong authentication schemes, misconfigurations, etc. Even timing attacks might be a problem in some cases. As you can see, not every security problem can be solved by input validation.
However it is not recommended to rely on Client Side validation as that can be bypassed by a User with use of a Web Proxy. The traffic is also prone to MITM and can be tampered by someone else other than User.
As you have asked when you can relax this rule knowingly accepting the risk, it can be when the communication is happening with a trusted third-party. You can check for ORIGIN and DOMAIN however to be safe.
One situation can be when you are using 2-way SSL handshake to authenticate, are keeping the audit logs, the web service treat inputs as text not commands and all communication is happening over dedicated/secured channel, you can relax a bit.
As already said, the idea behind "don't trust the client" is that the client can be sending anything. It is at the mercy of the user, who could be an attacker, and therefore must be considered a potential attacker itself.
This is paramount in web development, but applies almost equally well to any other client/server architecture. (It's just easier to exploit by poking at a web page than by reverse engineering or decompiling and modifying or reimplementing a desktop application.)
Let's take a simple example:
Sitting at a sunny beach drinking your beverage of choice, you are implementing a web page where the user can type in a text length, and a text, and the web server responds by sending back however many characters of that text that the client specifies. You also put some validation on the web page to make sure that the user doesn't ask the server to send back a longer text than the one they entered, that the user doesn't provide a negative length, and a few other sanity checks.
So if I send "5" and "Hello World", I get back "Hello". If I send "8" and "thunderstorm", I get back "thunders". If I send "0" and anything, I get an empty string back. If I try sending "-2" and anything, the page errors out and no request is sent. And so on.
That sounds safe and simple, doesn't it? And as long as everyone is playing by the rules, there's no problem.
However, attackers typically don't play by the rules. It's a dark, stormy night when Carol comes across your web page, and starts playing with it. By making a slight tweak to the client-side validation script, or by calling the web service directly, or by some other sleight of client-side hand, she is able to send requests that ask the server to send back more data than she sends to it. So she might send "20" and "Cookie". Now what happens?
As long as you're keeping the user input separate from everything else, your typical managed language either won't allow this, or will handle it relatively gracefully. But if you're combining the data provided by the client with other data, or if you're using a lower-level language, we've basically just reinvented the HEARTBLEED attack on OpenSSL. The fix to this particular example is simple: just add a bounds check on the server, so that the client can't ask for more data than it itself sent, no matter what shenanigans the client is up to.
This can be generalized to any other case as well. Does your system require that time periods entered do not overlap? It'll probably do something weird if they do overlap, unless you have a check for it. Does it require that a monetary amount is positive and greater than zero? I'll bet dollars to doughnuts that something unplanned-for will happen if these conditions are not met and you don't make sure they are. Does it require that links point at valid Internet URLs (no
<a href="script:alert('boo!')">try it!</a> allowed)? You had better make sure of that server side, because nothing forces the client that the user is in control of to reject such input from the user. And so on and so forth.
As a general principle, any client-side validation should be considered a convenience for the user, and any value supplied by the client should be considered at least potentially untrustworthy. Consequently, you should always plan for, and cater to, the case where the client-side validation has not executed, or when the client is playing games with the server.
That's what "don't trust the client" is all about.
The only situation I can see where this wouldn't apply is where either the server is hugely underpowered compared to the client (so re-running the validation would be prohibitively expensive), or where you just don't care. I don't see either of those applying in anything but extremely narrow circumstances; and if they do apply, you're likely to know.
Trusting the client as little as possible is definitely a correct principle, and I cannot imagine any time where this would not be the case and could be ignored. Glancing at the OWASP Top 10 2017, at least items 1, 4, 5, 7, and 8 could potentially be impacted by trusting the client too much. While I don't like speaking in absolutes when it comes to InfoSec, I can't say there would be many scenarios where not trusting the client is an incorrect practice.
Here are some examples to reinforce why the user should not be trusted:
Let's say you are writing a payment processor, specifically functions to send money. The back end code would expect certain parameters for a transaction, perhaps the source account number (stored in a hidden html field), destination account number, and a dollar amount. When this function is called, it throws the account number into an SQL query in order to obtain the account details. The account information from this lookup is sent back to the browser on a confirmation page.
Where can the user input cause problems here? First off, what happens if the user enters a negative dollar amount? What if the user changes the hidden field to an account number that's not their own? What if the account number field contains SQL escapes and commands (SQL injection), or JavaScipt code (XSS)? This is a very simple scenaio, but there is so much that can go wrong. Too much trust in the user can allow for circumvention of intended business logic or access controls as well as exposure to injection vulnerabilities.
Never trust the user; there is nothing on the client side that can prevent them from entering dangerous, unexpected input. Always validate input and perform server-side checks on everything.
That said, not all vulnerabilities result from placing to much trust in the client, and it would be impossible to list them all here. Going back to the OWASP Top 10, items 2, 3, 6, 8, 9, and 10, these appear to be affected by vulnerabilities largely unrelated to trusting the user.