After spending many years working in information security, as a consultant, I've had the chance to audit a multitude of different systems invented and developed by many different people. While there is a lot to say about that, the focus of this article is about some of the toxicity I've noticed in the field.
If you're like me, you must have heard the security jokes about developers during conferences, read the github issues of independent researchers yelling at volunteering contributors, or maybe witnessed developer shaming in whitepapers you read. Although it is (I believe) a minority, it is a pretty vocal one that has given a bad rap to security in general. Don't believe me? Go and ask someone working at a company with an internal security team, or read what Linus has to say about it. Of course different people at different companies will have different kind of experiences, so don't take this post as a generalization.
It is true that developers often don't give a damn about security. But this is not a good reason to dismiss them. If you work in the field, it is important to ask yourself "why?" first. And if you're a good person, what should follow next is "how can I help?".
Why? It might not be totally clear to people who have done little to no (non-security focused) development, but the mindset of someone who is writing an application is to build something cool. Imagine that you're trying to ship some neat app, and you realize that you haven't written the login page yet. It now dawns on you that you will have to figure out some rate-limiting password attempt mechanism, how to store the passwords, a way for people to recover their passwords, perhaps even two-factor authentication... But you don't want to do all of that, do you? You just want to write the cool app. You want to ship... Now imagine that after a few days you get everything done, and now some guy that you barely know is yelling at you because there is an exploitable XSS in your application. You almost hear yourself saying out loud "an XX-what?" and here goes your week end, perhaps even your week you start thinking. You will have to figure out what is going on, respond to the person who brought the bug, attempt to fix it. All of that while trying to minimize the impact on your personal reputation. And that's if it's only a single bug.
I have experienced this story of myself. Years ago, I was trying my hands with a popular PHP web framework of the time, when one of the config file asked from me to manually modify it in order to bootstrap the application. Naturally, I obliged, until I reached a line reading
// you must fill this value with a 32-random-characters key
$config['encryption_key'] = "";
Being pretty naive at the time, I simply wrote a bunch of words until I managed to get to the required number of letters (32). It must have looked like something like this:
// you must fill this value with a 32-random-characters key
$config['encryption_key'] = "IdontKnowWhatImDoing0932849jfewo";
Was I wrong? Or was it the framework's fault for asking me to do this in the first place? You got the idea: perhaps we should stop blaming developers.
How can you help? Empathize. You will often realize that security people who have worked non-security development jobs at some point in their career will often understand the developers better AND will manage to get them to fix things. I know, finding bugs is cool, and we're here to hack and break things. But some of these things have people behind them, the codebase is sometimes their own labor of love, their baby. Take the time to teach them about was is wrong, if they don't understand they won't be able to fix the issues effectively. Use language to vehiculate your ideas, sure it is a critical finding but do you need to write that on a github issue to shame someone? Perhaps the word "important" is better? Put yourself in their shoes and help them understand that you're not just coming here to break things, but you're helping them to make their app a better one! How awesome is that?
Sure they need to fix things and take security seriously. But how you get to there is as important.
I also need to mention that building better APIs for developers is an active area of research. You can't educate everyone, but you can contribute: write good tutorials (copy/pastable examples first), good documentation, and safe-by-default frameworks.
Thank-you for reading this.
Your mission, should you choose to accept it, is to be an awesome security peep now :)
EDIT: Mason pointed me to this article which I think has some really good points as well:
Another element of a critique-focused report involves the discussion of positive findings of the assessment. As the saying goes, a spoonful of sugar makes the medicine go down.
And indeed, If I liked what I've seen during an audit, I always start a discussion by telling the developers how much I liked the code/protocol/documentation. They are always really happy to hear it and my followed-up critiques are heard way more seriously. Interestingly enough, I've always felt like this is how a good performance-review should go as well.
In my free time in the last years, I have helped (for free) some VCs and friends to figure out what are good opportunities in the cryptography field. I'm at an excellent position to see what is serious cryptography, and even what a promising start up looks like. This is because my day-to-day job is to audit them.
It turns out that it is often quite easy to quickly spot the bad grapes by noticing common red flags. Here's a list of key words that you should probably stay away from: patented, proprietary protocol, one-time pad, AES-256, unbreakable, post-quantum, ICO, supply-chain, AI, machine learning, prime numbers, re-inventing TLS, etc.
In general, if a company focuses its pitch on the actual crypto they use instead of the problems they solve, you might want to turn around. If something doesn't seem right, find an expert to help you. Even Ycombinator got fooled.
Here I glance over a lot of the concepts. I don't explain what is Elgamal or how it works, I don't explain proofs based on games and what semantic security is (and why it is considered insufficient), I don't even explain what I mean by an adversary's advantage. I'm expecting that you will read that on your own and then head here to understand how you can use all of that to prove Elgamal's semantic security.
I've participated in multiple public audits since I've started working at the Cryptography Services of NCC Group. People can often see my work through the research I do, but a good chunk of my time is spent auditing and breaking real world applications. NCC Group sometimes release public reports and I think it is a good opportunity for outsiders to see what I work on, and what the audit-part of my work looks like.
Unfortunately we rarely release much details when we find critical vulnerabilities, so the more interesting reports are always partially published (like the Google one) or just not public. One day I'll find a way to talk about (without leaking out any information about clients of course) some of the juicy findings I and others have found in my years of consulting. Some of these war stories are extremely entertaining. We sometimes don't find much, but when we completely break a cryptocurrency or a company's flagship product, it feels like we're the best in the world at what we do.
On May 15th, I approached Yuval Yarom with a few issues I had found in some TLS implementations. This led to a collaboration between Eyal Ronen, Robert Gillham, Daniel Genkin, Adi Shamir, Yuval Yarom and I. Spearheaded by Eyal, the research has now been published here. And as you can see, the inventor of RSA himself is now recommending you to deprecate RSA in TLS.
The issues were disclosed back in August and the teams behind these projects were given time to resolve them. Everyone was very cooperative and CVEs have been dispatched (CVE-2018-12404, CVE-2018-19608, CVE-2018-16868, CVE-2018-16869, CVE-2018-16870).
The attack leverages a side-channel leak via cache access timings of these implementations in order to break the RSA key exchanges of TLS implementations. The attack is interesting from multiple points of view (besides the fact that it affects many major TLS implementations):
It affects all versions of TLS (including TLS 1.3) and QUIC. Where the latter version of TLS does not even offer an RSA key exchange! This prowess is achieve because of the only known downgrade attack on TLS 1.3.
It uses state-of-the-art cache attack techniques. Flush+Reload? Prime+Probe? Branch-Predition? We have it.
The attack is very efficient. We found ways to ACTIVELY target any browsers, slow some of them down, or use the long tail distribution to repeatdly try to break a session. We even make use of lattices to speed up the problem.
Manger and Ben-Or on RSA PKCS#1 v1.5. You heard of Bleichenbacher's million messages attack? Guess what, we found better. We use Manger's OAEP attack on RSA PKCS#1 v1.5 and even Ben-Or's algorithm which is more efficient than and was published BEFORE Bleichenbacher's work in 1998. I uploaded some of the code here.
While Ben-Or et al. research was initially used to support the security proofs of RSA, it was none-the-less already enough to attack the protocol. But it is only in 1998 that Daniel Bleichenbacher discovers a padding oracle and devise his own practical attack on RSA. The consequences are severe, most TLS implementations could be broken, thus mitigations were designed to prevent Daniel's attack. Follows a series of "re-discovery" where the world realizes that it is not so easy to implement such mitigations:
Let's be realistic, the mitigations that developers had to implement were unrealistic. Furthermore, an implementation that would attempt to log such attacks would actually help the attacks. Isn't that funny?
The research I'm talking about today can be seen as one more of these "re-discovery". My previous boss' boss (Scott Stender) once told me: "you can either be the first paper on the subject, or the best written one, or the last one". We're definitely not the first one, I don't know if we write that well, but we sure are hopping to be the last one :)
RSA in TLS?
Briefly, SSL/TLS (except TLS 1.3) can use an RSA key exchange during a handshake to negotiate a shared secret. An RSA key exchange is pretty straight forward: the client encrypts a shared secret under the server's RSA public key, then the server receives it and decrypts it. If we can use our attack to decrypt this value, we can then passively decrypt the session (and obtain a cookie for example) or we can actively impersonate one of the peer.
Attacking Browsers, In Practice.
We employ the BEAST-attack model (in addition to being colocated with the victim server for the cache attack) which I have previously explained in a video here.
Why several connections instead of just one? Because most browsers (except Firefox which we can fool) will time out after some time (usually 30s). If an RSA key exchange is negotiated between the two peers: it's great, we have all the time in the world to passively attack the protocol. If an RSA key exchange is NOT negotiated: we need to actively attack the session to either downgrade or fake the server's RSA signature (more on that later). This takes time, because the attack requires us to send thousands of messages to the server. It will likely fail. But if we can try again, many times? It will likely succeed after a few trials. And that is why we continuously send connection attempts to bank.com.
Attacking TLS 1.3
There exist two ways to attack TLS 1.3. In each attack, the server needs
to support an older version of the protocol as well.
The first technique relies on the fact that the current server’s public key is an RSA public
key, used to sign its ephemeral keys during the handshake, and that the
older version of TLS that the server supports re-use the same keys.
The second one relies on the fact that both peers support an older version
of TLS with a cipher suite supporting an RSA key exchange.
While TLS 1.3 does not use the RSA encryption algorithm for its key
exchanges, it does use the signature algorithm of RSA for it; if the
server’s certificate contain an RSA public key, it will be used to sign
its ephemeral public keys during the handshake. A TLS 1.3 client can
advertise which RSA signature algorithm it wants to support (if any)
between RSA and RSA-PSS. As most TLS 1.2 servers already provide support
for RSA , most will re-use their certificates instead of updating to the
more recent RSA-PSS. RSA digital signatures specified per the standard
are really close to the RSA encryption algorithm specified by the same
document, so close that Bleichenbacher’s decryption attack on RSA
encryption also works to forge RSA signatures. Intuitivelly, we have
$pms^e$ and the decryption attack allows us to find $(pms^e)^d = pms$,
for forging signatures we can pretend that the content to be signed
$tbs$ (see RFC 8446) is $tbs = pms^e$ and obtain $tbs^d$ via the attack, which is
by definition the signature over the message $tbs$. However, this
signature forgery requires an additional step (blinding) in the
conventional Bleichenbacher attack (in practice this can lead to
hundreds of thousands of additional messages).
Key-Reuse has been shown in the past to allow
for complex cross-protocol attacks on TLS. Indeed, we can successfully
forge our own signature of the handshake transcript (contained in the
CertificateVerify message) by negotiating a previous version of TLS
with the same server. The attack can be carried if this new connection
exposes a length or bleichenbacher oracle with a certificate using the
same RSA key but for key exchanges.
Downgrading to TLS 1.2
Every TLS connection starts with a negotiation of the TLS version and
other connection attributes. As the new version of TLS (1.3) does not
offer an RSA key exchange, the exploitation of our attack must first
begin with a downgrade to an older version of TLS. TLS 1.3 being
relatively recent (August 2018), most servers supporting it will also
support older versions of TLS (which all provide support for RSA key
exchanges). A server not supporting TLS 1.3 would thus respond with an
older TLS version’s (TLS 1.2 in our example) server hello message. To
downgrade a client’s connection attempt, we can simply spoof this answer
from the server. Besides protocol downgrades, other techniques exist to
force browser clients to fallback onto older TLS versions: network
glitches, a spoofed TCP RST packet, a lack of response, etc. (see POODLE)
Continuing with a spoofed TLS 1.2 handshake, we can simply present the
server’s RSA certificate in a ServerCertificate message and then end
the handshake with a ServerHelloDone message. At this point, if the
server does not have a trusted certificate allowing for RSA key
exchanges, or if the client refuse to support RSA key exchanges or older
versions than TLS 1.2, the attack is stopped. Otherwise, the client uses
the RSA public key contained in the certificate to encrypt the TLS
premaster secret, sends it in a ClientKeyExchange message and ends its
part of the handshake with a ChangeCipherSpec and a Finished
At this point, we need to perform our attack in order to decrypt the RSA
encrypted premaster secret. The last Finished message that we send must
contains an authentication tag (with HMAC) of the whole transcript, in
addition of being encrypted with the transport keys derived from the
premaster secret. While some clients will have no handshake timeouts,
most serious applications like browsers will give up on the connection
attempt if our response takes too much time to arrive. While the attack
only takes a few thousand of messages, this might still be too much in
practice. Fortunately, several techniques exist to slow down the
we can send the ChangeCipherSpec message which might reset the client’s timer
Once the decryption attack terminates, we can send the expected Finished
message to the client and finalize the handshake. From there everything
is possible, from passively relaying and observing messages to the
impersonated server through to actively tampering requests made to it.
This downgrade attack bypasses multiple downgrade mitigations: one
server-side and two client-side. TLS 1.3 servers that negotiate older
versions of TLS must advertise this information to their peers. This is
done by setting a quarter of the bytes from the server_random field in
the ServerHello message to a known value. TLS 1.3 clients that end
up negotiating an older version of TLS must check for these values and
abort the handshake if found. But as noted by the RFC, “It does not
provide downgrade protection when static RSA is used.” – Since we alter
this value to remove the warning bytes, the client has no opportunity to
detect our attack. On the other hand, a TLS 1.3 client that ends up
falling back to an older version of TLS must advertise this information in their subsequent client hellos, since
we impersonate the server we can simply ignore this warning.
Furthermore, a client also includes the version used by the client hello
inside of the encrypted premaster secret. For the same reason as
previously, this mitigation has no effect on our attack. As it stands,
RSA is the only known downgrade attack on TLS 1.3, which we are the
first to successfuly exploit in this research.
Attacking TLS 1.2
As with the previous attack, both the client and the server targetted
need to support RSA key exchanges. As this is a typical key exchange
most known browsers and servers support them, although they will often
prefer to negotiate a forward secret key exchange based on ephemeral
versions of the Elliptic Curve or Finite Field Diffie-Hellman key
exchanges. This is done as part of the cipher suite negotiation during
the first two handshake messages. To avoid this outcome, the
ClientHello message can be intercepted and modified to strip it out of
any non-RSA key exchanges advertised. The server will then only choose
from a set of RSA-key-exchange-based cipher suites which will allow us
to perform the same attack as previously discussed. Our modification of
the ClientHello message can only be detected with the Finished
message authenticating the correct handshake transcript, but since we
are in control of this message we can forge the expected tag.
On the other side, if both peers end up negotiating an RSA key exchange
on their own, we can passively observe the connection and take our time
to break the session.
LiveOverflow recently pushed a video about Nadim Kobeissi's last paper. It is quite informative and invite to the discussion: can an end-to-end encryption app served by a webserver be secure? If you haven't seen the video, go watch it. It gives a good explanation of something that is actually quite simple. Here's someone's opinion on unnecessary complicated papers:
“We use the standard Bellare-Rogaway definition of key exchange, which we briefly summarize below. [Fourteen pages of dense notation omitted] And in conclusion most Bluetooth devices use ‘000000’ as the hardcoded password.”
I have seen this kind of things happen quite often in the cryptography and security community. While some companies are 100% snake oils, some others do play on the border with their marketing claims but very real research. For example, advances in encrypted databases are mostly pushed by companies while attacks come from researchers not happy with their marketing claims. But this is a story for another time.
If you look at it from the other side, are mobile applications that more secure? While the threat model is not THAT different, in both of these solutions (thanks Nik Kinkel and Mason Hemmel for pointing that out to me) no specific targetting can happen. And this is probably a good argument if you're paranoia stops at this level (and you trust the app store, your OS, your hardware, etc.)
Indeed, a webapp can easily target you, serving a different app depending on the client's IP, whereas mobile apps need the cooperation of the App store to do that. So you're one level deeper in your defense in depth.
What about Signal's "native" desktop application? Is it any better? Well... you're probably downloading it from their webserver anyway and you're probably updating the app frequently as well...
I've been implementing the Disco protocol in C. It makes sense since Disco was designed specifically for embedded devices. The library is only 1,000 lines-of-code, including all the cryptographic primitives, and does everything the Go implementation does except for signing.
If you don't know what Disco is, it's a cryptographic library that allows you to secure communications (like TLS) and to hash, encrypt, authenticate, derive keys, generate random numbers, etc.
It's not as plug-and-play as the Golang version. There are no wrappers yet to encrypt, authenticate, hash, derive keys, etc. and I haven't made a decision as to what algorithm I should support for signatures (ed25519 with strobe? Or Strobe's Schnorr-variant with Curve25519?)
So it's mostly for people who know what they're doing for now.
Don't let that deter you though! I need people to play with it in order to improve the library. If you need help I'm here!