Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/UndeleteAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/UndeleteAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/UndeleteAction.java	(working copy)
@@ -42,6 +42,11 @@
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
+
         XWiki xwiki = context.getWiki();
         XWikiRequest request = context.getRequest();
         XWikiResponse response = context.getResponse();
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/AdminAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/AdminAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/AdminAction.java	(working copy)
@@ -34,6 +34,7 @@
 {
     private static final Log log = LogFactory.getLog(AdminAction.class);
 
+    @Override
     public String render(XWikiContext context) throws XWikiException
     {
         XWikiRequest request = context.getRequest();
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/XWikiAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/XWikiAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/XWikiAction.java	(working copy)
@@ -40,6 +40,7 @@
 import org.xwiki.container.servlet.ServletContainerException;
 import org.xwiki.container.servlet.ServletContainerInitializer;
 import org.xwiki.context.Execution;
+import org.xwiki.csrftoken.CSRFToken;
 import org.xwiki.observation.ObservationManager;
 import org.xwiki.observation.event.ActionExecutionEvent;
 import org.xwiki.velocity.VelocityManager;
@@ -524,4 +525,31 @@
         }
         return tdoc;
     }
+
+    /**
+     * Perform CSRF check and redirect to the resubmission page if needed.
+     * Throws an exception if the access should be denied, returns false if the check failed and
+     * the user will be redirected to a resubmission page.
+     * 
+     * @param context current xwiki context containing the request
+     * @return true if the check succeeded, false if resubmission is needed
+     * @throws XWikiException if the check fails
+     */
+    protected boolean csrfTokenCheck(XWikiContext context) throws XWikiException
+    {
+        CSRFToken csrf = Utils.getComponent(CSRFToken.class);
+        try {
+            String token = context.getRequest().getParameter("form_token");
+            if (!csrf.isTokenValid(token)) {
+                sendRedirect(context.getResponse(), csrf.getResubmissionURL());
+                return false;
+            }
+        } catch (XWikiException exception) {
+            // too bad
+            throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS,
+                XWikiException.ERROR_XWIKI_ACCESS_DENIED,
+                "Access denied, secret token verification failed", exception);
+        }
+        return true;
+    }
 }
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/DeleteAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/DeleteAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/DeleteAction.java	(working copy)
@@ -55,6 +55,10 @@
         if (!"1".equals(request.getParameter(CONFIRM_PARAM))) {
             return true;
         }
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
 
         String sindex = request.getParameter("id");
         if (sindex != null && xwiki.hasRecycleBin(context)) {
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/ResetVersionsAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/ResetVersionsAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/ResetVersionsAction.java	(working copy)
@@ -18,6 +16,11 @@
 
         String confirm = request.getParameter("confirm");
         if ((confirm != null) && (confirm.equals("1"))) {
+            // CSRF prevention
+            if (!csrfTokenCheck(context)) {
+                return false;
+            }
+
             XWikiDocument tdoc = getTranslatedDocument(doc, language, context);
             // Do it
             tdoc.resetArchive(context);
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/CommentAddAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/CommentAddAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/CommentAddAction.java	(working copy)
@@ -54,6 +54,11 @@
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
+
         XWiki xwiki = context.getWiki();
         XWikiResponse response = context.getResponse();
         XWikiDocument doc = context.getDoc();
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/UploadAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/UploadAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/UploadAction.java	(working copy)
@@ -80,6 +80,11 @@
             }
         }
 
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
+
         XWikiDocument doc = (XWikiDocument) context.getDoc().clone();
 
         // TODO This code does not use transactions properly. Each attachment uses its own transaction, thus in case of
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/RegisterAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/RegisterAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/RegisterAction.java	(working copy)
@@ -20,21 +20,26 @@
  */
 package com.xpn.xwiki.web;
 
+import org.apache.velocity.VelocityContext;
+
 import com.xpn.xwiki.XWiki;
 import com.xpn.xwiki.XWikiContext;
 import com.xpn.xwiki.XWikiException;
