فهرست منبع

Security Fix usd-2022-0041: CWE-284 Improper Access Control.

Thanks to Christian Pöschl of usd AG and xet7 !
Lauri Ojansivu 4 روز پیش
والد
کامیت
f6591d7820
2فایلهای تغییر یافته به همراه112 افزوده شده و 2 حذف شده
  1. 69 2
      models/users.js
  2. 43 0
      server/publications/users.js

+ 69 - 2
models/users.js

@@ -588,15 +588,37 @@ Users.deny({
 });
 
 
+// Custom MongoDB engine that enforces field restrictions
+class SecureMongoDBEngine extends MongoDBEngine {
+  getSearchCursor(searchObject, options) {
+    // Always enforce field projection to prevent data leakage
+    const secureProjection = {
+      _id: 1,
+      username: 1,
+      'profile.fullname': 1,
+      'profile.avatarUrl': 1,
+    };
+
+    // Override any projection passed in options
+    const secureOptions = {
+      ...options,
+      projection: secureProjection,
+    };
+
+    return super.getSearchCursor(searchObject, secureOptions);
+  }
+}
+
 // Search a user in the complete server database by its name, username or emails adress. This
 // is used for instance to add a new user to a board.
 UserSearchIndex = new Index({
   collection: Users,
   fields: ['username', 'profile.fullname', 'profile.avatarUrl'],
-  allowedFields: ['username', 'profile.fullname', 'profile.avatarUrl'],
-  engine: new MongoDBEngine({
+  allowedFields: ['username', 'profile.fullname', 'profile.avatarUrl', '_id'],
+  engine: new SecureMongoDBEngine({
     fields: function (searchObject, options) {
       return {
+        _id: 1,
         username: 1,
         'profile.fullname': 1,
         'profile.avatarUrl': 1,
@@ -2756,6 +2778,51 @@ if (Meteor.isServer) {
       });
     }
   });
+
+  // Server-side method to sanitize user data for search results
+  Meteor.methods({
+    sanitizeUserForSearch(userData) {
+      check(userData, Object);
+
+      // Only allow safe fields for user search
+      const safeFields = {
+        _id: 1,
+        username: 1,
+        'profile.fullname': 1,
+        'profile.avatarUrl': 1,
+        'profile.initials': 1,
+        'emails.address': 1,
+        'emails.verified': 1,
+        authenticationMethod: 1,
+        isAdmin: 1,
+        loginDisabled: 1,
+        teams: 1,
+        orgs: 1,
+      };
+
+      const sanitized = {};
+      for (const field of Object.keys(safeFields)) {
+        if (userData[field] !== undefined) {
+          sanitized[field] = userData[field];
+        }
+      }
+
+      // Ensure sensitive fields are never included
+      delete sanitized.services;
+      delete sanitized.resume;
+      delete sanitized.email;
+      delete sanitized.createdAt;
+      delete sanitized.modifiedAt;
+      delete sanitized.sessionData;
+      delete sanitized.importUsernames;
+
+      if (process.env.DEBUG === 'true') {
+        console.log('Sanitized user data for search:', Object.keys(sanitized));
+      }
+
+      return sanitized;
+    }
+  });
 }
 
 export default Users;

+ 43 - 0
server/publications/users.js

@@ -49,6 +49,49 @@ Meteor.publish('user-authenticationMethod', function (match) {
   return ret;
 });
 
+// Secure user search publication for board sharing
+Meteor.publish('user-search', function (searchTerm) {
+  check(searchTerm, String);
+
+  // Only allow logged-in users to search for other users
+  if (!this.userId) {
+    return this.ready();
+  }
+
+  // Create a regex for case-insensitive search
+  const searchRegex = new RegExp(searchTerm, 'i');
+
+  // Search for users by username, fullname, or email
+  const ret = ReactiveCache.getUsers(
+    {
+      $or: [
+        { username: searchRegex },
+        { 'profile.fullname': searchRegex },
+        { 'emails.address': searchRegex }
+      ]
+    },
+    {
+      fields: {
+        _id: 1,
+        username: 1,
+        'profile.fullname': 1,
+        'profile.avatarUrl': 1,
+        'profile.initials': 1,
+        'emails.address': 1,
+        'emails.verified': 1,
+        authenticationMethod: 1,
+        isAdmin: 1,
+        loginDisabled: 1,
+        teams: 1,
+        orgs: 1,
+      },
+    },
+    true,
+  );
+
+  return ret;
+});
+
 // update last connection date and last connection average time (in seconds) for a user
 // function UpdateLastConnectionDateAndLastConnectionAverageTime(lstUsers) {
 //   let lastConnectionAverageTime;