Skip to content

CLP (Read only) and write request using master key:  #1976

Closed
LadyK-21/parse-server
#8

Description

@neophob

Issue Description

  • set READ Only CLP (Simple Config)
  • try to update an entry / create a new entry

According to the Parse Docs, using the master key for an action should bypass all security checks:

The master key, on the other hand, is definitely a security mechanism. Using the master key allows you to bypass all of your app's security mechanisms, such as class-level permissions and ACLs. Having the master key is like having root access to your app's servers, and you should guard your master key with the same zeal with which you would guard your production machines' root password.

Steps to reproduce

  1. Create a Custom Table, set CLP to read only
  2. Use this code to create a new entry:
exports.createNewEntry = function(param) {
  const ParseEntry = Parse.Object.extend('Yourname');
  let parseEntry = new ParseEntry();
  parseEntry.set('param', param);

  let acl = new Parse.ACL();
  acl.setPublicReadAccess(true);
  acl.setPublicWriteAccess(false);
  parseEntry.setACL(acl);

  return parseEntry.save({}, { useMasterKey: true })
    .then(parseEntry => {
       ...
    });
};
  1. Result: ERROR: ParseError { code: 119, message: 'Permission denied for this action.' }

When I edit the CLP and allow write permission (or only the Create in the advanced section) this code works.

Expected Results

Bypass CLP/ACL Permission when using Master Key

Actual Outcome

No Permission

Environment Setup

  • Server
    • parse-server version: 2.2.11
    • Operating System: Linux (Ubuntu)

