CVE-2020-26288 (published 2020-12-30T20:15Z)

Parse Server is an open source backend that can be deployed to any infrastructure that can run Node.js. It is an npm package "parse-server". In Parse Server before version 4.5.0, user passwords involved in LDAP authentication are stored in cleartext. This is fixed in version 4.5.0 by stripping password after authentication to prevent cleartext password storage.

Severity: MEDIUM
CVSS v2 base score: 4.0 (AV:N/AC:L/Au:S/C:P/I:N/A:N)
CVSS v3 base score: 6.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-312
cwe_name: Cleartext Storage of Sensitive Information
description: The product stores sensitive information in cleartext within a resource that might be accessible to another control sphere.
extended_description: 
	 Because the information is stored in cleartext (i.e., unencrypted), attackers could potentially read it. Even if the information is encoded in a way that is not human-readable, certain techniques could determine which encoding is being used, then decode the information.
	 When organizations adopt cloud services, it can be easier for attackers to access the data from anywhere on the Internet.
	 In some systems/environments such as cloud, the use of "double encryption" (at both the software and hardware layer) might be required, and the developer might be solely responsible for both layers, instead of shared responsibility with the administrator of the broader system/environment.
       
         

Fix information:
repo_url: https://github.com/parse-community/parse-server
commit_details: repo_url: https://github.com/parse-community/parse-server
author: Antonio Davi Macedo Coelho de Castro
author_date: 2020-12-02 13:08:02-08:00
msg: Merge pull request from GHSA-4w46-w44m-3jq3

* strip password after authentication to prevent cleartext password storage

* fixed forgotten testcase forcing ;-/

* added test to check if password is not stored in user record

Co-authored-by: Fabian Strachanski <fabian@fastr.de>
repository: repo_url: https://github.com/parse-community/parse-server
repo_name: parse-community/parse-server
description: Parse Server for Node.js / Express
repo_language: JavaScript
owner: parse-community
file_changes: filename: LdapAuth.spec.js
change_type: ModificationType.MODIFY
diff: @@ -211,3 +211,49 @@ it('Should fail if the LDAP server encounters an error while searching', done =>
       .finally(() => server.close());
   });
 });
+
+it('Should delete the password from authData after validation', done => {
+  mockLdapServer(port, 'uid=testuser, o=example', true).then(server => {
+    const options = {
+      suffix: 'o=example',
+      url: `ldap://localhost:${port}`,
+      dn: 'uid={{id}}, o=example'
+    };
+
+    const authData = { id: 'testuser', password: 'secret' };
+
+    ldap
+      .validateAuthData(authData, options)
+      .then(() => {
+        expect(authData).toEqual({ id: 'testuser' });
+        done();
+      })
+      .catch(done.fail)
+      .finally(() => server.close());
+  });
+});
+
+it('Should not save the password in the user record after authentication', done => {
+  mockLdapServer(port, 'uid=testuser, o=example', true).then(server => {
+    const options = {
+      suffix: 'o=example',
+      url: `ldap://localhost:${port}`,
+      dn: 'uid={{id}}, o=example'
+    };
+    reconfigureServer({ auth: { ldap: options } }).then(() => {
+      const authData = { authData: { id: 'testuser', password: 'secret' } };
+      Parse.User.logInWith('ldap', authData).then((returnedUser) => {
+        const query = new Parse.Query("User");
+        query
+          .equalTo('objectId', returnedUser.id).first({ useMasterKey: true })
+          .then((user) => {
+            expect(user.get('authData')).toEqual({ ldap:{ id: 'testuser' }});
+            expect(user.get('authData').ldap.password).toBeUndefined();
+            done();
+          })
+          .catch(done.fail)
+          .finally(() => server.close())
+      })
+    });
+  });
+});

programming_language: TypeScript
filename: ldap.js
change_type: ModificationType.MODIFY
diff: @@ -23,6 +23,7 @@ function validateAuthData(authData, options) {
 
   return new Promise((resolve, reject) => {
     client.bind(userCn, authData.password, ldapError => {
+      delete(authData.password);
       if (ldapError) {
         let error;
         switch (ldapError.code) {

programming_language: JavaScript