As a general principle, any request that comes from a browser should be untrusted unless it can be verified. A request to a site doesn't necessarily reflect the user's intent.
Using SSL (HTTPS) for all sensitive pages and forms is the first step. It won't stop malicious requests from coming from the browser, but it will prevent alteration of pages or form data by a man-in-the-middle attack. Any site that asks a user to log in should take this precaution.
With that as a base, there are a number of ways to stop most CSRF attacks. The two most popular ones are nonce tokens and the double submit cookie method.
A nonce token is an arbitrary value sent by the server to the browser. The browser inserts it as a parameter into the POST request when it submits a form, and the server checks that it's the correct value. Browsers follow the same-origin policy, which says that data held in a particular page is accessible only to pages that have the same origin. A script on www.example.com/page1 can access the browser data for www.example.com/page2 but not the data for www.anotherexample.com. This makes the nonce value safe from discovery in most cases. It's not safe against same-page CSRF attacks.
Another method is to give the same nonce value in a cookie and a token. Again, the browser puts the token into the POST request. The server checks that the submitted token and the cookie match. The server doesn't have to save the token value, which makes the approach more convenient in some application environments.
CSRF attacks work because any request to a site normally delivers all cookies associated with it. A recent expansion to the cookie protocol offers another layer of protection. The SameSite cookie parameter directs the browser to limit or forbid the sending of cookies when the request comes from a page that doesn't belong to the same site as the destination. Two values are allowed.
- SameSite=strict directs the browser to send the cookie only if the source and destination belong to the same site.
- SameSite=lax makes an exception for top-level GET requests, i.e., those that operate at the address bar level.
This provides good protection against CSRF in browsers that support it. However, it didn't gain widespread adoption until 2018, so many older browsers will ignore the SameSite attribute. It should be treated just as an extra layer of defense, not as strong protection by itself.