Blame
| c4f02d | Samuli Seppänen | 2025-01-29 08:37:37 | 1 | # OpenVPN and SWEET32 |
| 2 | ||||
| 3 | Security researchers at INRIA published an attack on 64-bit block ciphers, such as 3DES and Blowfish [^0]. They show that they are able to recover plaintext when the same data is sent often enough, and demonstrate how cross-site scripting vulnerabilities can be exploited to send data of interest frequently enough. This vulnerability affects HTTPS as well as HTTP-over-OpenVPN. For a more detailed explanation, visit [sweet32.info](https://sweet32.info/) [^0]. |
|||
| 4 | ||||
| 5 | ## Am I affected? |
|||
| 6 | ||||
| 7 | This depends on the cipher you've chosen (OpenVPN's `--cipher` option). **OpenVPN's default cipher, BF-CBC, is affected by this attack.** |
|||
| 8 | ||||
| 9 | You can check if you're affected by installing OpenVPN 2.3.12 [^1] or newer, and running `openvpn --show-ciphers`. This will list which ciphers should no longer be used. For convenience, we provide a summary of commonly used ciphers here: |
|||
| 10 | ||||
| 11 | Affected ciphers that should no longer be used: |
|||
| 12 | - BF-* |
|||
| 13 | - DES* (including 3DES variants) |
|||
| 14 | - RC2-* |
|||
| 15 | ||||
| 16 | Ciphers that are *not* affected: |
|||
| 17 | - AES-* |
|||
| 18 | - CAMELLIA-* |
|||
| 19 | - SEED-* |
|||
| 20 | ||||
| 21 | ## Mitigation |
|||
| 22 | ||||
| 23 | ### 1. Change to a larger block cipher |
|||
| 24 | ||||
| 25 | The best mitigation is to transition away from small-block ciphers. This requires editing the cipher setting in all server and client configs (or upgrading to our experimental branch, see below). |
|||
| 26 | ||||
| 27 | For currently supported ciphers, OpenVPN recommends using AES-256-CBC or AES-128-CBC. OpenVPN 2.4 and newer will also support GCM. For 2.4+, we recommend using AES-256-GCM or AES-128-GCM. |
|||
| 28 | ||||
| 29 | ### 2. Renegotiate more often |
|||
| 30 | ||||
| 31 | If changing the cipher is not possible, for example, because you do not control the server or cannot update all client configs quickly, you can renegotiate new keys more frequently. For instance, add `--reneg-bytes 64000000` to your config to renegotiate after every 64 megabytes. |
|||
| 32 | ||||
| 33 | If you're using two-factor authentication or username-password authentication, this might require users to re-enter their 2FA token or username and password. To avoid this, do not use `--auth-nocache`, and use the `auth-token` option (see below) in the client-connect and auth-user-pass-verify scripts on the server side to request 2FA only once per session. |
|||
| 34 | ||||
| 35 | The (undocumented) `auth-token` option can be pushed by a client-connect script (running on the server) to instruct the connecting client to return this token as the password during the next authentication. The auth-user-pass-verify script (running on the server) should accept this token during subsequent authentication sessions until the token expires. |
|||
| 36 | ||||
| 37 | The following client-connect and auth-user-pass-verify scripts illustrate how these options can be used. **These scripts should not be used as-is!** They are examples only and should be adapted to your specific needs. |
|||
| 38 | ||||
| 39 | ```python |
|||
| 40 | #!/usr/bin/env python |
|||
| 41 | # client-connect script |
|||
| 42 | import base64 |
|||
| 43 | import hmac |
|||
| 44 | import os |
|||
| 45 | import sys |
|||
| 46 | import time |
|||
| 47 | ||||
| 48 | username = os.environ['username'] |
|||
| 49 | ||||
| 50 | ts = time.time() |
|||
| 51 | to_auth = str(ts) + ":" + username |
|||
| 52 | ||||
| 53 | h = hmac.new('mysecret') |
|||
| 54 | h.update(to_auth) |
|||
| 55 | digest = base64.b64encode(h.digest()) |
|||
| 56 | ||||
| 57 | auth_token = "push \"auth-token " + str(ts) + ":" + digest + "\"" |
|||
| 58 | ||||
| 59 | print("Sending auth-token:", auth_token) |
|||
| 60 | ||||
| 61 | open(sys.argv[1], 'w').write(auth_token) |
|||
| 62 | ``` |
|||
| 63 | ||||
| 64 | ```python |
|||
| 65 | #!/usr/bin/env python |
|||
| 66 | # auth-user-pass-verify script |
|||
| 67 | import base64 |
|||
| 68 | import hmac |
|||
| 69 | import os |
|||
| 70 | import time |
|||
| 71 | ||||
| 72 | username = os.environ['username'] |
|||
| 73 | password = os.environ['password'] |
|||
| 74 | ||||
| 75 | if (password == "mysecretpassword"): |
|||
| 76 | print("password OK") |
|||
| 77 | exit(0) |
|||
| 78 | ||||
| 79 | token = password.split(":") |
|||
| 80 | ||||
| 81 | to_auth = token[0] + ":" + os.environ['username'] |
|||
| 82 | ||||
| 83 | h = hmac.new('mysecret') |
|||
| 84 | h.update(to_auth) |
|||
| 85 | digest = h.digest() |
|||
| 86 | ||||
| 87 | if digest != base64.b64decode(token[1]): |
|||
| 88 | print("Auth-token incorrect") |
|||
| 89 | exit(1) |
|||
| 90 | ||||
| 91 | if time.time() - float(token[0]) > 60: |
|||
| 92 | print("Auth-token expired") |
|||
| 93 | exit(1) |
|||
| 94 | ||||
| 95 | exit(0) |
|||
| 96 | ``` |
|||
| 97 | ||||
| 98 | ### 3. Cipher negotiation (OpenVPN 2.4 and newer) |
|||
| 99 | ||||
| 100 | OpenVPN 2.4 and newer support cipher negotiation. If both peers (client and server) support cipher negotiation, OpenVPN will default to using AES-GCM. |
|||
| 101 | ||||
| 102 | For more details, see the OpenVPN 2.4 man page on `ncp-ciphers`. |
|||
| 103 | ||||
| 104 | ## References |
|||
| 105 | ||||
| 106 | [^0]: [sweet32.info](https://sweet32.info/) |
|||
| 107 | [^1]: [OpenVPN Downloads](https://openvpn.net/index.php/open-source/downloads.html) |
|||
| 108 | [^2]: [OpenVPN GitHub](https://github.com/OpenVPN/openvpn.git) |