Activity

  1. steven-supersolid commented on Jun 4, 2016

    @steven-supersolid
    Contributor

    Where are you calling createNewEntry - in Cloud Code or client side JS? Cloud Code is trusted so the master key will be set when the SDK is initialized. You can also pass this to the JS SDK if initializing yourself but must only do so if the code is in a trusted environment.

    Might be worth testing the same code with the ACL modification removed so you can see if there is a problem with just CLP and standard saving using the master key.

    You could also try passing null as the first parameter to save instead of {}. I think either should work but have only tested null.

  2. neophob commented on Jun 4, 2016

    @neophob
    ContributorAuthor

    I call this function from client side js. i initialized the sdk with the mater key. I also tried the null param instead the {} variant with the same result.

    when i change the clp permission and add write permission it works.

  3. flovilmart commented on Jun 4, 2016

    @flovilmart
    Contributor

    I'll have a look tomorrow. Do you confirm the behavior is different than on parse.com?

  4. flovilmart commented on Jun 4, 2016

    @flovilmart
    Contributor

    Something is sure, that should pass with the masterKey

  5. flovilmart commented on Jun 5, 2016

    @flovilmart
    Contributor

    I call this function from client side js. i initialized the sdk with the mater key.

    This is plain wrong... You should not let your masterKey in the client SDK!

  6. flovilmart commented on Jun 5, 2016

    @flovilmart
    Contributor

    I just ran some tests, and this behaves correctly, the masterKey is correctly processed when running from the server.

    Again, the masterKey SHOULD NEVER EVER EVER been sent to the client.

    Also, if you look at the code here: https://github.com/ParsePlatform/Parse-SDK-JS/blob/master/src/Parse.js#L32

    The masterKey is completely ignored when Parse is used in the Client SDK.

  7. neophob commented on Jun 5, 2016

    @neophob
    ContributorAuthor

    hi. i dont deploy rhe master key with my app. i only use it for admin tasks. but I think When Im using cloud code and do security checks there it should work aswell.

    thanks

  8. flovilmart commented on Jun 5, 2016

    @flovilmart
    Contributor

    Can you provide the logs when running parse-server with VERBOSE=1

  9. neophob commented on Jun 6, 2016

    @neophob
    ContributorAuthor

    sure, clientcode:

    const ParseMyClass = Parse.Object.extend('MyClass');
    let myClassEntry = new ParseMyClass();
    myClassEntry.set('param1', 'CC');
    myClassEntry.set('param2', 'BB');
    myClassEntry.set('param3', 'AA');
    
    let acl = new Parse.ACL();
    acl.setPublicReadAccess(true);
    acl.setPublicWriteAccess(false);
    myClassEntry.setACL(acl);
    
    return myClassEntry.save({}, { useMasterKey: true })
      .then(entry => {
        console.log('done');
      });
    

    server code (hint: log direction is upside down):

    
    verbose�: error: code=119, message=Permission denied for this action. 
    } 
    "param1": "CC" 
    "param2": "BB", 
    "param3": "AA", 
    }, 
    } 
    "read": true 
    "*": { 
    "ACL": { 
    'x-forwarded-port': '80' } { 
    'content-type': 'text/plain', 
    accept: '*/*', 
    'user-agent': 'node-XMLHttpRequest, Parse/js1.8.5 (NodeJS 4.3.2)', 
    'x-forwarded-proto': 'http', 
    'content-length': '249', 
    'x-forwarded-for': '-removed-, -removed-', 
    'accept-encoding': 'gzip', 
    connection: 'Close', 
    �verbose�: POST /parse/classes/MyClass { host: '-removed-',
    
  10. flovilmart commented on Jun 6, 2016

    @flovilmart
    Contributor

    When you say client code, from where is that code run from?

  11. neophob commented on Jun 6, 2016

    @neophob
    ContributorAuthor

    from my workstation (aka. NOT cloud code and NOT on the same device as the server runns)

  12. flovilmart commented on Jun 6, 2016

    @flovilmart
    Contributor

    How do you require parse? With require("parse/node") ?

  13. neophob commented on Jun 6, 2016

    @neophob
    ContributorAuthor

    correct yes:

    const Parse = require('parse/node');
    const PARSE_MASTER_KEY = process.env.XXX_MASTERKEY;
    const PARSE_SERVER = process.env.XXX_SERVER;
    const PARSE_APPID = process.env.XXX_APPID;
    Parse.initialize(PARSE_APPID, '', PARSE_MASTER_KEY);
    Parse.serverURL = PARSE_SERVER;
    

    (the master key is obsolete but doesn't hurt)

  14. sdf611097 commented on Jun 8, 2016

    @sdf611097
    Contributor

    How you do this step?

    1. Create a Custom Table, set CLP to read only
  15. davimacedo commented on Jun 8, 2016

    @davimacedo
    Member

    To make it works, you will have to save your function as a cloud function in your parse server cloud code and then call it from client (your workstation).

    In our main.js, create samething like:

    Parse.Cloud.define("createNewEntry", function (request, response) {
      const ParseEntry = Parse.Object.extend('Yourname');
      let parseEntry = new ParseEntry();
      parseEntry.set('param', request.params.param);
    
      let acl = new Parse.ACL();
      acl.setPublicReadAccess(true);
      acl.setPublicWriteAccess(false);
      parseEntry.setACL(acl);
    
     parseEntry.save({}, { useMasterKey: true })
        .then(parseEntry => {
           ...
           response.success(parseEntry);
        }).catch(response.error);
    });
    

    In your client call just function like:

    Parse.Cloud.run("createNewEntry", {param: "param"}).then(parseEntry => {
      ...
    });
    
  16. neophob commented on Jun 8, 2016

    @neophob
    ContributorAuthor

    @davimacedo thanks for your reply. I don't think having admin related tasks in cloud code make sense - security wise and user wise.

    @flovilmart any hints from your side?

  17. davimacedo commented on Jun 8, 2016

    @davimacedo
    Member

    @neophob you can first check in your cloud function if user is 'admin'

    Actually it is the unique way to make it works since sending master key from client (admin is also client) is not recommended/allowed anymore as you can see in the link @flovilmart sent:
    https://github.com/ParsePlatform/Parse-SDK-JS/blob/master/src/Parse.js#L32

  18. neophob commented on Jun 8, 2016

    @neophob
    ContributorAuthor

    Hmm I'm a bit confused now. In a recent parse SDK commit (parse-community/Parse-SDK-JS@8a3d011) the documentation was updated, the changed text:

    useMasterKey: In Cloud Code and Node only, causes the Master Key to be used for this request.

    So it looks like the docs clearly say we can use the masterKey from node, but how do we actually SET the master key?

  19. davimacedo commented on Jun 9, 2016

    @davimacedo
    Member

    Hi @neophob I've just realized that you are using in a Node.js environment and there userMarterKey: true should be working. Sorry about that.

    When you pasted your initialization code, you said: "(the master key is obsolete but doesn't hurt)"
    What do you mean?

  20. neophob commented on Jun 9, 2016

    @neophob
    ContributorAuthor

    From parse.js sdk (node)

      /**
       * Call this method first to set up your authentication tokens for Parse.
       * You can get your keys from the Data Browser on parse.com.
       * @method initialize
       * @param {String} applicationId Your Parse Application ID.
       * @param {String} javaScriptKey (optional) Your Parse JavaScript Key (Not needed for parse-server)
       * @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!)
       * @static
       */
      initialize: function initialize(applicationId, javaScriptKey) {
        Parse._initialize(applicationId, javaScriptKey);
      },
    
      _initialize: function _initialize(applicationId, javaScriptKey, masterKey) {
        _CoreManager2['default'].set('APPLICATION_ID', applicationId);
        _CoreManager2['default'].set('JAVASCRIPT_KEY', javaScriptKey);
        _CoreManager2['default'].set('MASTER_KEY', masterKey);
        _CoreManager2['default'].set('USE_MASTER_KEY', false);
      }
    
    ...
    
    Parse.initialize = Parse._initialize;
    

    I just saw that the Parse.initialize = Parse._initialize; is available on node.js only. So correction: this should work when initializing with the master key...

  21. neophob commented on Jun 22, 2016

    @neophob
    ContributorAuthor

    any news on this issue?

  22. neophob commented on Jun 22, 2016

    @neophob
    ContributorAuthor

    seems to work with latest parse server now (v2.2.13)

  23. 11k commented on Jul 19, 2016

    @11k

    Just want to pop in and say that this works for me only if I call Parse.Cloud.useMasterKey() in addition to passing { useMasterKey: true } as an option to each operation that needs to bypass CLPs/ACLs.

  24. haroot commented on Aug 15, 2018

    @haroot

    here is how you override CLPs/ACLs for protected Collections starting with a "_" persay like "_User" aka "User"

    async function updateByUserId(id, field, value) {
      const query = new Parse.Query(Parse.User);
      const user = await query.get(id, { useMasterKey: true });
      user.set(field, value);
      return user.save(null, { useMasterKey: true });
    }
    
  25. flovilmart commented on Aug 15, 2018

    @flovilmart
    Contributor

    @haroot I edited the snippet to remove inaccuracies with the latest and future versions of the server

  26. haroot commented on Aug 15, 2018

    @haroot

    @flovilmart thanks ill replace the old one in the morning . A lot cleaner, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions