I'm building a system that need to collect some user sensitive data via secured web connection, store it securely on the server for later automated decryption and reuse. System should also allow user to view some part of the secured data (e.g., *****ze) and/or change it completely via web. System should provide reasonable level of security.
I was thinking of the following infrastructure:
App (Web) Server 1
Web server with proper TLS support
for secured web connections.
Use public-key algorithm (e.g. RSA) to
encrypt entered user sensitive data
and send it to App Server 2 via
one-way outbound secured channel
(e.g. ssh-2) without storing it
anywhere on either App Server 1 or DB
Server 1.
Use user-password-dependent
symmetric-key algorithm to encrypt
some part of the entered data (e.g.
last few letters/digits) and store
it on the DB Server 1 for later
retrieval by App Server 1 during
user web session.
Re-use step 2 for data modification by user via web.
DB Server 1
Store unsecured non-sensitive user
data.
Store some part of the sensitive
user data encrypted on App Server 1
(see step 3 above)
App Server 2
Do NOT EVER send anything
TO App Server 1 or DB Server 1.
Receive encrypted user sensitive
data from App Server 1 and store it
in DB Server 2.
Retrieve encrypted
user sensitive data from DB Server 2
according to the local schedules,
decrypt it using private key
(see App Server 1, step 2) stored
locally on App Server 2 with proper key management.
DB Server 2
Store encrypted user sensitive data (see App Server 2, step 2)
If either App (Web) Server 1 or DB Server 1 or both are compromised then attacker will not be able to get any user sensitive data (either encrypted or not). All attacker will have is access to public-key and encryption algorithms which are well known anyway. Attacker will however be able to modify web-server to get currently logged users passwords in plaintext and decrypt part of user sensitive data stored in DB Server 1 (see App Server 1, step 3) which I don't consider as a big deal. Attacker will be able to (via code modification) also intercept user sensitive data entered by users via web during potential attack. Later I consider as a higher risk, but provided that it is hard (is it?) for attacker to modify code without someone noticing I guess I shouldn't worry much about it.
If App Server 2 and private key are compromised then attacker will have access to everything, but App Server 2 or DB Server 2 are not web facing so it shouldn't be a problem.
How secure is this architecture? Is my understanding of how encryption algorithms and secured protocols work correct?
Thank you!
I don't think I can give a proper response because I'm not sure the goal of your system is clear. While I appreciate you getting feedback on a design, it's a bit hard without a purpose.
I would suggest to you this though:
Strongly document and analyse your threat model first
You need to come up with a fixed hard-lined list of all possible attack scenarios. Local attackers, etc, who are you trying to protect against? You also say things like 'with proper key management'; yet this is one of the hardest things to do. So don't just assume you can get this right; fully plan out how you will do this, with specific linking to who it will prevent attacks by.
The reason you need to do a threat model, is that you will need to determine on what angles you will be vulnerable; because this will be the case.
I will also suggest that while the theory is good; in crypto implementation is also very critical. Do not just assume that you will do things correctly, you really need to take care as to where random numbers come from, and other such things.
I know this is a bit vague, but I do think that at least coming up with formal and strong threat model, will be very helpful for you.
So far so good. You are well on your way to a very secure architecture. There are other concerns, such as firewalls, password policies, logging, monitoring and alerting to consider, but everything you described so far is very solid. If the data is sensitive enough, consider a third party audit of your security.
I would not recommend using any form of public key to communicate from your web server to your app server. If you control both system just a regular secret system of encryption. You know the identity of your app server, so keeping the key secure is not an issue. If you ever need to change or update the secret key just do so manually to prevent it from leaking across a connection.
What I would be most careful about is direction of data transfer from your server in your DMZ, which should only be your webserver, to those boxes residing internally to your network. It is becoming increasingly common for legitimate domains to be compromised to distribute malware to visiting users. That is bad, but if the malware were to turn in ward to your network instead of only outward to your users then your business would be completely hosed.
I also did not see anything about preventing sql injection or system hardening/patching to prevent malware distribution. This should be your first and most important consideration. If security were important to you then you would be your architecture to be flexible to minor customizations of inter-server communication and frequent patching. Most websites, even major legitimate businesses, never fix their security holes even if they are compromised. You must be continually fixing security holes and changing things to prevent holes from arise if you wish to avoid being compromised in the first place.
To prevent becoming a malware distributor I would suggest making hard and fast rules upon how media is served that contains any sort of client-side scripting. Client-side scripting can be found in JavaScript, ActiveX, Flash, Acrobat, Silverlight, and other code or plugin that executes on the client system. Policies for serving that content must exist so that anomolous code fragments can be immediately identified. My recommendation is to NEVER embed client-side code directly into a browser, but always reference some external file. I would also suggest conslidating like minded media to give you better asset control and save you bandwidth, such as serving one large JavaScript file instead of 8 small ones. I would also recommend forcing all such media onto an external content distribution system that references your domain in its directory structure. That way media is not served from your servers directly and if it served from you directly you can quickly identify it as potentially malicious and necessittating a security review.
Related
I am creating a Chromium/Electron based Mac app. The app is essentially a browser for my customers to use a web service that I have no control over. My requirement is that users of my app (who may have root access on their Mac) should not be able to view the URLs the app is visiting, and should be unable to gain access to the cookies the app is storing. Normally it is not hard to MITM yourself, or attach a debugger to an app and dump memory to see the URLs and cookies.
How can I prevent these types of leaks to the user? If it's impossible, it may be acceptable to make it very hard so that a very high level of sophistication is needed.
Your users have full control of their devices, it is not possible to securely prevent them from proxying or exploring what your client-side app does. Obfuscation would seem like an option, but in the end, the http request that leaves your app will traverse the whole OS through different layers, and your user can easily observe that, if not else then in network packets (but usually much easier).
The only way it is possible to prevent the user from knowing what's happening is if you have your own backend. The frontend app (Electron) would make a request to your backend, which in turn could make any request with any parameters without the user being aware.
Note though that your backend could still be used as a proxy or oracle just like if the user was connecting to the real service. This might or might not be a problem in your case, depending on what you actually want to achieve and why.
The app is essentially a browser for my customers to use a web service that I have no control over. My requirement is that users of my app (who may have root access on their Mac) should not be able to view the URLs the app is visiting, and should be unable to gain access to the cookies the app is storing
Basically, you cannot (you could with the appropriate infrastructure. But you lack that infrastructure).
Network communications can be secured, to a point, using HTTPS (if you can't even use that, then you're completely out of luck - users wouldn't even need root access to the Mac to sniff traffic). You need to verify the server certificate to be sure you're connecting to the correct server.
One thing you might do - effectual just against wannabes, I'm afraid - is first run a test API call on some random server and verify that the connection either fully succeeds, with the proper server identification and matching IP, if the server exists, or that it properly fails if the server never existed. Anything else would be a telltale that someone has taken over the network layer, and at that point you could connect to a different server, making different calls, and lament that the server isn't answering properly.
Strings in memory can be (air quote) protected (end air quote) by having them available only for the shortest time, and otherwise stored in a different form - you can have for example an URL and a random byte sequence with the same length, then store the sequence and the XOR of the URL and the sequence. You can then reconstruct the URL every time you need it, remembering to clear it off any app caches it might find its way into. Also, just for the lols, you can keep a baker's dozen of different URLs sprinkled in the clear throughout the code. A memory dump at that point will turn out nothing useful.
Files, of course, can be encrypted with any one of several schemes - the files residing on the same machine that has to know how to decode them makes all such schemes ultimately vulnerable, but there again, you can try and obfuscate things. I once stored some information in a ZIP file - but it was just the header of an encrypted ZIP file, with the appropriate directory entry block glued at the end. The data were actually just gzipped in the clear, there was no password whatsoever. The guys that tried to decode the file thought it was a plain encrypted Zip file with the extension changed, wasted a significant amount of time with several Zip cracking tools, and ended up owing me a beer.
More than that, there is not much that can realistically be done.
A big advantage would be in outsourcing the API calls and "cookie" maintenance to an external service that you control, e.g. on Amazon AWS or Azure or similar. Then you could employ all kinds of protection schemes (for example: all outbound API calls could be stored in an opaque object, timestamped, nonced, and encrypted with your server's public key, and the responses sent encrypted with your client's unique key). Since this is relatively simple and cost-effective, it would also be my recommendation.
UPDATE: I have concluded my research on this problem and posted a lengthy blog entry explaining my findings: The Unspoken Vulnerability of JWTs. I explain how the big push to use JWTs for local authentication is leaving out one crucial detail: that the signing key must be protected. I also explain that unless you're willing to go to great lengths to protect the keys, you're better off either delegating authentication via Oauth or using traditional session IDs.
I have seen much discussion of the security of JSON Web Tokens -- replay, revocation, data transparency, token-specified alg, token encryption, XSS, CSRF -- but I've not seen any assessment of the risk imposed by relying on a signing key.
If someone breaches a server and acquires a JWT signing key, it seems to me that this person could thereafter use the key to forge unexpired JWTs and secretly gain access. Of course, a server could look up each JWT on each request to confirm its validity, but servers use JWTs exactly so they don't have to do this. The server could confirm the IP address, but that also involves a lookup if the JWT is not to be trusted, and apparently doing this precludes reliable mobile access anyway.
Contrast this with a breach of a server based on session IDs. If this server is hashing passwords, the attacker would have to snag and use a session ID separately for each user before it expires. If the server were only storing hashes of the session IDs, the attacker would have to write to the server to ensure access. Regardless, it seems that the attacker is less advantaged.
I have found one architecture that uses JWTs without this disadvantage. A reverse proxy sits between untrusted clients externally and a backend collection of microservices internally, described here by Nordic APIs. A client acquires an opaque token from an authorization server and uses that token to communicate with the server app for all requests. For each request, the proxy translates the opaque token into a JWT and caches their association. The external world never provides JWTs, limiting the damage wrought by stealing keys (because the proxy goes to the authentication server to confirm the opaque tokens). However, this approach requires dereferencing each client token just as session IDs require per-request dereferencing, eliminating the benefit of JWTs for client requests. In this case, JWTs just allow services to pass user data among themselves without having to fully trust one another -- but I'm still trying to understand the value of the approach.
My concern appears to apply only to the use of JWTs as authentication tokens by untrusted clients. Yet JWTs are used by a number of high-profile APIs, including Google APIs. What am I missing? Maybe server breaches are rarely read-only? Are there ways to mitigate the risk?
I believe you're thinking about this the wrong way. Don't get me wrong, it's great you're considering security, however the way you're approaching it in regards to double checking things server-side, adding additional checks that defeat the objective of stateless sessions, etc, appear to be along a one way street towards the end of your own sanity.
To sum up the two standard approaches:
JWTs are sessionless state objects, MAC'd by a secret key held server side.
Traditional Session Identifiers are stored either in memory or in a database server-side, and as you say are often hashed to prevent sessions from being hijacked should this data be leaked.
You are also right that write access is often harder for an attacker to achieve. The reason is that database data is often extracted from a target system via a SQL injection exploit. This almost always provides read access to data, but it is harder to insert data using this technique, although not impossible (some exploits actually result in full root access of the target machine being achieved).
If you have a vulnerability that allows access to the key when using JWTs or one that allows database tables to be written to when using session identifiers, then it's game over - you are compromised because your user sessions can be hijacked.
So not more damaging necessarily, it all depends on the depth of the vulnerability.
Double check that the security of your JWT keys align with your risk appetite:
Where are they stored?
Who has access?
Where are backups stored?
Are different keys used in pre-production and production deployments of your app?
The ways to mitigate is as good practise dictates with any web app:
Regular security assessments and penetration testing.
Security code reviews.
Intrusion detection and prevention (IDS/IPS).
WAF.
These will help you evaluate where your real risks lie. It is pointless concentrating on one particular aspect of your application so much, because this will lead to the neglect of others, which may well be higher risk to your business model. JWTs aren't dangerous and have no more risk than other components of your system necessarily, however if you've chosen to use them you should make sure you're using them appropriately. Whether you are or not comes down to the particular context of your application and that is difficult to assess in a general sense, so I hope my answer guides you in the right direction.
When an attacker is able to get hold of the signing key in a JWT based system that means that he is able to get access to the server backend itself. In that case all hope is lost. In comparison to that, when the same attack succeeds in session based systems the attacker would be able to intercept username/password authentication requests to the backend, and/or generate sessions ids himself, and/or change the validation routines required to validate the session ids and/or modify the data to which the session id points. Any security mechanism used to mitigate this works as well for session systems as for JWT systems.
I want to digitally sign documents and messages on a Linux server. How do I securely store the private key and a passphrase if any?
The problem is, if an application gets compromised, keys would also become compromised. If I could somehow let an app sign something, but don't let it touch actual keys, that wouldn't completely solve my problem (as an attacker would still be able to sign anything for some time), but reduce the impact (like, we won't have to revoke the keys).
For example, in case of SSL servers there's no such problem because usually there's no practical need for an application to access the keys. Hence, they can be semi-securely stored in a separate location. E.g. a webserver (like nginx) would be able to read the keys, but not the application.
Am I overthinking it? Is it even worthy thinking of?
Create a separate, lightweight signing application that listens on an UNIX socket and runs as a separate user from the main app; when your app wants to sign something it throws the file and any additional info down that socket, and gets back the signed file.
If the application ever gets compromised the attacker will still be able to sign files as long as he is still on the server, but unless he uses a privilege escalation exploit to get root privileges and copy the signing app's key, he won't he able to steal the key and then sign at will without being connected to the server.
You can replace the UNIX socket with a standard TCP socket and put the signing app on a separate server for extra security; make sure to implement some basic access control on the signing app and of course use proper firewall rules to make sure the signing server is never exposed to the internet, or simplify things a bit by using a "setuid" binary for signing that gets invoked by your app, in that case the signing binary will run as a different user with additional privileges to access the keys, while the webapp itself doesn't have such privileges.
Basically you should implement a rudimentary software HSM.
If you have very high security needs you could consider moving the keys to a completely independent server, or better yet a hardware security module (but those are expensive). Like you mention it can help prevent the loss of keys, but if the app is compromised the attacker could still sign whatever they wanted.
The main reason to go through the trouble then is auditing. That is if you have your signing server or device keep logs of everything it signs, then if only your app is compromised you will be better able to assess the extent of the damage (assuming you're signing server has not been compromised).
So yes there are benefits, but your first focus should be on securing your main application properly, because once that's compromised you're already having a very bad no good day, even if you have moved your keys to a sperate service.
Many users – myself included – would like the security of having everything they do on a web service encrypted. That is, they don't won't any one at the web service to be able to look at their: posts, info, tasks, etc...
This is also major complaint in this discussion of an otherwise cool service: http://news.ycombinator.com/item?id=1549115
Since this data needs to be recoverable, some sort of two-way encryption is required. But unless you're prompting the user for the encryption key on every request, this key will need to be stored on the server, and the point of encrypting the data is basically lost.
What is a way to securely encrypt user data without degrading the user experience (asking for some key on every request)?
-- UPDATE --
From #Borealid's answer, I've focused on two possibilities: challenge-response protocols, where no data (password included) is sent in the "clear", and non-challenge-response protocols, where data (password included) is sent in the "clear" (although over HTTPS).
Challenge-response protocols (specifically SRP: http://srp.stanford.edu/)
It seems that its implementation would need to rely on either a fully AJAX site or using web storage. This is so the browser can persist the challenge-response data during encryption and also the encryption key between different "pages". (I'm assuming after authentication is completed I would send them back the encrypted encryption key, which they would decrypt client-side to obtain the real encryption key.)
The problem is that I'm either:
fully AJAX, which I don't like because I love urls and don't won't a user to live exclusively on a single url, or
I have to store data encryption keys in web storage, which based on http://dev.w3.org/html5/webstorage/ will persist even after the browser is closed and could be a security vulnerability
In addition, as SRP takes more than one request ( http://srp.stanford.edu/design.html ), there needs to be some persistence on the server-side. This is just another difficulty.
Traditionally
If I'm ok transmitting passwords and data in the clear (although over HTTPS), then the client-side issues above are not present.
On registration, I'll generate a random unique encryption key for the user, and encrypt it using their password and a random salt.
In the database, I'll store the user's password hash and salt (through bcrypt), encrypted encryption key, encryption key salt, and encryption iv.
After an authentication, I'll also need to use their password to decrypt the encryption key so that they may view and enter new data. I store this encryption key only temporarily and delete it when they explicitly "log out".
The problems with this approach is that (like #Borealid points out) evil sysadmins can still look at your data when you are logged in.
I'm also not sure how to store the encryption keys when users are logged in. If they are in the same data store, a stolen database would reveal all data of those who were logged in at the time of theft.
Is there a better in-memory data store for storing these encryption keys (and challenge data during an SRP authentication)? Is this something Redis would be good for?
If the data need to be recoverable in the event of user error, you can't use something like a cookie (which could get deleted). And as you point out, server-side keys don't actually secure the user against malicious sysadmins; they only help with things like databases stolen offline.
However, if you're running a normal web service, you've already gotten pretty lucky - the user, in order to be unique and non-ephemeral, must be logged in. This means they go through some authentication step which proves their identity. In order to prove their identity, most web sites use a passed credential (a password).
So long as you don't use a challenge-response authentication protocol, which most web sites don't, you can use an encryption key derived from a combination of a server-side secret and the user's password. Store the encryption key only while the user is authenticated.
If you do this, the users are still vulnerable to sysadmins peeking while they're using the service (or stealing their passwords). You might want to go a step further. To go one up, don't send the password to the server at all. Instead, use a challenge-response protocol for authentication to your website, and encrypt the data with a derivative of the user's password via JavaScript before uploading anything.
This is foolproof security: if you try to steal the user's password, the user can see what you're doing because the code for the theft is right there in the page you sent them. Your web service never touches their data unencrypted. This is also no hindrance to the normal user experience. The user just enters their password to log in, as per normal.
This method is what is used by Lacie's storage cloud service. It's very well done.
Note: when I say "use foo to encrypt", I really mean "use foo to encrypt a secure symmetric key which is then used with a random salt to encrypt". Know your cryptography. I'm only talking about the secret, not the methodology.
None of those other solutions are going to maintain the feature set requested -- which specifically wants to preserve the user experience. If you look at the site referenced in the link, they email you a nightly past journal entry. You're not going to get that with JavaScript trickery per above because you don't have the browser to depend on. So basically this is all leading you down a path to a degraded user experience.
What you would want, or more precisely the best solution you're going to find in this space, is not so much what wuala does per above, but rather something like hush.com. The handling of user data needs to be done on the client side at all times -- this is generally accomplished via full client-side Java (like the Facebook photo uploader, etc), but HTML/JavaScript might get you there these days. JavaScript encryption is pretty poor, so you may be better off ignoring it.
OK, so now you've got client-side Java running a Journal entry encryption service. The next feature was to email past journal entries to users every night. Well, you're not going to get that in an unencrypted email obviously. This is where you're going to need to change the user experience one way or the other. The simplest solution is not to email the entry and instead to provide for instance a journal entry browser in the Java app that reminds them of some old entry once they get to the website based on a link in the daily email. A much more complex solution would be to use JavaScript encryption to decrypt the entry as an attachment inline in the email. This isn't rocket science but there is a fairly huge amount of trickery involved. This is the general path used by several web email encryption services such as IronPort. You can get a demo email by going to http://www.ironport.com/securedemo/.
As much as I'd love to see a properly encrypted version of all this, my final comment would be that journal entries are not state secrets. Given a solid privacy policy and good site security semantics, I'm sure 99% of your users will feel just fine about things. Doing all this right with true security will take an enormous amount of effort per above and at least some design/UE changes.
You should look into the MIT project CryptDB which supports querying an encrypted database using a subset of SQL. (see the forbes article, mefi thread, or Homomorphic encryption on wikipedia)
There is the Tahoe-LAFS project for cloud storage too, which conceivably could be leveraged into a fully anonymous social networking application, one day in the distant future.
If you want to perform computations on a server without even the server being able to see the data, you may be interested in knowing about fully homomorphic encryption. A fully homomorphic encryption scheme lets you perform arbitrary computations on encrypted data, even if you can't decrypt it. However, this is still a topic of research.
For now, I guess your best bet would be to encrypt all posts and assign meaningless (e.g. sequential) IDs to each one. For a more in-depth discussion of how to encrypt server-side data with today's technology, look up.
Here's the situation: we have a common library which can retrieve database connection details from a central configuration store that we have setup. Each application uses this library when working with a database.
Basically, it will call a stored procedure and say "I am {xyz} application, I need to connect o " and it will return the connection details for that applications primary database (server, instance, database, user, and password).
How would one go about locking that down so that only application {xyz} can retrieve the passwords for {xyz} databases (there is a list of database details for each application... i just need to secure the passwords)?
The usual way is to have a different config store per app and give each app a different user/password to connect to the config store.
That doesn't prevent anyone from changing the app and replacing the user/password for app X with the values from app Y but it's a bit more secure, especially when you compile this data in instead of supplying it via a config file.
If you want to be really secure, you must first create a secure connection to the store (so you need a DB drivers that supports this). This connection must be created using a secure key that is unique per application and which can be verified (so no one can just copy them around). You will need to secure the executable with hashes (the app will calculate its own hash somehow and send that to the server who will have a list of valid hashes for each app).
All in all, it's not something trivial which you can just turn on with an obscure option. You will need to learn a lot about security and secure data exchange, first. You'll need a way to safely install your app in an insecure place, verify its integrity, protect the code against debuggers that can be attached at runtime and against it running in the virtual machine, etc.
Off the top of my head, try PKI.
Are you trying to protected yourself from malicous programs, and is this a central database that these applications are connecting to? If so you should probably consider a middle layer between your database and application.
I'm not sure this applies to your case, depending on how what your answers to the abovementioned would be, but by the comments it sounds like you are having a similar case to what this question is about.
Securing your Data Layer in a C# Application
The simplest/most straightforward way would be to store the passwords in encrypted format (storing passwords in plaintext is just plain bad anyhow, as recently demonstrated over at PerlMonks) and make each application responsible for doing its own password encryption/decryption. It would then not matter whether an app retrieved another app's passwords, as it would still be unable to decrypt them.
One possibility is to keep the passwords in the database in an encrypted form, and convey the encryption key to the allowed application(s) in a secure connection.Then, only the application with the encryption key can actually get the passwords and not others.