-import com.xpn.xwiki.doc.XWikiDocument;
-import org.apache.velocity.VelocityContext;
 
 public class RegisterAction extends XWikiAction {
-	public boolean action(XWikiContext context) throws XWikiException {
+    @Override
+    public boolean action(XWikiContext context) throws XWikiException {
         XWiki xwiki = context.getWiki();
         XWikiRequest request = context.getRequest();
         XWikiResponse response = context.getResponse();
-        XWikiDocument doc = context.getDoc();
 
         String register = request.getParameter("register");
         if ((register!=null)&&(register.equals("1"))) {
+            // CSRF prevention
+            if (!csrfTokenCheck(context)) {
+                return false;
+            }
+
             int useemail = xwiki.getXWikiPreferenceAsInt("use_email_verification", 0, context);
             int result;
             if (useemail==1)
@@ -52,9 +57,10 @@
             sendRedirect(response, redirect);
             return false;
         }
-	}
-	
-	public String render(XWikiContext context) throws XWikiException {
+    }
+
+    @Override
+    public String render(XWikiContext context) throws XWikiException {
         return "register";
-	}
+    }
 }
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/ObjectRemoveAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/ObjectRemoveAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/ObjectRemoveAction.java	(working copy)
@@ -65,6 +65,11 @@
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
+
         XWiki xwiki = context.getWiki();
         XWikiResponse response = context.getResponse();
         String username = context.getUser();
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/RollbackAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/RollbackAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/RollbackAction.java	(working copy)
@@ -30,6 +30,11 @@
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
+
         XWiki xwiki = context.getWiki();
         XWikiResponse response = context.getResponse();
         XWikiDocument doc = context.getDoc();
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/SaveAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/SaveAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/SaveAction.java	(working copy)
@@ -170,6 +170,11 @@
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
+
         if (save(context)) {
             return true;
         }
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/CreateAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/CreateAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/CreateAction.java	(working copy)
@@ -36,6 +36,7 @@
      * 
      * @see XWikiAction#render(XWikiContext)
      */
