Are both GET and POST request vulnerable to CSRF? Should we use PUT instead?
Yes, both GET and POST are vulnerable to CSRF.
However, RFC 7231 states
the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe".
Therefore, if a website has kept to the standard and only implements "unsafe" actions as POSTs, then here only POST requests are vulnerable. However, many websites don't do this for all of their "unsafe" actions - for example, logout functionality is often overlooked.
Although PUT is safe without CORS, it isn't the correct method to use here. If you're implementing CSRF protection, a good method for protecting XHR requests is to add a header such as
X-Requested-With to your requests and then validate this header server side. You could combine this with a random value that is checked server side to add further protection in the case of browser plugins like Flash containing vulnerabilities that accidentally allow the header. See this answer and this one.
GET and POST can both be vulnerable to CSRF unless the server puts a strong Anti-CSRF mechanism in place, the server cant rely on the browser to prevent cross-domain requests. As for PUT requests, there is a slight difference, theoretically it is vulnerable too, however, it requires the circumstances to be more conducive. Here is why:
While GET and POST requests can be easily made through HTML forms, images, script tags etc, making a PUT request is slightly more complicated, you cant make a PUT request without the browser interfering with the request.
If you use the XmlHttpRequest API to set the request method as PUT, the browser first sends a CORS(Cross Origin Resource Sharing) 'pre-flight' request to the target server basically seeking permission to make the request, what happens is that an OPTIONS request is made to the target server with a header set to the Origin of the request say: http://example.com (where your script is hosted) and Access-Control-Request-Method and Access-Control-Request-Headers. If the server responds with Access-Control-Allow-Origin: http://example.com, then the browser sends your request forward. Pertinently, you need the server to return value for the Origin header as your server, if the Origin header is set to '*', then credentials cannot be sent with the request. As making a PUT request with credentials(cookies) to a server that allows Origin: * is not allowed. From https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS:
Unlike simple requests (discussed above), "preflighted" requests first send an HTTP request by the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if:
It uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted. It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)
So, conducting a CSRF attack against a page that uses PUT is slightly harder, as it requires the cooperation of the target server. However, this shouldn't be relied on as a viable anti-CSRF mechanism as browser quirks may render this mechanism for protection useless.
Also, as for the circumstances that may render dependence on PUT method as a protection have been summed up well in this answer: https://stackoverflow.com/a/11972282/4399898
Hope that helped.