Netlify-cms: Securing cloudinary credentials

Created on 21 Dec 2018  路  7Comments  路  Source: netlify/netlify-cms

To integrate cloudinary, the official documentation here asks one to put in api_key and cloud_name inside config.yml.

Aren't these secure parameters? IMHO these 2 values should probably come into the application via environment variables and should never be checked in inside the config.yml file.

What are your views on this from a security perspective ?

Most helpful comment

I did some more digging last night and I think the docs would greatly benefit from making it clear, that after adding in your cloud name and api key, you are still required to authenticate to Cloudinary to do anything. When I was testing around with my own key I was obviously logged in to Cloudinary and hence was never presented with an authentication request.

Thanks for helping clarify things @erquhart 馃憤

All 7 comments

cloud_name is definitely not considered secret however api_key is partially secret, though it should be okay if your Cloudinary permissions are set up to disallow unsigned uploads.

Previously I've used Netlify functions + Netlify identity to generate authentication signatures for Cloudinary to allow uploads to my users without them requiring Cloudinary credentials.

It is possible for the config.yml to access Netlify build Environment Variables by building it at deploy time. i.e

config.yml
https://github.com/pdkn/netlify-cms-with-next-js/blob/master/admin/config.yml

media_library:
  name: cloudinary
  config:
    cloud_name: ${CLOUDINARY_CLOUD_NAME}
    api_key: ${CLOUDINARY_API_KEY}

next.config.js
https://github.com/pdkn/netlify-cms-with-next-js/blob/master/next.config.js

module.exports = {
  exportPathMap: async function (defaultPathMap, { dev, dir, outDir, distDir, buildId }) {

    await copyFolder(join(dir, 'admin'), join(outDir, 'admin'))

    let cmsConfigFile = join(outDir, 'admin/config.yml')
    let contents = await readFile(cmsConfigFile, 'utf8')
    let replaced_contents = contents
      .replace('${CLOUDINARY_CLOUD_NAME}', process.env.CLOUDINARY_CLOUD_NAME)
      .replace('${CLOUDINARY_API_KEY}', process.env.CLOUDINARY_API_KEY)

    let tmpfile = `${cmsConfigFile}.jstmpreplace`
    await writeFile(tmpfile, replaced_contents, 'utf8')
    await rename(tmpfile, cmsConfigFile)


    return defaultPathMap
  },
};

However, the config.yml will still be on the server so some extra authentication will be required to prevent access.

While yes unsigned uploads aren't enabled by default, and you can't enable it/create one without the api_secret, there is still nothing stopping me from setting up the Media Library Widget with someone else's credentials--exactly the same way Netlify CMS does.

Now I can upload any content I want to an account under your name or run as many transformations as possible possibly costing you money for example.

Granted this is a combination of Netlify making the config details publicly available and Cloudinary only needed those to use the widget.

Changing the keys to an environmental variable only helps any publicly Git repos--I can still go to your website and visit /admin/config.yml to get your config file with the details.

@mikhailbot I believe your concerns are predicated on the API secret being made available in the Netlify CMS config.yml. This is not the case. Only the cloud name and api key are. These details alone provide no access whatsoever to any aspect of a user's Cloudinary cloud or account.

@kishaningithub regarding your initial post here, @levibuzolic summed it up well - these parameters aren't really secret. Netlify CMS aside, the Cloudinary media library widget itself requires that these values be exposed client side as they are required during initialization. Without the API secret or an authorized username/password for the cloud in question, there's no access.

If you don't like storing these details in a plain yaml file, you can pass them direct to the CMS at runtime via manual initialization. Configuration passed in this way has the config.yml object merged over it, so you can pass in just the media library details this way and let the rest of your config remain in config.yml.

Closing this, but feel free to ask more questions here or in community chat.

I did some more digging last night and I think the docs would greatly benefit from making it clear, that after adding in your cloud name and api key, you are still required to authenticate to Cloudinary to do anything. When I was testing around with my own key I was obviously logged in to Cloudinary and hence was never presented with an authentication request.

Thanks for helping clarify things @erquhart 馃憤

Definitely agreed, documentation reflecting that would help. I'll put it on the list, but if you're up for opening a PR we'll take it!

Was this page helpful?
0 / 5 - 0 ratings