Skip to content

Commit

Permalink
Merge pull request #484 from ipfs-shipyard/feat/securing-window-ipfs
Browse files Browse the repository at this point in the history
Improving security of window.ipfs (low hanging fruits)
  • Loading branch information
lidel committed May 23, 2018
2 parents cdfc775 + 6c4c51f commit 41e62df
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 1 deletion.
6 changes: 5 additions & 1 deletion add-on/src/contentScripts/ipfs-proxy/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ function init () {
injectScript(code)
}

init()
// Restricting window.ipfs to Secure Context
// See: https://github.com/ipfs-shipyard/ipfs-companion/issues/476
if (window.isSecureContext) {
init()
}
20 changes: 20 additions & 0 deletions add-on/src/lib/ipfs-proxy/api-whitelist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
"add",
"block",
"cat",
"dag",
"dht",
"dns",
"files",
"get",
"id",
"ls",
"name.resolve",
"object",
"pin",
"pubsub",
"repo",
"stats",
"swarm",
"version"
]
2 changes: 2 additions & 0 deletions add-on/src/lib/ipfs-proxy/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
const browser = require('webextension-polyfill')
const { createProxyServer, closeProxyServer } = require('ipfs-postmsg-proxy')
const AccessControl = require('./access-control')
const createPreApiWhitelist = require('./pre-api-whitelist')
const createPreAcl = require('./pre-acl')
const createPreMfsScope = require('./pre-mfs-scope')
const createRequestAccess = require('./request-access')
Expand Down Expand Up @@ -33,6 +34,7 @@ function createIpfsProxy (getIpfs, getState) {
postMessage: (data) => port.postMessage(data),
getMessageData: (d) => d,
pre: (fnName) => [
createPreApiWhitelist(fnName),
createPreAcl(fnName, getState, getScope, accessControl, requestAccess),
createPreMfsScope(fnName, getScope, getIpfs)
]
Expand Down
24 changes: 24 additions & 0 deletions add-on/src/lib/ipfs-proxy/pre-api-whitelist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Some APIs are too sensitive to be exposed to dapps on every website
// We follow a safe security practice of denying everything and allowing access
// to a pre-approved list of known APIs. This way if a new API is added
// it will be blocked by default, until it is explicitly enabled below.
const API_WHITELIST = Object.freeze(require('./api-whitelist.json'))

// Creates a "pre" function that is called prior to calling a real function
// on the IPFS instance. It will throw if access is denied due to API not being whitelisted
function createPreApiWhitelist (permission) {
return async (...args) => {
// Fail fast if API or namespace is not explicitly whitelisted
const permRoot = permission.split('.')[0]
if (!(API_WHITELIST.includes(permRoot) || API_WHITELIST.includes(permission))) {
console.log(`[ipfs-companion] Access to ${permission} API over window.ipfs is blocked. If you feel it should be allowed, open an issue at https://github.com/ipfs-shipyard/ipfs-companion/issues/new`)
const err = new Error(`Access to ${permission} API is globally blocked for window.ipfs`)
err.output = { payload: { isIpfsProxyWhitelistError: true, permission } }
throw err
}

return args
}
}

module.exports = createPreApiWhitelist
30 changes: 30 additions & 0 deletions test/functional/lib/ipfs-proxy/pre-api-whitelist.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict'
const { describe, it, before, after } = require('mocha')
const { expect } = require('chai')
const { URL } = require('url')
const createPreApiWhitelist = require('../../../../add-on/src/lib/ipfs-proxy/pre-api-whitelist')

describe('lib/ipfs-proxy/pre-api-whitelist', () => {
before(() => {
global.URL = URL
})

it('should throw early if access was not enabled via global API whitelist', async () => {
const permission = 'config.show'
const preApiWhitelist = createPreApiWhitelist(permission)

let error

try {
await preApiWhitelist()
} catch (err) {
error = err
}

expect(() => { if (error) throw error }).to.throw(`Access to ${permission} API is globally blocked for window.ipfs`)
})

after(() => {
delete global.URL
})
})

0 comments on commit 41e62df

Please sign in to comment.