CVE-2023-40572: Cross-Site Request Forgery (CSRF) (CWE-352)
value: XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. The create action is vulnerable to a CSRF attack, allowing script and thus remote code execution when targeting a user with script/programming right, thus compromising the confidentiality, integrity and availability of the whole XWiki installation. When a user with script right views this image and a log message `ERROR foo - Script executed!` appears in the log, the XWiki installation is vulnerable. This has been patched in XWiki 14.10.9 and 15.4RC1 by requiring a CSRF token for the actual page creation.
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 8
Published: 2023-08-24T02:15Z
Repository: https://github.com/xwiki/xwiki-platform
Language: Java
Files changed: xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/CreateAction.java, xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/web/CreateActionTest.java, xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm
Commit date: 2023-04-18 16:30:04 +0200
Commit message: XWIKI-20849: Require a CSRF token in the create action

Diff: diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/CreateAction.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/CreateAction.java
index c7b7ce6eda030..bb61779b1b5d7 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/CreateAction.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/CreateAction.java
@@ -21,10 +21,11 @@
 
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Provider;
 import javax.inject.Singleton;
 import javax.script.ScriptContext;
 
@@ -94,10 +95,13 @@ public class CreateAction extends XWikiAction
     /**
      * Local entity reference serializer hint.
      */
     private static final String LOCAL_SERIALIZER_HINT = "local";
 
+    @Inject
+    private CSRFToken csrf;
+
     /**
      * The action to perform when creating a new page from a template.
      *
      * @version $Id$
      */
@@ -183,13 +187,16 @@ public String render(XWikiContext context) throws XWikiException
         // Note: Note checking the logical spaceReference, but the space of the final actual document reference, since
         // that is where we are creating the new document.
         checkRights(newDocumentReference.getLastSpaceReference(), context);
 
         // Check if the document to create already exists and if it respects the name strategy
+        // Also check the CSRF token.
         XWikiDocument newDocument = context.getWiki().getDocument(newDocumentReference, context);
         if (handler.isDocumentAlreadyExisting(newDocument) || handler.isDocumentPathTooLong(newDocumentReference)
-            || !this.isEntityReferenceNameValid(newDocumentReference)) {
+            || !this.isEntityReferenceNameValid(newDocumentReference)
+            || !this.csrf.isTokenValid(context.getRequest().getParameter("form_token")))
+        {
             return CREATE_TEMPLATE;
         }
 
         // Verify if the "type" of document to create has been set, even if we currently do not use it in the action.
         // The goal is let the user be able to chose it, which have some consequences in the UI (thanks to javascript).
@@ -332,12 +339,11 @@ private String getRedirectParameters(String parent, String title, String templat
         }
         if (title != null) {
             redirectParams += "&title=" + Util.encodeURI(title, null);
         }
         // Both the save and the edit action might require a CSRF token
