Keeping Private Data Out of Your Code: Setting Up a Twitter_ebooks Bot Part 2
Keep Oauth Secret. Keep It Safe #
This post is sort of a follow-up to Joel McCoy's awesome post How To Make an _ebooks Bot, in which he details the basic usage & setup of the twitter_ebooks library. twitter_ebooks is a great framework for making Twitter bots of various kinds that can include interactive features and everything, not just the thing that assembles Markov-chained text out of a corpus of tweets, so I'd encourage people to check out the library, learn a little bit of basic string manipulation in Ruby, and let your imagination go wild!
Joel's post is great, but near the end of it, he describes putting some sensitive information into your bot's code— the pairs of keys & secrets that make it possible for someone to authenticate themselves as a particular application + twitter account with the Twitter API. Given these four strings, anyone could impersonate your application and/or post to the account you set up for your bot. Now, I understand why he wrote about doing this— it keeps the post simpler (just, uh, scroll down), and if all you do is write the code and then run the bot from your computer, your webhost, or something like Heroku, it's probably fine... if someone is able to access your bot's code from there you probably have bigger issues to worry about. Nowadays it just seems like second nature, though, for people to upload code from their hobby projects to Github as a public project, and that's where the problem comes in— suddenly your OAuth keys are just sitting there in the open, waiting to get picked up by someone and used against your will.
Green with ENV[] #
It's possible that you're not familiar with the ENV[] hash in Ruby, but maybe you've heard of environment variables in *nix, Mac OS X, or even Windows. Environment variables are basically just key/value pairs that are available to a running process in the operating system. Some are loaded in by the operating system via the shell which most processes are invoked from, some variables maybe you put into a file like .bashrc
or .profile
with the export KEY='value'
syntax or similar... you can even just set environment variables on the command line for the scope of a command by typing them out as KEY=value
before the command; if you've ever seen people work with Ruby on Rails you might have seen this in doing things like RAILS_ENV=production script/rails console
. And in Ruby, just like a lot of programming languages, the environment variables are available to you in a simple hash. In the command shown above, you could find the string production
in the variable ENV['RAILS_ENV']
. The commonly used PATH
environment variable could be found at ENV['PATH']
. Pretty simple!
So in order to hide our sensitive information from our Github profile, we want to substitute the hardcoded keys out for environment variables, and then inject the values back into the program via environment variables in a way that is suitable for wherever we're running our bot. The relevant portion of the code from Joel's original post should go from looking like this:
CONSUMER_KEY = "1xfGiiMY_API_KEYiiGfx1"
CONSUMER_SECRET = "1xfGiiMY_API_SECRETiiGfx1"
OAUTH_TOKEN = "1xfGiiMY_ACCESS_TOKENiiGfx1"
OAUTH_TOKEN_SECRET = "1xfGiiMY_ACCESS_TOKEN_SECRETiiGfx1"
to this:
CONSUMER_KEY = ENV['TWITTER_CONSUMER_KEY']
CONSUMER_SECRET = ENV['TWITTER_CONSUMER_SECRET']
OAUTH_TOKEN = ENV['TWITTER_OAUTH_TOKEN']
OAUTH_TOKEN_SECRET = ENV['TWITTER_TOKEN_SECRET']
Local variables #
When you make a bot, the first place you usually run it will be on the command line on your computer. You don't want to have to type the variables out in front of the command every time, so it might make sense to put them in a file somewhere. You want the file to be local to just that one bot, though, because once you're a master of Twitter bots and churning out a bot every day, you don't want to cross the streams and post content from one bot to another bot's account. Just imagine all the Klout you'd lose!
Luckily, the twitter_ebooks gem is already on the right path to helping you with this. You may have learned that you can start your bot via bundle exec foreman start
— you may have even gone out and learned that the foreman
gem looks for the Procfile
generated by the twitter_ebooks template generator in order to figure out what commands to run when you start it up that way. What you may not have learned, though, is that Foreman also loads extra environment variables in from a file called .env
in the current working directory. So just open a text editor and write key/value pairs into a file called .env
in the root of your bot repo, something like this:
TWITTER_CONSUMER_KEY="1xfGiiMY_API_KEYiiGfx1"
TWITTER_CONSUMER_SECRET="1xfGiiMY_API_SECRETiiGfx1"
TWITTER_OAUTH_TOKEN="1xfGiiMY_ACCESS_TOKENiiGfx1"
TWITTER_OAUTH_SECRET="1xfGiiMY_ACCESS_TOKEN_SECRETiiGfx1"
Then, and this is important, add .env
to your .gitignore
file and add/commit the .gitignore
to make sure that you never upload the .env
file to remote git repositories. If you have a template that you copy for new Ruby projects' .gitignore
files, I'd recommend going and adding .env
to it right now— environment variables are extremely useful in Ruby, and you can even use the .env
file outside of Foreman using the more lightweight "dotenv" gem if you don't want to use Foreman for some reason.
That's it! When you run your program through foreman, it should know pick up the keys and insert them into your code. Then you're in good shape!
Heroku variables #
Heroku has a special way of storing "config variables", which are basically just key/value pairs that get loaded in as environment variables when it spins up a new environment to run your program in. It's pretty well documented on Heroku's website, but to sum it up simply, you want to run these commands on the command line once you've got your heroku project connected up to the directory where your bot's code resides:
heroku config:set TWITTER_CONSUMER_KEY=1xfGiiMY_API_KEYiiGfx1
heroku config:set TWITTER_CONSUMER_SECRET=1xfGiiMY_API_SECRETiiGfx1
heroku config:set TWITTER_OAUTH_TOKEN=1xfGiiMY_ACCESS_TOKENiiGfx1
heroku config:set TWITTER_OAUTH_SECRET=1xfGiiMY_ACCESS_TOKEN_SECRETiiGfx1
Don't Repeat Myself #
Let's put the last two sections together: you may have realized that setting those config variables manually when you've got them stored in your .env
files is kinda silly. So what is there to do about that? Well, luckily someone wrote a Heroku plugin that lets you keep a Heroku app's config vars in sync with a local .env
file. You can find the plugin on Github, and the directions in the README are pretty straightforward:
- Run
heroku plugins:install git://github.com/ddollar/heroku-config.git
2) Runheroku config:push
to take the contents of your .env file and push them to Heroku. (You can also "pull" the variables from Heroku using the same plugin; check out the documentation on Github if you think that sounds helpful)
So now you should have a SECRETS-FREE git repository that you can safely upload to Github or your public Git hosting site of choice, and people can even use & adapt your code easily by just switching out the .env
file & Heroku config variables! How great is that?
- Previous: Backtracks
- Next: 2014: What I Did