Encrypting data
Having just finished adding attr_encrypted to some models, I've come to think that a hackers job might not be that hard.
I've got a database on a different server to the app servers - however, chances are that if someone managed to get on to the db server, that person could also access the app server where the keys are stored (perhaps that assumption is incorrect) as they have the same type of security measures.
Issue
Rails code is stored in a readable text format on the server, therefore the secret keys can be accessed. Surely if someone did get a hold of the database, and a hold of those keys the entire encryption of data becomes irrelevant as it simply (slightly) prolongs the hackers time to decrypt information.
If so, are there further security measure that can be taken, or have I completely missed the concept of encryption?
I've had a look around the attr_encrypted gem and associated readme and questions but could not find anything useful.
attr_encrypted protects your data from simple data leaks. Think NSA sniffing an inter-DC cable (where your db replication logs will be sent over) or disgruntled DBA (not having access to app source) dumping all your data into the internets.
If it is actual breach, intruders might not get access to both app code and database (depends on your architecture and security measures). If they have both, then yes, it's game over.
All in all, it's better to have it [for sensitive data] than not to have. Doesn't hurt, I'd say.
Don't put the keys in the app server and don't check them in to the git repository.
Instead, use environment variables. You can have different approaches for dev and production.
Very easy to do on Heroku (for example).
I have an encryption concern:
# app/models/concerns/encryption.rb
module Encryption
extend ActiveSupport::Concern
module ClassMethods
def encryption_key
ENV['ENCRYPT_KEY']
end
end
end
In the model, I do this
class User < ActiveRecord::Base
include Encryption
attr_encrypted :name, :key => encryption_key
end
In development, I use a .env file to store keys and retrieve with foreman.
Related
I use Heroku to deploy a Rails app. I store sensitive data such as API keys and passwords in Heroku's environment variables, and then use the data in rake tasks that utilize various APIs.
I am just wondering how secure Heroku's environmental variables are? Is there a way to hash these variables while retaining the ability to use them in the background somehow?
I came across a previous thread here: Is it secure to store passwords as environment variables (rather than as plain text) in config files?.
But it doesn't quite cover instances when I still need to unhashed password to perform important background tasks.
Several things (mostly my opinion):
--
1. API Key != Password
When you talk about API Keys, you're talking about a public token which is generally already very secure. The nature of API's nowadays is they need some sort of prior authentication (either at app or user level) to create a more robust level of security.
I would firstly ensure what type of data you're storing in the ENV variables. If it's pure passwords (for email etc), perhaps consider migrating your setup to one of the cloud providers (SendGrid / Mandrill etc), allowing you to use only API keys
The beauty of API keys is they can be changed whilst not affecting the base account, as well as limiting interactivity to the constrains of the API. Passwords affect the base account
--
2. ENV Vars are OS-level
They are part of the operating environment in which a process runs.
For example, a running process can query the value of the TEMP
environment variable to discover a suitable location to store
temporary files, or the HOME or USERPROFILE variable to find the
directory structure owned by the user running the process.
You must remember Environment Variables basically mean you store the data in the environment you're operating. The generally means the "OS", but can be the virtual instance of an OS too, if required.
The bottom line is your ENV vars are present in the core of your server. The same way as text files would be sitting in a directory on the hard drive - Environment Variables reside in the core of the OS
Unless you received a hack to the server itself, it would be very difficult to get the ENV variable data pro-grammatically, at least in my experience.
What are you looking for? Security against who or what?
Every piece of information store in a config file or the ENV is readable to everyone who has access to the server. And even more important, every gem can read the information and send it somewhere.
You can not encrypt the information, because then you need to store the key to decrypt somewhere. Same problem.
IMO both – environment variables and config files – are secure as long you can trust everyone that has access to your servers and you carefully reviewed the source code of all libraries and gems you have bundled with your app.
I have been looking at attr_encrypted, but it stores the keys in the code, which doesn't seem to be so secure. If my webserver gets breached, the encryption won't help much.
What are some options for which the data could still be secure if my webserver gets breached?
The keys have to be in memory in order to perform encryption and decryption. If your server is breached, you're pooched no matter how you store your keys; once an attacker has access to your local machine, all bets are off. If your app can decrypt data, they'll be able to, as well, depending on how much effort they're willing to spend to extract those keys.
attr_encrypted will protect you against things like SQL injections resulting in sensitive data being leaked, but if your app is compromised, then your attacker can get to anything your app can get to.
To make it harder, though, you can use password-encrypted keys. You can store your encrypted key on disk, and then when you deploy your app (or otherwise start it up), you will provide your password to decrypt the key into memory, then throw the password away. This still can't protect you against someone grepping through your process's memory, and it means that you have to have human interaction every time your app needs to start up (which could be a problem with automated monitoring) but it would substantially increase the difficulty of (but not prevent) extracting encryption keys from a compromised machine.
I use environment variables to set the keys. It's easy to have different approaches for dev or production.
An encryption concern:
# app/models/concerns/encryption.rb
module Encryption
extend ActiveSupport::Concern
module ClassMethods
def encryption_key
ENV['ENCRYPT_KEY']
end
end
end
In the model, use the concern's method.
class User < ActiveRecord::Base
include Encryption
attr_encrypted :name, :key => encryption_key
end
In development, use an .env file to store keys and retrieve environment variables with foreman. Heroku provides an easy way to set environment variables from the dashboard.
I run a live RoR (Rails 3.21.11) application on Heroku that contains some sensitive (personally-identifiable) information that we'd like to cache out (~80kb of JSON on a per-user basis).
Since we run on Heroku, we obviously trust Heroku with this data.
However, to use memcached, we need to use a Heroku addon, such as Memcachier.
The business problem: we are not willing to put this sensitive information on a third-party provider's infrastructure unless it is symmetrically encrypted on the way out.
Of course, I can do this:
value = encrypt_this(sensitive_value)
Rails.cache.write('key', value)
But we envisiage a future in which ActiveRecord objects, as well as good ol' JSON, will be stored -- so we need every bit of data going out to be automatically encrypted, and we don't want to have to write an encryption line into every bit of code that might want to use the cache.
Are there any gems/projects/tools to do this?
Although I haven't had a chance to use this yet the attr_encrypted library might get you some or all of the way there.
I have an application with user 'Notifications' think SO or facebook or twitter. However, as notifications won't necessarily change on every page view I decided to save them in memcached.
def get_notification
if current_user
mc = Dalli::Client.new('localhost:11211')
require_dependency 'notification.rb'
#new_notification = mc.get(current_user.id.to_s+'new_notification')
if #new_notification == nil
#new_notification = Notification.getNew(current_user.id)
mc.set(current_user.id.to_s+'notification',#new_notification)
end
end
end
I overlooked the obvious flaw in this implementation. Once the notifications are loaded they would never be refreshed until the user logs out or the cache entry expires. One way to do this is to negate the user's cache entry when a event for a new notification occurs. This would force a new request to the db. Is there any other way to implement this?
Currently you are manually connecting to Memchaced, check if key exists, store content, expire it. But as you may notice this gets tedious & repetitive very soon.
However, Rails Provides you with few patterns that you can use to accomplish this but more easily.
First using Cache Stores option you can instruct rails to use Memchached
config.cache_store = :mem_cache_store, "example.com"
This cache store uses memcached server to provide a
centralized cache for your application. Rails uses the bundled dalli
gem by default. This is currently the most popular cache store for
production websites. It can be used to provide a single, shared cache
cluster with very a high performance and redundancy.
When initializing the cache, you need to specify the addresses for all
memcached servers in your cluster. If none is specified, it will
assume memcached is running on the local host on the default port, but
this is not an ideal set up for larger sites.
The write and fetch methods on this cache accept two additional
options that take advantage of features specific to memcached. You can
specify :raw to send a value directly to the server with no
serialization. The value must be a string or number. You can use
memcached direct operation like increment and decrement only on raw
values. You can also specify :unless_exist if you don't want memcached
to overwrite an existing entry.
Using rails Cache store instead of directly using Dalli allows you to use the following Nicer API
Rails.cache.read('key')
Rails.cache.write('key', value)
Rails.cache.fetch('key') { value }
Now, rails for actually caching. you can use Declarative Etags or Fragment Caching to cache the notifications. here is an example using Declarative Etags
def get_notification
if current_user
#new_notification = Notification.getNew(current_user.id)
end
refresh_when #new_notification
end
Now the way declarative E-tags works is Template is not rendered when request
sends a matching ETag & cache copy is sent. However, when #new_notification changes the E-tag value will change too. Thus causing the cache to expire. Now, Caching is a vast topic to cover & there are variously techniques to do it. so probally I won't give you a full answers but I would point to the following resources so you can learn more:
Caching with Rail
Rails 4: Zombie Outlaws Course
Rails Cache for dummies
Caching Strategies for Rails
Happy Caching ;-)
I am trying to hide 2 secrets that I am using in one of my apps.
As I understand the keychain is a good place but I can not add them before I submit the app.
I thought about this scenario -
Pre seed the secrets in my app's CoreData Database by spreading them in other entities to obscure them. (I already have a seed DB in that app).
As the app launches for the first time, generate and move the keys to the keychain.
Delete the records from CoreData.
Is that safe or can the hacker see this happening and get those keys?
*THIRD EDIT**
Sorry for not explaining this scenario from the beginning - The App has many levels, each level contains files (audio, video, images). The user can purchase a level (IAP) and after the purchase is completed I need to download the files to his device.
For iOS6 the files are stored with Apple new "Hosted Content" feature. For iOS5 the files are stored in amazon S3.
So in all this process I have 2 keys:
1. IAP key, for verifying the purchase at Apple IAP.
2. S3 keys, for getting the files from S3 for iOS5 users:
NSString *secretAccessKey = #"xxxxxxxxx";
NSString *accessKey = #"xxxxxxxxx";
Do I need to protect those keys at all? I am afraid that people will be able to get the files from S3 with out purchasing the levels. Or that hackers will be able to build a hacked version with all the levels pre-downloaded inside.
Let me try to break down your question to multiple subquestions/assumption:
Assumptions:
a) Keychain is safe place
Actually, it's not that safe. If your application is installed on jailbroked device, a hacker will be able to get your keys from the keychain
Questions:
a) Is there a way to put some key into an app (binary which is delivered form AppStore) and be completely secure?
Short answer is NO. As soon as there is something in your binary, it could be reverse engineered.
b) Will obfuscation help?
Yes. It will increase time for a hacker to figure it out. If the keys which you have in app will "cost" less than a time spend on reverse engineering - generally speaking, you are good.
However, in most cases, security through obscurity is bad practice, It gives you a feeling that you are secure, but you aren't.
So, this could be one of security measures, but you need to have other security measures in place too.
c) What should I do in such case?*
It's hard to give you a good solution without knowing background what you are trying to do.
As example, why everybody should have access to the same Amazon S3? Do they need to read-only or write (as pointed out by Kendall Helmstetter Gein).
I believe one of the most secure scenarios would be something like that:
Your application should be passcode protected
First time you enter your application it requests a user to authenticate (enter his username, password) to the server
This authenticates against your server or other authentication provider (e.g. Google)
The server sends some authentication token to a device (quite often it's some type of cookie).
You encrypt this token based on hash of your application passcode and save it in keychain in this form
And now you can do one of two things:
hand over specific keys from the server to the client (so each client will have their own keys) and encrypt them with the hash of your application passcode
handle all operation with S3 on the server (and require client to send)
This way your protect from multiple possible attacks.
c) Whoooa.... I don't plan to implement all of this stuff which you just wrote, because it will take me months. Is there anything simpler?
I think it would be useful, if you have one set of keys per client.
If even this is too much then download encrypted keys from the server and save them in encrypted form on the device and have decryption key hardcoded into your app. I would say it's minimally invasive and at least your binary doesn't have keys in it.
P.S. Both Kendall and Rob are right.
Update 1 (based on new info)
First of all, have you seen in app purchase programming guide.
There is very good drawing under Server Product Model. This model protects against somebody who didn't buy new levels. There will be no amazon keys embedded in your application and your server side will hand over levels when it will receive receipt of purchase.
There is no perfect solution to protect against somebody who purchased the content (and decided to rip it off from your application), because at the end of days your application will have the content downloaded to a device and will need it in plain (unencrypted form) at some point of time.
If you are really concerned about this case, I would recommend to encrypt all your assets and hand over it in encrypted form from the server together with encryption key. Encryption key should be generated per client and asset should be encrypted using it.
This won't stop any advanced hacker, but at least it will protect from somebody using iExplorer and just copying files (since they will be encrypted).
Update 2
One more thing regarding update 1. You should store files unencrypted and store encryption key somewhere (e.g. in keychain).
In case your game requires internet connection, the best idea is to not store encryption key on the device at all. You can get it from the server each time when your app is started.
DO NOT store an S3 key used for write in your app! In short order someone sniffing traffic will see the write call to S3, in shorter order they will find that key and do whatever they like.
The ONLY way an application can write content to S3 with any degree of security is by going through a server you control.
If it's a key used for read-only use, meaning your S3 cannot be read publicly but the key can be used for read-only access with no ability to write, then you could embed it in the application but anyone wanting to can pull it out.
To lightly obscure pre-loaded sensitive data you could encrypt it in a file and the app can read it in to memory and decrypt before storing in the keychain. Again, someone will be able to get to these keys so it better not matter much if they can.
Edit:
Based on new information you are probably better off just embedding the secrets in code. Using a tool like iExplorer a causal user can easily get to a core data database or anything else in your application bundle, but object files are somewhat encrypted. If they have a jailbroken device they can easily get the un-encrypted versions but it still can be hard to find meaningful strings, perhaps store them in two parts and re-assemble in code.
Again it will not stop a determined hacker but it's enough to keep most people out.
You might want to also add some code that would attempt to ask your server if there's any override secrets it can download. That way if the secrets are leaked you could quickly react to it by changing the secrets used for your app, while shutting out anyone using a copied secret. To start with there would be no override to download. You don't want to have to wait for an application update to be able to use new keys.
There is no good way to hide a secret in a piece of code you send your attacker. As with most things of this type, you need to focus more on how to mitigate the problem when the key does leak rather than spend unbounded time trying to protect it. For instance, generating different keys for each user allows you to disable a key if it is being used abusively. Or working through a intermediary server allows you to control the protocol (i.e. the server has the key and is only willing to do certain things with it).
It is not a waste of time to do a little obfuscating. That's fine. But don't spend a lot of time on it. If it's in the program and it's highly valuable, then it will be hacked out. Focus on how to detect when that happens, and how to recover when it does. And as much as possible, move that kind of sensitive data into some other server that you control.