-        CSRFToken csrf = Utils.getComponent(CSRFToken.class);
-        redirectParams += "&form_token=" + Util.encodeURI(csrf.getToken(), null);
+        redirectParams += "&form_token=" + Util.encodeURI(this.csrf.getToken(), null);
 
         return redirectParams;
     }
 
     /**
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/web/CreateActionTest.java b/xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/web/CreateActionTest.java
index 1e18afea2dc88..d5564f20bd750 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/web/CreateActionTest.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/web/CreateActionTest.java
@@ -70,10 +70,12 @@
 @ComponentList
 @ReferenceComponentList
 @OldcoreTest
 class CreateActionTest
 {
+    private static final String CSRF_TOKEN_VALUE = "token4234343";
+
     @InjectMockitoOldcore
     MockitoOldcore oldcore;
 
     XWikiURLFactory mockURLFactory;
 
@@ -123,10 +125,14 @@ public void beforeEach() throws Exception
         this.context.setResponse(this.mockResponse);
 
         when(this.mockRequest.get("type")).thenReturn("plain");
 
         this.oldcore.getMocker().registerMockComponent(ObservationManager.class);
+
+        when(this.csrfToken.getToken()).thenReturn(CSRF_TOKEN_VALUE);
+        when(this.csrfToken.isTokenValid(CSRF_TOKEN_VALUE)).thenReturn(true);
+        when(this.mockRequest.getParameter("form_token")).thenReturn(CSRF_TOKEN_VALUE);
     }
 
     @Test
     void newDocumentFromURL() throws Exception
     {
@@ -135,39 +141,66 @@ void newDocumentFromURL() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token42";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Run the action
         String result = action.render(context);
 
         // The tests are below this line!
 
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         verify(mockURLFactory).createURL("X", "Y", "edit",
-            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki",
             context);
     }
 
+    @Test
+    void newDocumentFromURLWithInvalidToken() throws Exception
+    {
+        // new document = xwiki:X.Y
+        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("X"), "Y");
+        XWikiDocument document = mock(XWikiDocument.class);
+        when(document.getDocumentReference()).thenReturn(documentReference);
+        when(document.isNew()).thenReturn(true);
+        when(document.getLocalReferenceMaxLength()).thenReturn(255);
+        this.context.setDoc(document);
+
+        // Submit an invalid token
+        when(this.mockRequest.getParameter("form_token")).thenReturn("fakeToken");
+
+        // Run the action
+        String result = this.action.render(this.context);
+
+        // The tests are below this line!
+
+        // Verify that the create template is rendered, so the UI is displayed for the user to re-submit with the
+        // form token.
+        assertEquals("create", result);
+
+        // Verify that we got until the token validation.
+        verify(this.csrfToken).isTokenValid("fakeToken");
+
+        // We should not get this far so no redirect should be done, just the template will be rendered.
+        verify(this.mockURLFactory, never()).createURL(any(), any(), any(), any(), any(), any(),
+            any(XWikiContext.class));
+    }
+
     @Test
     void newDocumentButNonTerminalFromURL() throws Exception
     {
         // new document = xwiki:X.Y
         DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("X"), "Y");
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token432";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Pass the tocreate=nonterminal request parameter
         when(mockRequest.getParameter("tocreate")).thenReturn("nonterminal");
 
         // Run the action
@@ -176,12 +209,15 @@ void newDocumentButNonTerminalFromURL() throws Exception
         // The tests are below this line!
 
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
+        // Verify that the token has been validated.
+        verify(this.csrfToken).isTokenValid(CSRF_TOKEN_VALUE);
+
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null,
+            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null,
             "xwiki", context);
     }
 
     @Test
     void newDocumentFromURLWhenNoType() throws Exception
@@ -214,12 +250,10 @@ void newDocumentWebHomeTopLevelFromURL() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token4234";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Run the action
         String result = action.render(context);
 
         // The tests are below this line!
@@ -227,11 +261,11 @@ void newDocumentWebHomeTopLevelFromURL() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: The title is not "WebHome", but "X" (the space's name) to avoid exposing "WebHome" in the UI.
         verify(mockURLFactory).createURL("X", "WebHome", "edit",
-            "template=&parent=Main.WebHome&title=X&form_token=" + csrfTokenValue, null, "xwiki",
+            "template=&parent=Main.WebHome&title=X&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
             context);
     }
 
     @Test
     void newDocumentWebHomeFromURL() throws Exception
@@ -241,12 +275,10 @@ void newDocumentWebHomeFromURL() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token34342";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Run the action
         String result = action.render(context);
 
         // The tests are below this line!
@@ -255,11 +287,11 @@ void newDocumentWebHomeFromURL() throws Exception
         assertNull(result);
 
         // Note1: The bebavior is the same for both a top level space and a child space WebHome.
         // Note2: The title is not "WebHome", but "Y" (the space's name) to avoid exposing "WebHome" in the UI.
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null,
+            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null,
             "xwiki", context);
     }
 
     @Test
     void newDocumentWebHomeButTerminalFromURL() throws Exception
@@ -269,12 +301,10 @@ void newDocumentWebHomeButTerminalFromURL() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token4234343";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Pass the tocreate=terminal request parameter
         when(mockRequest.getParameter("tocreate")).thenReturn("terminal");
 
         // Run the action
@@ -285,11 +315,11 @@ void newDocumentWebHomeButTerminalFromURL() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: We are creating X.Y instead of X.Y.WebHome because the tocreate parameter says "terminal".
         verify(mockURLFactory).createURL("X", "Y", "edit",
-            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null, "xwiki",
+            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
             context);
     }
 
     @Test
     void newDocumentWebHomeTopLevelSpaceButTerminalFromURL() throws Exception
@@ -353,12 +383,10 @@ void existingDocumentFromUI() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token424345";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X&name=Y
         when(mockRequest.getParameter("spaceReference")).thenReturn("X");
         when(mockRequest.getParameter("name")).thenReturn("Y");
 
@@ -370,11 +398,11 @@ void existingDocumentFromUI() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: We are creating X.Y.WebHome since we default to non-terminal documents.
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null,
+            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null,
             "xwiki", context);
     }
 
     @Test
     void existingDocumentFromUICheckEscaping() throws Exception
@@ -384,12 +412,10 @@ void existingDocumentFromUICheckEscaping() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token42124343";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X.Y&name=Z
         when(mockRequest.getParameter("spaceReference")).thenReturn("X.Y");
         when(mockRequest.getParameter("name")).thenReturn("Z");
 
@@ -401,11 +427,11 @@ void existingDocumentFromUICheckEscaping() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: We are creating X.Y.Z.WebHome since we default to non-terminal documents.
         verify(mockURLFactory).createURL("X.Y.Z", "WebHome", "edit",
-            "template=&parent=Main.WebHome&title=Z&form_token=" + csrfTokenValue, null,
+            "template=&parent=Main.WebHome&title=Z&form_token=" + CSRF_TOKEN_VALUE, null,
             "xwiki", context);
     }
 
     @Test
     void existingDocumentTerminalFromUI() throws Exception
@@ -415,12 +441,10 @@ void existingDocumentTerminalFromUI() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token424334466";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X&name=Y&tocreate=terminal
         when(mockRequest.getParameter("spaceReference")).thenReturn("X");
         when(mockRequest.getParameter("name")).thenReturn("Y");
         when(mockRequest.getParameter("tocreate")).thenReturn("terminal");
@@ -433,11 +457,11 @@ void existingDocumentTerminalFromUI() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: We are creating X.Y instead of X.Y.WebHome because the tocreate parameter says "terminal".
         verify(mockURLFactory).createURL("X", "Y", "edit",
-            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null, "xwiki",
+            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
             context);
     }
 
     @Test
     void existingDocumentTerminalFromUICheckEscaping() throws Exception
@@ -447,12 +471,10 @@ void existingDocumentTerminalFromUICheckEscaping() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token429988";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X.Y&name=Z&tocreate=termina
         when(mockRequest.getParameter("spaceReference")).thenReturn("X.Y");
         when(mockRequest.getParameter("name")).thenReturn("Z");
         when(mockRequest.getParameter("tocreate")).thenReturn("terminal");
@@ -465,11 +487,11 @@ void existingDocumentTerminalFromUICheckEscaping() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: We are creating X.Y.Z instead of X.Y.Z.WebHome because the tocreate parameter says "terminal".
         verify(mockURLFactory).createURL("X.Y", "Z", "edit",
-            "template=&parent=Main.WebHome&title=Z&form_token=" + csrfTokenValue, null, "xwiki",
+            "template=&parent=Main.WebHome&title=Z&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
             context);
     }
 
     @Test
     void existingDocumentTerminalFromUIButAlreadyExisting() throws Exception
@@ -579,12 +601,10 @@ void existingDocumentFromUITopLevelDocument() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token42009";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI name=Y
         when(mockRequest.getParameter("name")).thenReturn("Y");
 
         // Run the action
@@ -595,11 +615,11 @@ void existingDocumentFromUITopLevelDocument() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: We are creating X.Y.WebHome since we default to non-terminal documents.
         verify(mockURLFactory).createURL("Y", "WebHome", "edit",
-            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null, "xwiki",
+            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
             context);
     }
 
     /*
      * Deprecated parameters
@@ -613,12 +633,10 @@ void existingDocumentFromUIDeprecated() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token4233311";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI space=X&page=Y
         when(mockRequest.getParameter("space")).thenReturn("X");
         when(mockRequest.getParameter("page")).thenReturn("Y");
 
@@ -630,11 +648,11 @@ void existingDocumentFromUIDeprecated() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: We are creating X.Y since the deprecated parameters were creating terminal documents by default.
         verify(mockURLFactory).createURL("X", "Y", "edit",
-            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null, "xwiki",
+            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
             context);
     }
 
     @Test
     void existingDocumentFromUIDeprecatedCheckEscaping() throws Exception
@@ -644,12 +662,10 @@ void existingDocumentFromUIDeprecatedCheckEscaping() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token422112455";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI space=X.Y&page=Z
         when(mockRequest.getParameter("space")).thenReturn("X.Y");
         when(mockRequest.getParameter("page")).thenReturn("Z");
 
@@ -662,11 +678,11 @@ void existingDocumentFromUIDeprecatedCheckEscaping() throws Exception
         assertNull(result);
 
         // Note1: The space parameter was previously considered as space name, not space reference, so it is escaped.
         // Note2: We are creating X\.Y.Z since the deprecated parameters were creating terminal documents by default.
         verify(mockURLFactory).createURL("X\\.Y", "Z", "edit",
-            "template=&parent=Main.WebHome&title=Z&form_token=" + csrfTokenValue, null, "xwiki",
+            "template=&parent=Main.WebHome&title=Z&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
             context);
     }
 
     @Test
     void existingDocumentNonTerminalFromUIDeprecated() throws Exception
@@ -676,12 +692,10 @@ void existingDocumentNonTerminalFromUIDeprecated() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token42778900";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI space=X&tocreate=space
         when(mockRequest.getParameter("space")).thenReturn("X");
         when(mockRequest.getParameter("tocreate")).thenReturn("space");
 
@@ -693,11 +707,11 @@ void existingDocumentNonTerminalFromUIDeprecated() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: We are creating X.WebHome because the tocreate parameter says "space".
         verify(mockURLFactory).createURL("X", "WebHome", "edit",
-            "template=&parent=Main.WebHome&title=X&form_token=" + csrfTokenValue, null, "xwiki",
+            "template=&parent=Main.WebHome&title=X&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
             context);
     }
 
     @Test
     void existingDocumentNonTerminalFromUIDeprecatedIgnoringPage() throws Exception
@@ -707,12 +721,10 @@ void existingDocumentNonTerminalFromUIDeprecatedIgnoringPage() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token4344119982";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI space=X&page=Y&tocreate=space
         when(mockRequest.getParameter("space")).thenReturn("X");
         when(mockRequest.getParameter("page")).thenReturn("Y");
         when(mockRequest.getParameter("tocreate")).thenReturn("space");
@@ -726,11 +738,11 @@ void existingDocumentNonTerminalFromUIDeprecatedIgnoringPage() throws Exception
         assertNull(result);
 
         // Note: We are creating X.WebHome instead of X.Y because the tocreate parameter says "space" and the page
         // parameter is ignored.
         verify(mockURLFactory).createURL("X", "WebHome", "edit",
-            "template=&parent=Main.WebHome&title=X&form_token=" + csrfTokenValue, null, "xwiki",
+            "template=&parent=Main.WebHome&title=X&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
             context);
     }
 
     @Test
     void existingDocumentNonTerminalFromUIDeprecatedCheckEscaping() throws Exception
@@ -740,12 +752,10 @@ void existingDocumentNonTerminalFromUIDeprecatedCheckEscaping() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token425553";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI space=X.Y&tocreate=space
         when(mockRequest.getParameter("space")).thenReturn("X.Y");
         when(mockRequest.getParameter("tocreate")).thenReturn("space");
 
@@ -758,11 +768,11 @@ void existingDocumentNonTerminalFromUIDeprecatedCheckEscaping() throws Exception
         assertNull(result);
 
         // Note1: The space parameter was previously considered as space name, not space reference, so it is escaped.
         // Note2: We are creating X\.Y.WebHome because the tocreate parameter says "space".
         verify(mockURLFactory).createURL("X\\.Y", "WebHome", "edit",
-            "template=&parent=Main.WebHome&title=X.Y&form_token=" + csrfTokenValue, null,
+            "template=&parent=Main.WebHome&title=X.Y&form_token=" + CSRF_TOKEN_VALUE, null,
             "xwiki", context);
     }
 
     /*
      * Template providers
@@ -899,12 +909,10 @@ void existingDocumentFromUITemplateProviderSpecified() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token42008766";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         when(mockRequest.getParameter("spaceReference")).thenReturn("X");
         when(mockRequest.getParameter("name")).thenReturn("Y");
@@ -922,11 +930,11 @@ void existingDocumentFromUITemplateProviderSpecified() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: We are creating X.Y and using the template extracted from the template provider.
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null, "xwiki",
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
             context);
     }
 
     @Test
     void existingDocumentFromUITemplateProviderSpecifiedRestrictionExists() throws Exception
@@ -975,12 +983,10 @@ void existingDocumentFromUITemplateProviderSpecifiedRestrictionExistsOnParentSpa
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token4221098";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X.Y.Z&name=W&templateProvider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         String spaceReferenceString = "X.Y.Z";
         when(mockRequest.getParameter("spaceReference")).thenReturn(spaceReferenceString);
@@ -1002,11 +1008,11 @@ void existingDocumentFromUITemplateProviderSpecifiedRestrictionExistsOnParentSpa
 
         // Note1: We are allowed to create anything under space X or its children, be it a terminal or a non-terminal
         // document
         // Note2: We are creating X.Y.Z.W and using the template extracted from the template provider.
         verify(mockURLFactory).createURL("X.Y.Z.W", "WebHome", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=W&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=W&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void existingDocumentFromUITemplateProviderSpecifiedButNotAllowed() throws Exception
@@ -1130,12 +1136,10 @@ void newDocumentWebHomeFromURLTemplateProviderSpecifiedTerminal() throws Excepti
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token42988733";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Specifying a template provider in the URL: templateprovider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);
 
@@ -1153,11 +1157,11 @@ void newDocumentWebHomeFromURLTemplateProviderSpecifiedTerminal() throws Excepti
         assertNull(result);
 
         // Note: We are creating the document X.Y as terminal and using a template, as specified in the template
         // provider.
         verify(mockURLFactory).createURL("X", "Y", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void newDocumentWebHomeFromURLTemplateProviderSpecifiedTerminalOverriddenFromUIToNonTerminal() throws Exception
@@ -1167,12 +1171,10 @@ void newDocumentWebHomeFromURLTemplateProviderSpecifiedTerminalOverriddenFromUIT
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token4222113555";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Specifying a template provider in the URL: templateprovider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);
         when(mockRequest.getParameter("tocreate")).thenReturn("nonterminal");
@@ -1191,11 +1193,11 @@ void newDocumentWebHomeFromURLTemplateProviderSpecifiedTerminalOverriddenFromUIT
         assertNull(result);
 
         // Note: We are creating the document X.Y.WebHome as non-terminal even if the template provider says otherwise.
         // Also using a template, as specified in the template provider.
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void newDocumentFromURLTemplateProviderSpecifiedNonTerminal() throws Exception
@@ -1205,12 +1207,10 @@ void newDocumentFromURLTemplateProviderSpecifiedNonTerminal() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token4200983331";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Specifying a template provider in the URL: templateprovider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);
 
@@ -1228,11 +1228,11 @@ void newDocumentFromURLTemplateProviderSpecifiedNonTerminal() throws Exception
         assertNull(result);
 
         // Note: We are creating the document X.Y as terminal and using a template, as specified in the template
         // provider.
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void newDocumentFromURLTemplateProviderSpecifiedNonTerminalButOverriddenFromUITerminal() throws Exception
@@ -1242,12 +1242,10 @@ void newDocumentFromURLTemplateProviderSpecifiedNonTerminalButOverriddenFromUITe
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token42234456";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Specifying a template provider in the URL: templateprovider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);
         when(mockRequest.getParameter("tocreate")).thenReturn("terminal");
@@ -1266,11 +1264,11 @@ void newDocumentFromURLTemplateProviderSpecifiedNonTerminalButOverriddenFromUITe
         assertNull(result);
 
         // Note: We are creating the document X.Y as terminal and using a template, as specified in the template
         // provider.
         verify(mockURLFactory).createURL("X", "Y", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void existingDocumentFromUITemplateSpecified() throws Exception
@@ -1281,12 +1279,10 @@ void existingDocumentFromUITemplateSpecified() throws Exception
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
 
         context.setDoc(document);
-        String csrfTokenValue = "token4298833";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X&name=Y&template=XWiki.MyTemplate
         String templateDocumentFullName = "XWiki.MyTemplate";
         DocumentReference templateDocumentReference =
             new DocumentReference("MyTemplate", Arrays.asList("XWiki"), "xwiki");
@@ -1305,11 +1301,11 @@ void existingDocumentFromUITemplateSpecified() throws Exception
         // Verify null is returned (this means the response has been returned)
         assertNull(result);
 
         // Note: We are creating X.Y.WebHome and using the template specified in the request.
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void existingDocumentFromUITemplateProviderSpecifiedTerminal() throws Exception
@@ -1319,12 +1315,10 @@ void existingDocumentFromUITemplateProviderSpecifiedTerminal() throws Exception
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token4244112";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         String spaceReferenceString = "X";
         when(mockRequest.getParameter("spaceReference")).thenReturn(spaceReferenceString);
@@ -1345,11 +1339,11 @@ void existingDocumentFromUITemplateProviderSpecifiedTerminal() throws Exception
         assertNull(result);
 
         // Note: We are creating the document X.Y as terminal and using a template, as specified in the template
         // provider.
         verify(mockURLFactory).createURL("X", "Y", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void existingDocumentFromUITemplateProviderSpecifiedTerminalOverridenFromUIToNonTerminal() throws Exception
@@ -1359,12 +1353,10 @@ void existingDocumentFromUITemplateProviderSpecifiedTerminalOverridenFromUIToNon
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token422555987";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         String spaceReferenceString = "X";
         when(mockRequest.getParameter("spaceReference")).thenReturn(spaceReferenceString);
@@ -1386,11 +1378,11 @@ void existingDocumentFromUITemplateProviderSpecifiedTerminalOverridenFromUIToNon
         assertNull(result);
 
         // Note: We are creating the document X.Y.WebHome as non-terminal, even if the template provider says otherwise.
         // Also using a template, as specified in the template provider.
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void existingDocumentFromUITemplateProviderSpecifiedNonTerminal() throws Exception
@@ -1400,12 +1392,10 @@ void existingDocumentFromUITemplateProviderSpecifiedNonTerminal() throws Excepti
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token424111553";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         String spaceReferenceString = "X";
         when(mockRequest.getParameter("spaceReference")).thenReturn(spaceReferenceString);
@@ -1426,11 +1416,11 @@ void existingDocumentFromUITemplateProviderSpecifiedNonTerminal() throws Excepti
         assertNull(result);
 
         // Note: We are creating the document X.Y.WebHome as non-terminal and using a template, as specified in the
         // template provider.
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void existingDocumentFromUITemplateProviderSpecifiedNonTerminalOverridenFromUIToTerminal() throws Exception
@@ -1440,12 +1430,10 @@ void existingDocumentFromUITemplateProviderSpecifiedNonTerminalOverridenFromUITo
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token42008733";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         String spaceReferenceString = "X";
         when(mockRequest.getParameter("spaceReference")).thenReturn(spaceReferenceString);
@@ -1467,11 +1455,11 @@ void existingDocumentFromUITemplateProviderSpecifiedNonTerminalOverridenFromUITo
         assertNull(result);
 
         // Note: We are creating the document X.Y as terminal, even if the template provider says otherwise.
         // Also using a template, as specified in the template provider.
         verify(mockURLFactory).createURL("X", "Y", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void newDocumentWebHomeFromURLTemplateProviderSpecifiedButOldPageType() throws Exception
@@ -1481,12 +1469,10 @@ void newDocumentWebHomeFromURLTemplateProviderSpecifiedButOldPageType() throws E
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token423366";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Specifying a template provider in the URL: templateprovider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);
 
@@ -1505,11 +1491,11 @@ void newDocumentWebHomeFromURLTemplateProviderSpecifiedButOldPageType() throws E
 
         // Note: We are creating the document X.Y as terminal, since the template provider did not specify a "terminal"
         // property and it used the old "page" type instead. Also using a template, as specified in the template
         // provider.
         verify(mockURLFactory).createURL("X", "Y", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void newDocumentWebHomeFromURLTemplateProviderSpecifiedButOldPageTypeButOverriddenFromUIToNonTerminal()
@@ -1520,12 +1506,10 @@ void newDocumentWebHomeFromURLTemplateProviderSpecifiedButOldPageTypeButOverridd
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(true);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token4265677398";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Specifying a template provider in the URL: templateprovider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);
         when(mockRequest.getParameter("tocreate")).thenReturn("nonterminal");
@@ -1545,11 +1529,11 @@ void newDocumentWebHomeFromURLTemplateProviderSpecifiedButOldPageTypeButOverridd
 
         // Note: We are creating the document X.Y.WebHome as non-terminal, since even if the template provider did not
         // specify a "terminal" property and it used the old "page" type, the UI explicitly asked for a non-terminal
         // document. Also using a template, as specified in the template provider.
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void existingDocumentFromUITemplateProviderSpecifiedButOldSpaceType() throws Exception
@@ -1559,12 +1543,10 @@ void existingDocumentFromUITemplateProviderSpecifiedButOldSpaceType() throws Exc
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token421198337";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         when(mockRequest.getParameter("spaceReference")).thenReturn("X");
         when(mockRequest.getParameter("name")).thenReturn("Y");
@@ -1585,11 +1567,11 @@ void existingDocumentFromUITemplateProviderSpecifiedButOldSpaceType() throws Exc
 
         // Note: We are creating X.Y.WebHome as non-terminal, since the template provider does not specify a "terminal"
         // property and we fallback on the "type" property's value. Also using the template extracted from the template
         // provider.
         verify(mockURLFactory).createURL("X.Y", "WebHome", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void existingDocumentFromUITemplateProviderSpecifiedButOldSpaceTypeButOverridenFromUIToTerminal() throws Exception
@@ -1599,12 +1581,10 @@ void existingDocumentFromUITemplateProviderSpecifiedButOldSpaceTypeButOverridenF
         XWikiDocument document = mock(XWikiDocument.class);
         when(document.getDocumentReference()).thenReturn(documentReference);
         when(document.isNew()).thenReturn(false);
         when(document.getLocalReferenceMaxLength()).thenReturn(255);
         context.setDoc(document);
-        String csrfTokenValue = "token429866333";
-        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
 
         // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
         String templateProviderFullName = "XWiki.MyTemplateProvider";
         when(mockRequest.getParameter("spaceReference")).thenReturn("X");
         when(mockRequest.getParameter("name")).thenReturn("Y");
@@ -1625,11 +1605,11 @@ void existingDocumentFromUITemplateProviderSpecifiedButOldSpaceTypeButOverridenF
         assertNull(result);
 
         // Note: We are creating X.Y as terminal, since it is overriden from the UI, regardless of any backwards
         // compatibility resolutions. Also using the template extracted from the template provider.
         verify(mockURLFactory).createURL("X", "Y", "edit",
-            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
+            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
             null, "xwiki", context);
     }
 
     @Test
     void newDocumentWebHomeFromURLTemplateProviderSpecifiedWithSaveAndEdit() throws Exception
diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm
index 1e46fbba304d9..436e4ec4c321b 100644
--- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm
+++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm
@@ -274,10 +274,11 @@
 
   ## The "templateprovider" field is needed by the CreateAction, even empty.
   ## By the past, it was "xwiki/create.js" which created this field, but it was causing problems when the user sent the
   ## form before the JavaScript code was executed.
   <input type="hidden" name="templateprovider" id="templateprovider" value="" />
+  <input type="hidden" name="form_token" value="$!{escapetool.xml($services.csrf.getToken())}"/>
 
   <div class='row'>
     ## Hide the first column when displayed in an AJAX call by clicking on a Wanted Link (because we know the target
     ## location from the link reference) or when the current document is new (because the create action uses the location
     ## of the current document as target in this case).

Vulnerable code:             || !this.isEntityReferenceNameValid(newDocumentReference)) {
        CSRFToken csrf = Utils.getComponent(CSRFToken.class);
        redirectParams += "&form_token=" + Util.encodeURI(csrf.getToken(), null);
        String csrfTokenValue = "token42";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token432";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null,
        String csrfTokenValue = "token4234";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=X&form_token=" + csrfTokenValue, null, "xwiki",
        String csrfTokenValue = "token34342";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null,
        String csrfTokenValue = "token4234343";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null, "xwiki",
        String csrfTokenValue = "token424345";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null,
        String csrfTokenValue = "token42124343";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Z&form_token=" + csrfTokenValue, null,
        String csrfTokenValue = "token424334466";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null, "xwiki",
        String csrfTokenValue = "token429988";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Z&form_token=" + csrfTokenValue, null, "xwiki",
        String csrfTokenValue = "token42009";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null, "xwiki",
        String csrfTokenValue = "token4233311";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null, "xwiki",
        String csrfTokenValue = "token422112455";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=Z&form_token=" + csrfTokenValue, null, "xwiki",
        String csrfTokenValue = "token42778900";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=X&form_token=" + csrfTokenValue, null, "xwiki",
        String csrfTokenValue = "token4344119982";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=X&form_token=" + csrfTokenValue, null, "xwiki",
        String csrfTokenValue = "token425553";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=&parent=Main.WebHome&title=X.Y&form_token=" + csrfTokenValue, null,
        String csrfTokenValue = "token42008766";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue, null, "xwiki",
        String csrfTokenValue = "token4221098";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=W&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token42988733";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token4222113555";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token4200983331";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token42234456";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token4298833";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token4244112";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token422555987";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token424111553";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token42008733";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token423366";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token4265677398";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token421198337";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
        String csrfTokenValue = "token429866333";
        when(this.csrfToken.getToken()).thenReturn(csrfTokenValue);
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + csrfTokenValue,
Fixed code: import javax.inject.Inject;
    @Inject
    private CSRFToken csrf;

        // Also check the CSRF token.
            || !this.isEntityReferenceNameValid(newDocumentReference)
            || !this.csrf.isTokenValid(context.getRequest().getParameter("form_token")))
        {
        redirectParams += "&form_token=" + Util.encodeURI(this.csrf.getToken(), null);
    private static final String CSRF_TOKEN_VALUE = "token4234343";


        when(this.csrfToken.getToken()).thenReturn(CSRF_TOKEN_VALUE);
        when(this.csrfToken.isTokenValid(CSRF_TOKEN_VALUE)).thenReturn(true);
        when(this.mockRequest.getParameter("form_token")).thenReturn(CSRF_TOKEN_VALUE);
            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
    @Test
    void newDocumentFromURLWithInvalidToken() throws Exception
    {
        // new document = xwiki:X.Y
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("X"), "Y");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(true);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        this.context.setDoc(document);

        // Submit an invalid token
        when(this.mockRequest.getParameter("form_token")).thenReturn("fakeToken");

        // Run the action
        String result = this.action.render(this.context);

        // The tests are below this line!

        // Verify that the create template is rendered, so the UI is displayed for the user to re-submit with the
        // form token.
        assertEquals("create", result);

        // Verify that we got until the token validation.
        verify(this.csrfToken).isTokenValid("fakeToken");

        // We should not get this far so no redirect should be done, just the template will be rendered.
        verify(this.mockURLFactory, never()).createURL(any(), any(), any(), any(), any(), any(),
            any(XWikiContext.class));
    }

        // Verify that the token has been validated.
        verify(this.csrfToken).isTokenValid(CSRF_TOKEN_VALUE);

            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null,
            "template=&parent=Main.WebHome&title=X&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null,
            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null,
            "template=&parent=Main.WebHome&title=Z&form_token=" + CSRF_TOKEN_VALUE, null,
            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
            "template=&parent=Main.WebHome&title=Z&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
            "template=&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
            "template=&parent=Main.WebHome&title=Z&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
            "template=&parent=Main.WebHome&title=X&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
            "template=&parent=Main.WebHome&title=X&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
            "template=&parent=Main.WebHome&title=X.Y&form_token=" + CSRF_TOKEN_VALUE, null,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE, null, "xwiki",
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=W&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
            "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y&form_token=" + CSRF_TOKEN_VALUE,
  <input type="hidden" name="form_token" value="$!{escapetool.xml($services.csrf.getToken())}"/>