The attacker claimed that he compromised their webserver. If he had access to the private key and encrypted numbers, then they were indeed being stored on an internet-facing machine.
07:52 < HTP> the CCrypter class of the linode application
context was accessable from outside the wwwroot using
undocumented ColdFusion methods. i was fully able to
decrypt the ccs using the in-memory privkey that they
supplied the password for.
Linode has two servers. One is a web server that runs their website and contains its source code. One is a database server containing customer information and encrypted credit card numbers. The web server is connected to the internet. The database server is not. They communicate over the private network.
The web server is compromised. The attacker has the source code, which includes the IP/host and login credentials for the database server, since the website code talks to the database to function. The attacker can now access the encrypted credit card numbers in the database by connecting from the web server.
At no point are the encrypted numbers stored on an internet-facing machine, but the attacker now has access to them. There is no evidence that says otherwise. There's no way to build this that doesn't have a path from the internet to the card numbers either, since eventually they need to get onto an internet-connected machine to talk to the payment gateway and actually charge those cards.
> There's no way to build this that doesn't have a path from the internet to the card numbers either, since eventually they need to get onto an internet-connected machine to talk to the payment gateway and actually charge those cards.
I have to disagree.
Web server has write-only access to a remote database table on the accounts/billing server, writes account change requests; accounts/billing server polls table, matches account numbers to billing information, submits charge requests to payment gateway via a firewall that allows access to only a single IP or set of IPs; accounts/billing server writes charge result back to another table; web server has read-only access to the results table and subsequently changes the user's account type or sends out an email or performs other administrative duties.
That's how I'd build it, I've done similar stuff before. (I have a paranoid business client.)
The nice thing about this is that you can capture credit card information on the web server, encrypt it with the public key, and then decrypt it using the private key which is only on the accounts/billing server.
Getting unauthorized remote access to the accounts/billing server would be a challenge. If they needed authorized remote access to it for customer service reasons on ports other than ssh, I'd use authpf on the pf firewall sitting between the accounts/billing server and the rest of the network, along with a unique password-protected ssh key for each employee. But, really, 1 or 2 sysadmins are the only people who should have non-console access to it.
> The web server is compromised. The attacker has the
> source code, which includes the IP/host and login
> credentials for the database server, since the website
> code talks to the database to function.
IMO, if internet-facing machine A is storing login credentials to machine B, then machine B should be considered internet-facing.
There is no reason for a properly-designed system to be storing passwords or credit card numbers in the same database as the rest of the user's data. Such security-critical data should always be placed on a hardened machine, with the web server communicating via a small and easily-auditable interface.
It's not a contradiction, because 1) the webserver would not have login credentials to the hardened machine and 2) the interface would be much smaller than "everything in SQL".
A typical example of such an interface would expose about four operations:
// Return true if the given user/password pair is valid.
bool PasswordValid(string user, string password)
// Return true if the password was changed successfully.
bool ChangePassword(string user, string old_password, string new_password)
// Returns a reset token.
string RequestPasswordReset(string user)
// Returns true if the password was reset successfully.
bool CompletePasswordReset(string user, string token, string new_password)
With reasonable changes to support two-factor, if needed.
Credit card data would have a similar interface -- add card, remove card, list user's cards without full cc#, charge card.
The advantages of this over the sort of "hashed passwords in MySQL" design are obvious. Not only is sensitive data protected against "SELECT *", but it's now possible to apply security policies to password management. For example, the password service might enforce rate limiting on how often a particular user's password can be checked -- that way, even if the web server is compromised, the attacker will be unable to compromise passwords any faster than they could via the standard login screen.
The password machine would typically be configured to have only the password service and SSH running, with SSH access limited to a key that's stored in a safe somewhere and used only for emergencies.
If a developer doesn't feel comfortable building such a system themselves (reasonable), then there are many commercial products available. They're expensive for a home user or ramen-budget startup, but a company like Linode would be able to afford one easily.
That'd still be 'internet-facing' though, by your definition. Yes, it might require one more round of cracking (the simple interface), but the machines are still connected.
No, it would not. Please read my post again. The webserver would not have login credentials for the password management machine.
Additionally, I object to your implication that any network service can be trivially compromised with "one more round of cracking". This is a view of software security that is propagated by science-fiction movies, and is not grounded in real life. Vulnerabilities that permit remote code execution are relatively easy to prevent by taking appropriate precautions when designing and implementing a network service, and almost every such vulnerability has its root cause in doing something obviously insecure (e.g. writing in C/C++ or a language with eval(), passing user input into an OS procedure).