A number of web frameworks and standards incorporate encrypted user data, where the security assurance is built on the assumption “if it was encrypted by the server, it is trustworthy”.
We know these features by many names: ViewState, Encrypted Beans, Encrypted Parameters, Authentication Cookies, MessageEncryptor.
WHY WEB ENCRYPTION FRAMEWORKS ARE INTERESTING AND POSSIBLY DANGEROUS:
1) There is a presumed level of trust because it is encrypted. And if it is presumed trustworthy, developers will think it is okay to dangerous things based on it. (Hacking .NET with forged Authentication Cookies)
2) There is often an unclear threat model and security contract between application developers and framework. I don’t believe I ever have read a framework documentation which clearly says what programming contract an application developer must follow to not break security. And the framework developers may change the security contract in future releases.
- Stripes 1.4.x encrypted with a unique key per session, Stripes 1.5.x encrypts with a global key; which changes the security requirements on application source code – a web site which relied heavily on Stripes 1.4.x security model could be much less secure after been upgraded to Stripes 1.5.x.
- Framework developers may choose to increase security to defend against attacks, possibly changing the programming contracts and causing problems for legacy application built under different behaviours.
3) Audit and verification is next to non-existent. Your average penetration tester is often not skilled in evaluating encryption solution. And developers & code auditors may have a hard time evaluating correctness of application code because there is no contract. Almost all web penetration testers knows how to look for SQL injection, most don’t understand encryption at all. And application developers and framework developers alike, most aren’t cryptography experts.
THREATS TO WEB ENCRYPTION FRAMEWORKS:
Choosen ciphertext attacks: The attacker would love to be able to mount attacks which allows the attacker to generate valid ciphertext using tampering attacks (funny example, check out “CAPTCHA graffiti“).
Choosen plaintext attacks: The attacker would love to be able to just create ANY plaintext. In some frameworks, it is actually crazy easy for an application developer to accidentally create an choosen plaintext vulnerability; they only need to create one web page which returns an encrypted copy of a user controlled input. A single application developer flaw may undermine the security of the web framework.
Cross-Site Request Forgery attacks: An hacker may observe the encrypted parameters for an important web transaction, and then launch classic CSRFattack. This attack works brilliantly against many frameworks 🙂
PROTECTING AGAINST WEB ENCRYPTION THREATS:
Application developers should, unless they have very good reasons to believe otherwise, assume that encrypted data is not trustworthy data,
Framework developers should implement encryption best practices, such as authenticated encryption. That means either doing AEAD ciphers such as AES-GCM, or do Encypt-then-Mac (HMAC of ciphertext). TODO: write a blog post on this subject.
Framework developers should limit scope of encryption. For example, CSRF attacks against encrypted parameters works because two different sessions can encrypt and decode each others parameters. Defining the scope (per page? per session? per user?) and enforce scope limitation can make the framework robust enough defend against some vulnerabilities in application code and web browsers.
HOW TO LIMIT SCOPE OF ENCRYPTION:
A web framework could limit encryption scope by having a unique key per scope.
- A simple way of doing this is to create a random encryption key, and put it into the server-side HTTP Session. Under a set of assumption, i.e. a session for each user is acceptable, and session hijacking is otherwise prevented, this acceptable.
- Another way is to perform Key Derive per scope, with scope included in the PBKDF salt/context, to generate scope unique keys in runtime.
Another way for web frameworks to limit encryption scope could be to include scope in the plaintext and reject any decrypted plaintext originating from the wrong scope. For example, a framework create encrypt application plaintext as such follows:
- plaintext = BASE64(scope) + “:” + application_plaintext.
- ciphertext = authenticated_encryption( plaintext ).
And decrypt as follows:
- plaintext = authenticated_decryption( ciphertext ).
- if ( not plaintext.startWith ( BASE64(scope) + “:” ) throw new EncryptionBadScopeException