Giter Club home page Giter Club logo

Comments (19)

MichaelJCole avatar MichaelJCole commented on June 5, 2024 1

@shideneyu Like I said in my first comment, that's every web application ever written.

"Never trust the client" is the first rule of client-server security.
"Never trust a peer" is the first rule of p2p security.

My expectation would be the "good" peer rejects the new data from the "bad" peer, and that "good" peers can know what's good from the IPFS data, not the code.

from orbitdb.

haydenyoung avatar haydenyoung commented on June 5, 2024 1

When writing malicious records to secondDb, check whether firstDb is replicating them. If not, the access controllers should be working as expected and your firstDb should be "protected" from any data being written to secondDb.

There is nothing stopping secondUser from circumventing (for whatever reason) local checks on secondDb. firstUser is not responsible for ensuring the integrity of secondUser's data. Instead, we want to ensure that firstDb will not replicate secondDb's data if it is in violation of the rules laid out by the Custom AC.

from orbitdb.

shideneyu avatar shideneyu commented on June 5, 2024 1

We want to ensure that firstDb will not replicate secondDb's data if it is in violation of the rules laid out by the Custom AC.

This is what I was expecting. But I can't replicate this scenario. Can you show me how that works @haydenyoung ?

In my code, firstDb is replicating secondDb's data. You just need to run the code twice and you'll see on the second firstDb.all() that it fetched the violating data.

image

Or you can just run this: node.mjs https://gist.github.com/shideneyu/31e77772ec2a14f2746252720efdbd5e

from orbitdb.

haydenyoung avatar haydenyoung commented on June 5, 2024

Could you provide the version of orbitdb you are using? The example code above looks very outdated. For example, there is no addAccessController function available in the latest version of OrbitDB.

You can install the latest version of OrbitDB using npm i @orbitdb/core.

from orbitdb.

shideneyu avatar shideneyu commented on June 5, 2024

Thank you four reply. I took some time to fix my code as to use the latest version of orbitdb (1.0.0) and show you the bug:

import { create } from 'ipfs-core'
import { createOrbitDB, useAccessController } from '@orbitdb/core'

const CustomAccessController = () => async ({ orbitdb, identities, address }) => {
  address = '/custom/access-controller';

  return {
    address,
    canAppend: (entry, identityProvider) => {
      if(entry.payload.key == 'forbiddenKey') {
        return false;
      }
      else {
        return true;
      }
    }
  };
};

CustomAccessController.type = 'custom';

useAccessController(CustomAccessController);

var ipfs = await create();
var orbitdb = await createOrbitDB({ ipfs });

const db = await orbitdb.open('someUniqIdenifier', {
  type: 'keyvalue',
  AccessController: CustomAccessController(),
});

console.log("db address:", db.address);

await db.put('myFirstKey', 'ok');
try {
  await db.put('forbiddenKey', 'ok');
}
catch {
  console.log('Error ! Forbidden');
}

var allEntries = await db.all();

for (const [key, value] of Object.entries(allEntries)) {
  console.log(key, value);
}

await db.close()
await orbitdb.stop()
await ipfs.stop()

// Malicious code starts here

const MyMaliciousAccessController = () => async ({ orbitdb, identities, address }) => {
  address = '/custom/access-controller';

  return {
    address,
    canAppend: (entry, identityProvider) => {
      return true;
    }
  };
};

MyMaliciousAccessController.type = 'custom';


useAccessController(MyMaliciousAccessController);

var ipfs = await create();
var orbitdb = await createOrbitDB({ ipfs });

const secondDb = await orbitdb.open(db.address, {
  type: 'keyvalue',
  AccessController: MyMaliciousAccessController(),
});

await secondDb.put('forbiddenKey', 'ok');

var allEntries = await secondDb.all();

for (const [key, value] of Object.entries(allEntries)) {
  console.log(key, value);
}

So here I created a custom access controller called CustomAccessController. There is just one rule: every keys are accepted but not forbiddenKey.
It works perfectly.

But if a malicious user wants to add this key, he just needs to clone all the code and set returns true; in the canAppend function of his own malicious access controller.

I thought that the AccessController was immutable once created and that new users are forced to use it. Did I miss something ?

The doc states:

The access information cannot be changed after the initial setup (as it is immutable). If different write access is needed, you will need to set up a new database and associated IPFSAccessController. It is important to note that this will change the address of the database.

