XSRF

XSRF(Cross-site request forgery), is a common security problem in web applications. The previous link also describes in detail how XSRF attacks are implemented.

A common approach to preventing XSRF today is to record an unpredictable cookie for each user and then require that all submitted requests (POST/PUT/DELETE) must have this cookie data. If this data does not match , then the request may be forged.

Beego has a built-in XSRF prevention mechanism. To use this mechanism, you need to add the enablexsrf setting to the application configuration file:

  1. enablexsrf = true
  2. xsrfkey = 61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o
  3. xsrfexpire = 3600

Or:

  1. web.EnableXSRF = true
  2. web.XSRFKEY = "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o"
  3. web.XSRFExpire = 3600 //过期时间,默认1小时

If XSRF is enabled, then Beego’s web application will set a cookie value of _xsrf for all users (expires 1 hour by default), and if the POST PUT DELET request does not have this cookie value, then the request will be rejected outright.

Beego uses the Secure and HTTP-ONLY options to save cookies, so in most cases this means you will need to use the HTTPS protocol and will not be able to access the cookie values inside JS.

In the early days when these two options were not available, attackers could easily get hold of the cookie values we set, thus causing security problems. However, even if these two options are added, it does not mean that they are foolproof. For example, an attacker can try to overwrite the cookie set by the HTTP protocol with the HTTP protocol, as described in the secure option above.

Because Beego needs to get the Token to compare with the value in the cookie, Beego requires the user to carry the XSRF Token in their request, and you can do this in two ways.

  • Carry a field called _xsrf in the form, which contains the XSRF Token;
  • Set X-Xsrftoken or X-Csrftoken in the HTTP HEADER of the submitted request, the value is the Token;

Form With Token

The easiest way to do this is to use Beego’s method of adding a field to the form that brings back the XSRF token:

  1. func (mc *MainController) XsrfPage() {
  2. mc.XSRFExpire = 7200
  3. mc.Data["xsrfdata"] = template.HTML(mc.XSRFFormHTML())
  4. mc.TplName = "xsrf.html"
  5. }

And xsrf.html:

  1. <form action="/new_message" method="post">
  2. {{ .xsrfdata }}
  3. <input type="text" name="message" />
  4. <input type="submit" value="Post" />
  5. </form>

.xsrfdata is mc.Data["xsrfdata"] and more details refer to Template Engine

Page Meta

It is simpler to add the XSRF HEADER to each request by extending Ajax

Requires you to save a _xsrf value in the HTML

  1. func (this *HomeController) Get(){
  2. this.Data["xsrf_token"] = this.XSRFToken()
  3. }

And use it:

  1. <head>
  2. <meta name="_xsrf" content="{{.xsrf_token}}" />
  3. </head>

Extend the ajax method to add the _xsrf value to the header to support jquery post/get and other methods that use ajax internally:

  1. var ajax = $.ajax;
  2. $.extend({
  3. ajax: function (url, options) {
  4. if (typeof url === "object") {
  5. options = url;
  6. url = undefined;
  7. }
  8. options = options || {};
  9. url = options.url;
  10. var xsrftoken = $("meta[name=_xsrf]").attr("content");
  11. var headers = options.headers || {};
  12. var domain = document.domain.replace(/\./gi, "\\.");
  13. if (
  14. !/^(http:|https:).*/.test(url) ||
  15. eval("/^(http:|https:)\\/\\/(.+\\.)*" + domain + ".*/").test(url)
  16. ) {
  17. headers = $.extend(headers, { "X-Xsrftoken": xsrftoken });
  18. }
  19. options.headers = headers;
  20. return ajax(url, options);
  21. },
  22. });

Note that here you can replace ajax or JQuery with your own front-end framework, as the core lies in setting the header headers, {'X-Xsrftoken':xsrftoken}.

The xsrftoken can be inside an HTML tag, or it can be read directly from the previous response and brought in when the form is submitted. For example:

  1. func (mc *MainController) XsrfJSON() {
  2. mc.XSRFExpire = 7200
  3. type data struct {
  4. XsrfToken string `json:"xsrfToken"`
  5. }
  6. _ = mc.JSONResp(&data{XsrfToken: mc.XSRFToken()})
  7. }

Controller Skips XSRF

XSRF was previously a globally set parameter, if set then all API requests will be validated, but there are times when the API logic does not need to be validated, so now supports setting the mask at the Controller level:

  1. type AdminController struct{
  2. web.Controller
  3. }
  4. func (a *AdminController) Prepare() {
  5. a.EnableXSRF = false
  6. }

Refer to Controller API Hooks - Prepare

Similarly, the expiration time is set globally as web.XSRFExpire, but there are times when we can modify this expiration time in the controller to specifically address a particular type of processing logic:

  1. func (this *HomeController) Get(){
  2. this.XSRFExpire = 7200
  3. // ...
  4. }

Reference