Skip to content

Commit

Permalink
Merge pull request #107 from jenkinsci-cert/SECURITY-406
Browse files Browse the repository at this point in the history
[SECURITY-406] Prevent user creation via GET /user/whatever
  • Loading branch information
jglick authored Jan 23, 2017
2 parents e6aa166 + 4036ca2 commit b88b20e
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 2 deletions.
17 changes: 17 additions & 0 deletions core/src/main/java/hudson/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -1078,5 +1078,22 @@ public int getPriority() {
* JENKINS-22346.
*/
public static boolean ALLOW_NON_EXISTENT_USER_TO_LOGIN = Boolean.getBoolean(User.class.getName()+".allowNonExistentUserToLogin");


/**
* Jenkins historically created a (usually) ephemeral user record when an user with Overall/Administer permission
* accesses a /user/arbitraryName URL.
* <p>
* Unfortunately this constitutes a CSRF vulnerability, as malicious users can make admins create arbitrary numbers
* of ephemeral user records, so the behavior was changed in Jenkins 2.TODO / 2.32.2.
* <p>
* As some users may be relying on the previous behavior, setting this to true restores the previous behavior. This
* is not recommended.
*
* SECURITY-406.
*/
// TODO 2.4+ SystemProperties
@Restricted(NoExternalUse.class)
public static boolean ALLOW_USER_CREATION_VIA_URL = Boolean.getBoolean(User.class.getName() + ".allowUserCreationViaUrl");
}

4 changes: 2 additions & 2 deletions core/src/main/java/jenkins/model/Jenkins.java
Original file line number Diff line number Diff line change
Expand Up @@ -2485,11 +2485,11 @@ private File getRootDirFor(String name) {
/**
* Gets the user of the given name.
*
* @return the user of the given name (which may or may not be an id), if that person exists or the invoker {@link #hasPermission} on {@link #ADMINISTER}; else null
* @return the user of the given name (which may or may not be an id), if that person exists; else null
* @see User#get(String,boolean), {@link User#getById(String, boolean)}
*/
public @CheckForNull User getUser(String name) {
return User.get(name,hasPermission(ADMINISTER));
return User.get(name, User.ALLOW_USER_CREATION_VIA_URL && hasPermission(ADMINISTER));
}

public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name ) throws IOException {
Expand Down
23 changes: 23 additions & 0 deletions test/src/test/java/jenkins/model/JenkinsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
Expand Down Expand Up @@ -81,6 +82,28 @@ public class JenkinsTest {

@Rule public JenkinsRule j = new JenkinsRule();

@Issue("SECURITY-406")
@Test
public void testUserCreationFromUrlForAdmins() throws Exception {
WebClient wc = j.createWebClient();

assertNull("User not supposed to exist", User.getById("nonexistent", false));
wc.assertFails("user/nonexistent", 404);
assertNull("User not supposed to exist", User.getById("nonexistent", false));

try {
User.ALLOW_USER_CREATION_VIA_URL = true;

// expected to work
wc.goTo("user/nonexistent2");

assertNotNull("User supposed to exist", User.getById("nonexistent2", false));

} finally {
User.ALLOW_USER_CREATION_VIA_URL = false;
}
}

@Test
public void testIsDisplayNameUniqueTrue() throws Exception {
final String curJobName = "curJobName";
Expand Down

0 comments on commit b88b20e

Please sign in to comment.