Thank you very much for your support and hard work 🙏 I really hope we can find a solution to this issue. I really hope that we can prevent users from editing databases if they use malicious access controller. Otherwise, accesscontrollers would be as good as writing a if condition before a db.put/add :-(

from orbitdb.

MichaelJCole avatar MichaelJCole commented on June 5, 2024

Hi, I was reading through docs/issues today preparing to use OrbitDB and noticed this:

https://github.com/orbitdb/orbitdb/blob/main/docs/ACCESS_CONTROLLERS.md

OrbitDB is bundled with two AccessControllers; IPFSAccessController, an immutable access controller which uses IPFS to store the access settings, and OrbitDBAccessController, a mutable access controller which uses OrbitDB's keyvalue database to store one or more permissions.

I'm very interested in this and thanks for working on it. Two things I noticed:

  1. Are you using the same identity when you make the secondDb? From the code it would seem your user has write access.

  2. Client code in the wild will always be malicious. There's nothing to stop a client from writing malicious data for itself and pushing it out to the network.

The problem would be if a non-malicious client reads and accepts the malicious data.

I haven't dug into this, but it looks like tests have been written around these use cases: https://github.com/orbitdb/orbitdb/blob/main/test/access-controllers/orbit-db-access-controller.test.js#L240

from orbitdb.

shideneyu avatar shideneyu commented on June 5, 2024

Hello
Thanks for participating to this issue
You can copy paste the entire code at #1122 (comment)
Put it into bug.mjs and run node bug.mjs, and you will be able to better understand what is happening. You can even use it to help you write your own code.

Are you using the same identity when you make the secondDb? From the code it would seem your user has write access.

By default, it's as if we use AccessController({ write: ['*'] }), meaning that everyone has write access.
In my code I did not specify any identity. Meaning that the identities are different for the two dbs I guess ?

When you use identities, you can then read the identity in the canAppend function of the access controller to then verify who sent the message.

I haven't dug into this, but it looks like tests have been written around these use cases: https://github.com/orbitdb/orbitdb/blob/main/test/access-controllers/orbit-db-access-controller.test.js#L240

These use cases are unfortunately different from what I want to achieve. grant and revoke authorize identities to write or not into the db.

In my example, everyone can write in the db. But I want to prevent some information to be written.

Actually my real usecase is a bit different, that example was made to make it simple to debug. My real usecase is:

I want each identity to be able to write and update their own message on the same DB. But I don't want other identities to be able to overwrite or delete the messages made by other identities.

I tried to make an example as simple as possible. My real code has custom identity providers, and custom access controllers. I even managed to read the database from the access controller.

But my whole project is void of interest if people can overwrite other's message. I'm not sure why I'm the first to encounter this issue. My usecase is very basic I think.

So in the end, this is exactly the title of this issue: Custom AccessController is not immutable. I hope I am wrong.

from orbitdb.

MichaelJCole avatar MichaelJCole commented on June 5, 2024

Given what I know, I'd implement that as a database of databases. Each user having a database with write access, and some central index of databases. That's more like distributed database-per-user in couchdb/pouchdb than a central mysql server.

I think OpenMLS can support per-message-auth-integrity through groups, but it's a very heavyweight solution, and I don't know an IPFS or javascript implementation.

I'm new here, what do yall think?

from orbitdb.

shideneyu avatar shideneyu commented on June 5, 2024

The more I dig to the rabbit hole, the more I come to the realization that no amount of effort can help us since a user can just monkey patch OrbitDB alltogether to bypass all securities related procedures and grant themselves ever possible rights since we can open a database with our own malicious access controller and identities.

Because the access controllers and identities manifest are published to IPFS but what enforces their usages are not the IPFS nodes but this very library.

If someone monkey patches this everything is over: https://github.com/orbitdb/orbitdb/blob/main/src/orbitdb.js#L127

And I think I finally saw on why just creating a malicious access controller works, it's due to this line here: https://github.com/orbitdb/orbitdb/blob/main/src/access-controllers/index.js#L39

My malicious controller has the same type has the original one. It simply overrides it.

But even if we patch this, nothing prevents a malicious user from polluting or corrupting the database by evading the identity and access controllers rules, by patching their own OrbitDB library or using said exploit I revealed, is that right @haydenyoung ?

In other words I'm afraid that any keyValue database can be deleted or updated by anyone even if their associated manifest has rules that govern their writing rights. Because only front code that can be changed by the user prevents them from opening a database with a different manifest.

from orbitdb.

shideneyu avatar shideneyu commented on June 5, 2024

That is right. Unfortunately for the keyValue database, I don't think that a good peer can know what was the previous "valid" value for a key if it got updated or deleted ?

from orbitdb.

shideneyu avatar shideneyu commented on June 5, 2024

Maybe if we sign every messages and check the oplog. I'll dig this

from orbitdb.

haydenyoung avatar haydenyoung commented on June 5, 2024

@shideneyu can you confirm that firstDb is running the "good" custom AC and secondDB is running the malicious one? I.e. firstDb isn't running the malicious AC?

If it is running correctly, firstDb should be throwing "cannot write" errors when it tries to replicate secondDb's unwanted record.

from orbitdb.

shideneyu avatar shideneyu commented on June 5, 2024

@haydenyoung yes that is the issue. canAppend() is not used when replicating dbs.
firstDb is not throwing "cannot write" errors when it replicates secondDb's unwanted record.

To make it simple, you can reproduce it by running those three files one by one:

node create_with_db1.mjs , node append_with_db2.mjs, node replicate_with_db1.mjs

create_with_db1.mjs , append_with_db2.mjs , replicate_with_db1.mjs

image

from orbitdb.

haydenyoung avatar haydenyoung commented on June 5, 2024

It looks like db1 and db2 are pointing at the same database. Make sure you instantiate two separate ipfs nodes and two separate orbitdb peers by using different paths (I.e. this is how it would look if deployed "in the wild", i.e. two separate nodes on two separate servers/browsers).

I have attached two separate scripts which should give you a better idea how to sync two peers and how peer 1 guarantees it does not write the malicious record using the correct CustomAC.
db1.js.txt
db2.js.txt

Firstly, remove the ".txt" suffix from the db1 and db2 js files (or rename suffix to mjs).

Run db1.js in the terminal:

node db1.js

this will initialize your orbitdb node in a separate directory (./orbitdb/1). It will also print out the database address (will look something like /orbitdb/). Leave db1.js running so that db2.js can connect to it.

Run db2.js in a separate terminal, using db1's address as the address for db2 (I.e. a replicating db):

node db2.js /orbitdb/<hash-that-you-copied-from-db-1>

db2 will successfully write a record to its own log using the malicious AC but db1 will throw a "not allowed to write" error, not allowing the malicious record to be written to db1's log.

Hope this helps.

from orbitdb.

shideneyu avatar shideneyu commented on June 5, 2024

I understand better now, this helps a lot thanks 🙏

Make sure you instantiate two separate ipfs nodes and two separate orbitdb peers by using different paths (I.e. this is how it would look if deployed "in the wild", i.e. two separate nodes on two separate servers/browsers).

My understanding is a bit limited though. In my context I wanted to create a live chat. I did not expect to be hosting nodes since I wanted a decentralized application hosted on IPFS (just plain html / js).

If all of the users messaging each others on that website become peers (ip4/127.0.0.1/tcp/0), if they all close their computers, a new user will not be able to replicate db1 is that right ?

Does this mean that the guy who created db1 with the right Controller Access needs to stay up 24/24 ?
Or does it mean I need to maintain my own IPFS nodes that people can connect to ? (it would become centralized then)

How does your solution works with a scenario where there is no special nodes and that the data is stored on IPFS (maybe with webrtc stars nodes) ?

from orbitdb.

haydenyoung avatar haydenyoung commented on June 5, 2024

If all of the users messaging each others on that website become peers (ip4/127.0.0.1/tcp/0), if they all close their computers, a new user will not be able to replicate db1 is that right ?

Yes, that's the nature of p2p. The peers are providing the data but there's no guarantee that they will be available. Hence, OrbitDB strives to make every replica of the db eventually consistent despite the node unreliability. However, what would be the reasoning for a new user be replicating the chat log when everyone else has left the chat?

Does this mean that the guy who created db1 with the right Controller Access needs to stay up 24/24 ?

Not necessarily. If one of the above peers is available, the new user can dial into one of these peers to replicate the data.

The AC type is retrieved as part of the db manifest (db description). The AC will be the one used by OrbitDB; either one of the bundled IPFS or OrbitDB or a custom AC. In the case of the examples you gave above, this would be the custom AC you have deployed as part of your chat app.

How does your solution works with a scenario where there is no special nodes and that the data is stored on IPFS (maybe with webrtc stars nodes) ?

I'm not entirely sure I understand what "special nodes" are.

Since the db's manifest and entries are stored using IPFS storage I guess you could potentially pin each block remotely but you would still need to re-index the database entries and determine the db heads when replicating on another peer. I've never tried this so I'm not even sure if retrieving data from remotely pinned IPFS blocks is even feasible.

from orbitdb.

haydenyoung avatar haydenyoung commented on June 5, 2024

@shideneyu If you are looking for guidance or would like to discuss developing your decentralized ideas further, I would recommend engaging the OrbitDB community. They may be able to give you more insight regarding implementation of OrbitDB.

from orbitdb.

haydenyoung avatar haydenyoung commented on June 5, 2024

Or does it mean I need to maintain my own IPFS nodes that people can connect to ? (it would become centralized then)

Well I think centralization is a sliding scale. Let's say you have 10 peers and one of them is up all the time. Provided the other peers are not completely reliant on this peer I think all you have is a decentralized app with a really reliable peer.

from orbitdb.

haydenyoung avatar haydenyoung commented on June 5, 2024

@shideneyu I'm going to close this issue as I think it is resolved. However, if you have any more questions, feel free to re-open this issue or pose any questions you have to the community on the OrbitDB Matrix/Gitter channels.

from orbitdb.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.