For non-PHP based web-clients (JSON) making use of Laravel Controllers; What would be the potential alternatives to CSRF tokens in Laravel to secure web requests?
If your API uses an authentication scheme that does not depend on the authentication token being sent automatically by the browser (which practically means the token or session id is not in a cookie), your API is not vulnerable to CSRF. This includes token-based auths, unless the token is stored in a cookie.
If cookies are used to pass auth tokens (including session ids, which is the same in this respect), you need CSRF protection for all requests that change server state (mostly data, but also logon status or privilege level for example).
For Laravel, you need to pass the token value from the XSRF-TOKEN cookie as a request header value in X-CSRF-TOKEN. With jQuery, this is easily accomplished in any client framework by reading the cookie value and adding it to requests:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': csrfCookieValue
}
});
If your client is not browser based, you can implement a different protection than the one in Laravel already. OWASP has a cheat sheet on what your options are, probably double submit is the easiest to implement while being reasonably secure. In very short, you create a random token and send that to the server as a cookie and also as a request header, the server only compares whether the two (cookie and header) match. This works, because an attacker on a differnet origin (domain) cannot set or access a cookie for the application origin due to the same origin policy in browsers.
I think the whole point on using a csrf token is for use inside the app. If you are sending your request via ajax from the app itself then you can simply append the csrf_token to the request. However, if you are sending data from an external source via json then the best way would be to use oAuth to secure access to your api. Luckily laravel has already built this functionality with laravel/passport so implememting it is fairly straightforward.
Related
used framework: nuxt.js + spring boot(api)
Invoke once api to generate random values and set httpOnly cookie(e.g. key=csrfToken) when the page is first accessed.
The api response is also stored in vuex.
(the response also has token body. with Set-Cookie header.)
When request using axios, If there is the "csrfToken" cookie, add custom header(e.g. key=CSRF_TOKEN_HEADER) to the request.
In the server, if csrfToken cookies are delivered, look up the custom header values to compare them to see if they are the same.
I know that $store is not secure in itself. However, I think CSRF is defensible because $store is not accessible from outside sites.
Please let me know if there is anything wrong with me.
When request using axios, If there is the "csrfToken" cookie, add custom header(e.g. key=CSRF_TOKEN_HEADER) to the request.
Since the cookie is httpOnly, you wouldn't be able to tell if there was or not.
A CSRF attack works by tricking the user into making a request that they didn't intend to make.
The traditional way to do this is to have a form on an attacking website which has the action sent to the website being attacked. When that form is submitted, the request comes from the user's browser and has all the user's cookies for the target website included.
The defence against this is to require that the request includes information that the attacking site cannot know. The CSRF token.
Since the attacking site can't read that token from the user's cookies or the site's session store (depending on where it was stored) or from anywhere else, they can't include it in the form data.
But you aren't using a regular form submission. You are using JavaScript to make the request.
This comes with a built-in defence: The Same Origin Policy and Preflight Requests.
All you need do is force the request to be preflighted (e.g. by setting the Content-Type request header to application/json and including a JSON payload in the body).
This will force a request from the attacking site to make a preflight request. Your server won't have the attacker whitelisted. The browser will therefore never make the attacking request.
If the attacker tries to make a non-preflighted request, it won't have the JSON payload, so your server can reject it as malformed.
I am using React SPA, Express, Express-session, Passport, and JWT.
I'm confused about some of the different client-side storage options to store tokens: Cookies, Session, and JWT / Passport.
Do tokens have to be stored in cookies, even if I can store them in req.sessionID?
Many websites use cookies to store shopping cart tokens. So far I have stored shopping cart data based on the session ID without adding any cookies.
So when users visit my website, I will match it with their
req.sessionID and then retrieve the data in the database like shopping carts and user session.
Do I need to store cookies? I can access it via req.sessionID to get the data needed.
And the second
I have made authentication using a passport-google-oauth20.After I successfully login, the data is saved into the session. and to send it to the client I have to send it via the URL query ?token='sdsaxas'.
in this case I get a lot of difference of opinion. someone saved it
into local storage and someone saved it into cookies by converting it to a token using JWT.
jwt.sign(
payload,
keys.jwt.secretOrPrivateKey,
{
expiresIn:keys.jwt.expiresIn // < i dont know what is this expired for cookies or localstorage ?
}, (err, token) => {
res.redirect(keys.origin.url + "?token=" + token);
});
Can I indeed store everything related to the session by using sessionID (without cookies or localstorage)?
Only by doing fetch once or every page refresh and retrieving the data and then saved into redux because I use React SPA.
This answer is based on the stateless approach and therefore it doesn't talk about the traditional session management
You have asked two altogether different questions:
Shopping cart - which is more related to business functionality
OAuth 2 & JWT - which is related to security and authentication
As a user of an ecommerce website, I'd expect that any item I add to my shopping cart from my mobile device while commuting to my workplace, should be available in the cart when I login to the website from my PC after reaching home. Therefore, the cart data should be saved in the back-end DB and linked to my user account.
When it comes to authentication using OAuth 2.0, the JWT access token and / or refresh token need to be stored somewhere in the client device, so that once the user authenticates himself by providing login credentials, he doesn't need to provide his credentials again to navigate through the website. In this context, the browser local storage, session storage and cookies are all valid options. However, note that here the cookie is not linked to any session on the server side. In other words, the cookie doesn't store any session id. The cookie is merely used as a storage for access token which is passed to the server with every http request and the server then validates the token using the digital signature to ensure that it is not tampered and it is not expired.
Although all three storage options for access and / or refresh tokens are popular, cookie seems to be the most secured option when used in the correct way.
To understand this better, I recommend you read this and this along with the OAuth 2.0 specification.
Update On 16-Feb-2019
I said earlier that cookie seems to be the most secured options. I'd like to further clarify the point here.
The reason I think browser localStorage and sessionStorage do not provide enough security for storing auth tokens are as follows:
If XSS occurs, the malicious script can easily read the tokens from there and send them to a remote server. There on-wards the remote server or attacker would have no problem in impersonating the victim user.
localStorage and sessionStorage are not shared across sub-domains. So, if we have two SPA running on different sub-domains, we won't get the SSO functionality because the token stored by one app won't be available to the other app within the organization. There are some solutions using iframe, but those look more like workarounds rather than a good solution. And when the response header X-Frame-Options is used to avoid clickjacking attacks with iframe, any solution with iframe is out of question.
These risks can, however, be mitigated by using a fingerprint (as mentioned in OWASP JWT Cheat Sheet) which again in turn requires a cookie.
The idea of fingerprint is, generate a cryptographically strong random string of bytes. The Base64 string of the raw string will then be stored in a HttpOnly, Secure, SameSite cookie with name prefix __Secure-. Proper values for Domain and Path attributes should be used as per business requirement. A SHA256 hash of the string will also be passed in a claim of JWT. Thus even if an XSS attack sends the JWT access token to an attacker controlled remote server, it cannot send the original string in cookie and as a result the server can reject the request based on the absence of the cookie. The cookie being HttpOnly cannot be read by XSS scripts.
Therefore, even when we use localStorage and sessionStorage, we have to use a cookie to make it secured. On top of that, we add the sub-domain restriction as mentioned above.
Now, the only concern about using a cookie to store JWT is, CSRF attack. Since we use SameSite cookie, CSRF is mitigated because cross-site requests (AJAX or just through hyperlinks) are not possible. If the site is used in any old browser or some other not so popular browsers that do not support SameSite cookie, we can still mitigate CSRF by additionally using a CSRF cookie with a cryptographically strong random value such that every AJAX request reads the cookie value and add the cookie value in a custom HTTP header (except GET and HEAD requests which are not supposed to do any state modifications). Since CSRF cannot read anything due to same origin policy and it is based on exploiting the unsafe HTTP methods like POST, PUT and DELETE, this CSRF cookie will mitigate the CSRF risk. This approach of using CSRF cookie is used by all modern SPA frameworks. The Angular approach is mentioned here.
Also, since the cookie is httpOnly and Secured, XSS script cannot read it. Thus XSS is also mitigated.
It may be also worth mentioning that XSS and script injection can be further mitigated by using appropriate content-security-policy response header.
Other CSRF mitigation approaches
State Variable (Auth0 uses it) - The client will generate and pass with every request a cryptographically strong random nonce which the server will echo back along with its response allowing the client to validate the nonce. It's explained in Auth0 doc.
Always check the referer header and accept requests only when referer is a trusted domain. If referer header is absent or a non-whitelisted domain, simply reject the request. When using SSL/TLS referrer is usually present. Landing pages (that is mostly informational and not containing login form or any secured content) may be little relaxed and allow requests with missing referer header.
TRACE HTTP method should be blocked in the server as this can be used to read the httpOnly cookie.
Also, set the header Strict-Transport-Security: max-age=; includeSubDomains to allow only secured connections to prevent any man-in-the-middle overwrite the CSRF cookies from a sub-domain.
LocalStorage/SessionStorage is vulnerable to XXS attacks. Access Token can be read by JavaScript.
Cookies, with httpOnly, secure and SameSite=strict flags, are more secure. Access Token and its payload can not be accessed by JavaScript.
BUT, if there is an XSS vulnerability, the attacker would be able to send requests as the authenticated user anyway because the malicious script does not need to read the cookie value, cookies could be sent by the browser automatically.
This statement is true but the risks are different.
With cookies, the access token is still hidden, attackers could only carry out “onsite” attacks. The malicious scripts injected into the web app could be limited, or it might not be very easy to change/inject more scripts. Users or web apps might need to be targeted first by attackers. These conditions limit the scale of the attack.
With localStorage, attackers can read the access token and carry out attacks remotely. They can even share the token with other attackers and cause more serious damage. If attackers manage to inject malicious scripts in CDNs, let’s say google fonts API, attackers would be able to siphon access token and URLs from all websites that use the comprised CDN, and easily find new targets. Websites that use localStorage are more easily to become targets.
For the sake of arguments
A pen-testing might flag your use of localStorage for sensitive data as a risk.
If it was ok for JavaScript to read access token from localStorage from an XSS attack, why do you think the httpOnly flag is still recommended by everyone.
Recommendation from OWASP
Do not store session identifiers in local storage as the data is always accessible by JavaScript. Cookies can mitigate this risk using the httpOnly flag.
https://medium.com/#coolgk/localstorage-vs-cookie-for-jwt-access-token-war-in-short-943fb23239ca
HTTP is a stateless protocol. Read that answer for more detail, but essentially that means that HTTP servers, such as your web server, do not store any information about clients beyond the lifetime of one request. This is a problem for web apps because it means you can't remember which user is logged in.
Cookies were invented as the solution to this. Cookies are textual data that the client and server send back and forth on every request. They allow you to effectively maintain application state data, by having the client and server agree on what they remember each time they communicate.
This means, fundamentally, you cannot have a session without a cookie. There must be a cookie that stores at least the session ID, so that you can find out which user is currently logged into your app by looking up the session. This is what express-session does: the documentation for the main session method explicitly notes that the session ID is stored in a cookie.
so my question is do I need to store cookies?because I can access it via req.sessionID to get the data needed.
You don't need to store cookies. express-session will do this for you. Your application as a whole does need to store a cookie; without it, you wouldn't have a req.sessionID to look up.
According to my experience, just store token in localStorage.
localStorage:
it can store information up tp 5MB. You do not need to ask user's permission to store token in localStorage.
The only concern is that whether the target device support localStorage api.
Check here: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
It is widely supported. But according to my experience, if you have an ios app, and there is a html page in this app which ask the user to store token (also called webview), the localStorage api cannot be recognized and throw an error.
The solution is simply i just put token in url and transfer it every time. In webview, url is not visible.
Cookie:
It is a very old style to store info locally. Storage in cookie is relatively small and you need to ask user's permission in order to store token in cookie.
Cookies are sent with every request, so they can worsen performance (especially for mobile data connections). Modern APIs for client storage are the Web storage API (localStorage and sessionStorage) and IndexedDB.
(https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
Do not store token in sessionStorage or redux.
Data stored in sessionStorage will be lost if the tab is closed. If a user accidentally closed a tab, the token is lost and the server will not be able to identify the current user.
Token stored in redux is not different to be stored in other js files. redux store is just another js file. information stored in redux get lost for every page refresh.
In conclusion,
most of the time, token is stored in localStorage if using a modern style. In certain scenarios, you can store token in cookie and may be put in url sometimes. But never store in session.
Hope it helps.
I am creating a web service very much like shopify but using only JS. Nodejs express for an API with GraphQl and Cors enabled. VueJS for frontend. My authentication is running with JWT. But I have things like anonymous checkout so i need a CSRF protection. The thing is my API is not a router. My router is in the frontend and im only getting the data I need via Graphql through Axios calls to the API. I took a look at the csurf module and tried it out but currently the way im getting a CSRF token to the frontend is with a /getCSRFToken endpoint on the API which i've read is not a good practice and the other thing is It's enabled to access to everyone because of the CORS enabled.
This is the main source of information I have: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
I don't know how to exactly set up the CSRF protection without having am API route for getting the CSRF token and sending it as a cookie through the response and generally make the whole thing secure with the best practices.
I was also thinking about restricting access to the API only for the domains of the shops that are in the system but don't now if that will be any good either.
Any suggestions are welcome
You can generate the cookie client side (using window.crypto), then have the JS read it and send it in a header, the server simply has to verify that they match. But this is vulnerable to the fact the cookie is not HttpOnly (because your JS needs to read it!). For this reason, this method is not best practice, but it is better than nothing.
It also does not prevent users from issuing requests from curl and such once they figure out that they only need to provide a matching cookie and header, but they still cannot issue requests on behalf of other users unless they have the target users authorisation credentials.
There actually isn't anything wrong with having an API route which generates a token per request, although it does result in doubled request density (you need a new token for each request!). The reason for this is that an attacker cannot read the response from an external site (CORS will prevent this). Then you are not vulnerable to any cookie exploit, since you never store a cookie in the first place.
Edit: I see you hint at having CORS * enabled for this endpoint to be public. If your API really is public then you'll have probably better off using OAuth2/JWT authentication instead, this way CSRF becomes irrelevant, since the authentication does not come from cookies.
Trying to keep a value across multiple requests encounters difficulty with history functionality, so it's recommended to either use a token per request or...
You could also store a cookie from the getCsrfToken() request and keep it valid for some time, but make it HttpOnly, since it was issued by the API, the API will be responsible for making sure that it is receiving a valid CSRF token.
The issue with both of the above is that if you want true anonymity then you can't tie these tokens to a particular user, so one user could avoid the CSRF checks on behalf of another by using their own CSRF token!
If you can come up with some way around that whilst maintaining anonymity then the server can check the validity of the tokens that it is receiving, such that one user cannot use their token on behalf of another.
Your last idea (assuming that you want true anonymity) is probably the best. Provided that the user agent is trustworthy, the referer and Origin headers cannot be tampered with, so if you are happy to lock down your API to only the domains which your JS is running on, then doing a referer/Origin check server side will not be easily worked around by an attacker. This isn't best practice, but is practically effective.
Again, curl requests and such can be issued freely, but they can only be issued on behalf of another user if the attacker has the user's authorisation credentials.
The last thing to note is that CSRF is an alternative attack vector to XSS, but if you have XSS vulnerabilities, then CSRF defences usually become obsolete, so make sure that you defend XSS first, before implementing CSRF defence.
I've been reading up on REST and there are a lot of questions on SO about it, as well as on a lot of other sites and blogs. Though I've never seen this specific question asked...for some reason, I can't wrap my mind around this concept...
If I'm building a RESTful API, and I want to secure it, one of the methods I've seen is to use a security token. When I've used other APIs, there's been a token and a shared secret...makes sense. What I don't understand is, requests to a rest service operation are being made through javascript (XHR/Ajax), what is to prevent someone from sniffing that out with something simple like FireBug (or "view source" in the browser) and copying the API key, and then impersonating that person using the key and secret?
We're exposing an API that partners can only use on domains that they have registered with us. Its content is partly public (but preferably only to be shown on the domains we know), but is mostly private to our users. So:
To determine what is shown, our user must be logged in with us, but this is handled separately.
To determine where the data is shown, a public API key is used to limit access to domains we know, and above all to ensure the private user data is not vulnerable to CSRF.
This API key is indeed visible to anyone, we do not authenticate our partner in any other way, and we don't need REFERER. Still, it is secure:
When our get-csrf-token.js?apiKey=abc123 is requested:
Look up the key abc123 in the database and get a list of valid domains for that key.
Look for the CSRF validation cookie. If it does not exist, generate a secure random value and put it in a HTTP-only session cookie. If the cookie did exist, get the existing random value.
Create a CSRF token from the API key and the random value from the cookie, and sign it. (Rather than keeping a list of tokens on the server, we're signing the values. Both values will be readable in the signed token, that's fine.)
Set the response to not be cached, add the cookie, and return a script like:
var apiConfig = apiConfig || {};
if(document.domain === 'example.com'
|| document.domain === 'www.example.com') {
apiConfig.csrfToken = 'API key, random value, signature';
// Invoke a callback if the partner wants us to
if(typeof apiConfig.fnInit !== 'undefined') {
apiConfig.fnInit();
}
} else {
alert('This site is not authorised for this API key.');
}
Notes:
The above does not prevent a server side script from faking a request, but only ensures that the domain matches if requested by a browser.
The same origin policy for JavaScript ensures that a browser cannot use XHR (Ajax) to load and then inspect the JavaScript source. Instead, a regular browser can only load it using <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123"> (or a dynamic equivalent), and will then run the code. Of course, your server should not support Cross-Origin Resource Sharing nor JSONP for the generated JavaScript.
A browser script can change the value of document.domain before loading the above script. But the same origin policy only allows for shortening the domain by removing prefixes, like rewriting subdomain.example.com to just example.com, or myblog.wordpress.com to wordpress.com, or in some browsers even bbc.co.uk to co.uk.
If the JavaScript file is fetched using some server side script then the server will also get the cookie. However, a third party server cannot make a user’s browser associate that cookie to our domain. Hence, a CSRF token and validation cookie that have been fetched using a server side script, can only be used by subsequent server side calls, not in a browser. However, such server side calls will never include the user cookie, and hence can only fetch public data. This is the same data a server side script could scrape from the partner's website directly.
When a user logs in, set some user cookie in whatever way you like. (The user might already have logged in before the JavaScript was requested.)
All subsequent API requests to the server (including GET and JSONP requests) must include the CSRF token, the CSRF validation cookie, and (if logged on) the user cookie. The server can now determine if the request is to be trusted:
The presence of a valid CSRF token ensures the JavaScript was loaded from the expected domain, if loaded by a browser.
The presence of the CSRF token without the validation cookie indicates forgery.
The presence of both the CSRF token and the CSRF validation cookie does not ensure anything: this could either be a forged server side request, or a valid request from a browser. (It could not be a request from a browser made from an unsupported domain.)
The presence of the user cookie ensures the user is logged on, but does not ensure the user is a member of the given partner, nor that the user is viewing the correct website.
The presence of the user cookie without the CSRF validation cookie indicates forgery.
The presence of the user cookie ensures the current request is made through a browser. (Assuming a user would not enter their credentials on an unknown website, and assuming we don’t care about users using their own credentials to make some server side request.) If we also have the CSRF validation cookie, then that CSRF validation cookie was also received using a browser. Next, if we also have a CSRF token with a valid signature, and the random number in the CSRF validation cookie matches the one in that CSRF token, then the JavaScript for that token was also received during that very same earlier request during which the CSRF cookie was set, hence also using a browser. This then also implies the above JavaScript code was executed before the token was set, and that at that time the domain was valid for the given API key.
So: the server can now safely use the API key from the signed token.
If at any point the server does not trust the request, then a 403 Forbidden is returned. The widget can respond to that by showing a warning to the user.
It's not required to sign the CSRF validation cookie, as we're comparing it to the signed CSRF token. Not signing the cookie makes each HTTP request shorter, and the server validation a bit faster.
The generated CSRF token is valid indefinitely, but only in combination with the validation cookie, so effectively until the browser is closed.
We could limit the lifetime of the token's signature. We could delete the CSRF validation cookie when the user logs out, to meet the OWASP recommendation. And to not share the per-user random number between multiple partners, one could add the API key to the cookie name. But even then one cannot easily refresh the CSRF validation cookie when a new token is requested, as users might be browsing the same site in multiple windows, sharing a single cookie (which, when refreshing, would be updated in all windows, after which the JavaScript token in the other windows would no longer match that single cookie).
For those who use OAuth, see also OAuth and Client-Side Widgets, from which I got the JavaScript idea. For server side use of the API, in which we cannot rely on the JavaScript code to limit the domain, we're using secret keys instead of the public API keys.
api secret is not passed explicitly, secret is used to generate a sign of current request, at the server side, the server generate the sign following the same process, if the two sign matches, then the request is authenticated successfully -- so only the sign is passed through the request, not the secret.
This question has an accepted answer but just to clarify, shared secret authentication works like this:
Client has public key, this can be shared with anyone, doesn't
matter, so you can embed it in javascript. This is used to identify the user on the server.
Server has secret key and this secret MUST be protected. Therefore,
shared key authentication requires that you can protect your secret
key. So a public javascript client that connects directly to another
service is not possible because you need a server middleman to
protect the secret.
Server signs request using some algorithm that includes the secret
key (the secret key is sort of like a salt) and preferably a timestamp then sends the request to the service. The timestamp is to prevent "replay" attacks. A signature of a request is only valid for around n seconds. You can check that on the server by getting the timestamp header that should contain the value of the timestamp that was included in the signature. If that timestamp is expired, the request fails.
The service gets the request which contains not only the signature
but also all the fields that were signed in plain text.
The service then signs the request in the same way using the shared
secret key and compares the signatures.
I will try to answer the the question in it's original context. So question is "Is the secret (API) key safe to be placed with in JavaScript.
In my opinion it is very unsafe as it defeats the purpose of authentication between the systems. Since the key will be exposed to the user, user may retrieve information he/she is not authorized to. Because in a typical rest communication authentication is only based on the API Key.
A solution in my opinion is that the JavaScript call essentially pass the request to an internal server component who is responsible from making a rest call. The internal server component let's say a Servlet will read the API key from a secured source such as permission based file system, insert into the HTTP header and make the external rest call.
I hope this helps.
I supose you mean session key not API key. That problem is inherited from the http protocol and known as Session hijacking. The normal "workaround" is, as on any web site, to change to https.
To run the REST service secure you must enable https, and probably client authentification. But after all, this is beyond the REST idea. REST never talks about security.
What you want to do on the server side is generate an expiring session id that is sent back to the client on login or signup.
The client can then use that session id as a shared secret to sign subsequent requests.
The session id is only passed once and this MUST be over SSL.
See example here
Use a nonce and timestamp when signing the request to prevent session hijacking.
I want to protect my WebApi endpoints against CSRF. Angular team recommends to use digest of your site's authentication cookie with a salt for added security. I'm not sure what's the advantage here comparing to random token. What is more, how do you protect login page?
Here is explanation why do you need to protect login page https://security.stackexchange.com/questions/2120/when-the-use-of-a-antiforgerytoken-is-not-required-needed
The method you're describing is detailed here:
Cross Site Request Forgery (XSRF) Protection XSRF is a technique by
which an unauthorized site can gain your user's private data. Angular
provides a mechanism to counter XSRF. When performing XHR requests,
the $http service reads a token from a cookie (by default, XSRF-TOKEN)
and sets it as an HTTP header (X-XSRF-TOKEN). Since only JavaScript
that runs on your domain could read the cookie, your server can be
assured that the XHR came from JavaScript running on your domain. The
header will not be set for cross-domain requests.
To take advantage of this, your server needs to set a token in a
JavaScript readable session cookie called XSRF-TOKEN on the first HTTP
GET request. On subsequent XHR requests the server can verify that the
cookie matches X-XSRF-TOKEN HTTP header, and therefore be sure that
only JavaScript running on your domain could have sent the request.
The token must be unique for each user and must be verifiable by the
server (to prevent the JavaScript from making up its own tokens). We
recommend that the token is a digest of your site's authentication
cookie with a salt for added security.
The name of the headers can be specified using the xsrfHeaderName and
xsrfCookieName properties of either $httpProvider.defaults at
config-time, $http.defaults at run-time, or the per-request config
object.
In order to prevent collisions in environments where multiple Angular
apps share the same domain or subdomain, we recommend that each
application uses unique cookie name.
This appears to be a variation on the Encrypted Token Pattern CSRF prevention method, using hashing rather than encryption, and the salt is acting like the secret key.
It also relies upon a HTTP feature that prevents custom headers from being sent cross domain, much like adding X-Requested-With does.
The part that states "an unauthorized site can gain your user's private data" is slightly misleading, as CSRF does not allow this directly - a CSRF attack allows an unauthorized site to submit your site's forms in the context of the currently logged in user.
An advantage of this method is that you do not need to save anything extra server side. For example, if you are using token-based authentication you can easily verify that the CSRF token received matches the cookie by applying the same hash with the server-side salt. Even if you are using cookie-based authentication you do not need to verify the CSRF token against a separate value stored server-side - you simply hash the cookie value to verify that it matches the passed token.
See this answer for how to protect against login CSRF.