+    @Override
     public String render(XWikiContext context) throws XWikiException
     {
         return "create";
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/SaveAndContinueAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/SaveAndContinueAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/SaveAndContinueAction.java	(working copy)
@@ -44,23 +44,16 @@
      */
     @Override
     public boolean action(XWikiContext context) throws XWikiException {
-        XWikiRequest request = context.getRequest();
-        XWikiResponse response = context.getResponse();
-
-        // Try to find the URL of the edit page which we came from.
-        String back = request.getParameter("xcontinue");
-        if (StringUtils.isEmpty(back)) {
-            back = request.getParameter("xredirect");
-        }
-        if (StringUtils.isEmpty(back)) {
-            back = removeAllParametersFromQueryStringExceptEditor(request.getHeader("Referer"));
-        }
-        if (StringUtils.isEmpty(back)) {
-            back = context.getDoc().getURL("edit", context);
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
         }
 
+        // Try to find the URL of the edit page which we came from
+        String back = findBackURL(context);
+
         // This will never be true if "back" comes from request.getHeader("referer")
-        if (back != null && back.indexOf("editor=class") >= 0) {
+        if (back != null && back.contains("editor=class")) {
             PropUpdateAction pua = new PropUpdateAction();
             if (pua.propUpdate(context)) {
                 pua.render(context);
@@ -80,7 +73,7 @@
 
         // Forward back to the originating page
         try {
-            response.sendRedirect(back);
+            context.getResponse().sendRedirect(back);
         } catch (IOException ignored) {
             // This exception is ignored because it will only be thrown if content has already been sent to the
             // response. This should never happen but we have to catch the exception anyway.
@@ -100,6 +93,28 @@
     }
 
     /**
+     * Try to find the URL of the edit page which we came from.
+     * 
+     * @param context current xwiki context
+     * @return URL of the edit page
+     */
+    private String findBackURL(XWikiContext context)
+    {
+        XWikiRequest request = context.getRequest();
+        String back = request.getParameter("xcontinue");
+        if (StringUtils.isEmpty(back)) {
+            back = request.getParameter("xredirect");
+        }
+        if (StringUtils.isEmpty(back)) {
+            back = removeAllParametersFromQueryStringExceptEditor(request.getHeader("Referer"));
+        }
+        if (StringUtils.isEmpty(back)) {
+            back = context.getDoc().getURL("edit", context);
+        }
+        return back;
+    }
+
+    /**
      * @param url the URL to get a modified version of.
      * @return A modified version of the input url where all parameters 
      *         are stripped from the query string except "editor"
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/PropAddAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/PropAddAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/PropAddAction.java	(working copy)
@@ -35,6 +35,11 @@
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
+
         XWiki xwiki = context.getWiki();
         XWikiResponse response = context.getResponse();
         XWikiDocument doc = context.getDoc();
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/DeleteAttachmentAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/DeleteAttachmentAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/DeleteAttachmentAction.java	(working copy)
@@ -40,6 +40,11 @@
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
+
         XWikiRequest request = context.getRequest();
         XWikiResponse response = context.getResponse();
         XWikiDocument doc = context.getDoc();
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/ObjectAddAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/ObjectAddAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/ObjectAddAction.java	(working copy)
@@ -39,6 +39,11 @@
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
+
         XWiki xwiki = context.getWiki();
         XWikiResponse response = context.getResponse();
         String username = context.getUser();
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/PropUpdateAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/PropUpdateAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/PropUpdateAction.java	(working copy)
@@ -105,6 +105,11 @@
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
+
         try {
             if (propUpdate(context)) {
                 return true;
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsAction.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsAction.java	(revision 29535)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsAction.java	(working copy)
@@ -40,21 +40,24 @@
     @Override
     public boolean action(XWikiContext context) throws XWikiException
     {
-        DeleteVersionsForm form = (DeleteVersionsForm) context.getForm();
+        // CSRF prevention
+        if (!csrfTokenCheck(context)) {
+            return false;
+        }
 
+        DeleteVersionsForm form = (DeleteVersionsForm) context.getForm();
         if (!form.isConfirmed()) {
             return true;
         }
 
         Version v1;
         Version v2;
-        Version rev = form.getRev();
-        if (rev == null) {
+        if (form.getRev() == null) {
             v1 = form.getRev1();
             v2 = form.getRev2();
         } else {
-            v1 = rev;
-            v2 = rev;
+            v1 = form.getRev();
+            v2 = form.getRev();
         }
 
         if (v1 != null && v2 != null) {
Index: core/xwiki-core/src/main/resources/ApplicationResources.properties
===================================================================
--- core/xwiki-core/src/main/resources/ApplicationResources.properties	(revision 29535)
+++ core/xwiki-core/src/main/resources/ApplicationResources.properties	(working copy)
@@ -2442,6 +2442,9 @@
 admin.templates.createprovider.defaultdocname=MyTemplateProvider
 admin.templates.createprovider.create=Create
 
+# CSRFToken resources
+csrftoken.confirmation=<p>This request contains an invalid authentication information.</p><p>This might happen in the following situations:<ul><li>You left the editor open in another window/tab and logged off and on again</li><li>Your authentication token exipired after a long period of inactivity</li><li>Somebody tried to perform a CSRF attack</li></ul></p><p>If you are sure that none of these situations apply in your case, you might have found a bug. We are sorry about that, please report it on <a href="http://jira.xwiki.org/">XWiki JIRA</a></p><p>If you are unsure, say <strong>No</strong>.</p><br/><p>Do you want to resend the request?</p>
+
 #
 # XWiki Classes
 #
Index: core/xwiki-core/pom.xml
===================================================================
--- core/xwiki-core/pom.xml	(revision 29535)
+++ core/xwiki-core/pom.xml	(working copy)
@@ -816,6 +816,11 @@
       <version>${pom.version}</version>
       <scope>runtime</scope>
     </dependency>
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-core-csrftoken</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
 
     <!-- Used to mock XWiki class -->
     <dependency>
Index: tools/xwiki-configuration-resources/src/main/resources/xwiki.properties.vm
===================================================================
--- tools/xwiki-configuration-resources/src/main/resources/xwiki.properties.vm	(revision 29535)
+++ tools/xwiki-configuration-resources/src/main/resources/xwiki.properties.vm	(working copy)
@@ -34,6 +34,21 @@
 #-# Default value is 100.
 # core.renderingcache.size=100
 
+#-# [Since 2.4]
+#-# Controls whether secret token validation mechanism should be used (to prevent CSRF attacks).
+#-#
+#-# If enabled, all actions requiring "comment", "edit", "delete", "admin" or "programming" rights
+#-# will check that the parameter "form_token" with the value of a random secret token is present
+#-# in the request.
+#-# This feature requires CSRFToken component.
+#-# 
+#-# Valid values:
+#-#   0: Disabled
+#-#   1: Enabled
+#-#
+#-# Default value is 0
+# core.csrftoken.enabled = 0
+
 #-------------------------------------------------------------------------------------
 # Rendering
 #-------------------------------------------------------------------------------------
