shadowsocks-org icon indicating copy to clipboard operation
shadowsocks-org copied to clipboard

Secure single-port multi-user authentication

Open riobard opened this issue 8 years ago • 46 comments
trafficstars

Summary

Current commercial deployments of Shadowsocks suffer from a social-engineering weakness that any client knows the Symmetric Pre-Shared Key (SPSK) and can use it to MitM attack other clients of the same server.

To counter this weakness, SPSK must be abandoned in favor of asymmetric private/public key pairs. The server's private key must be kept secret at all times. Clients only know the server's public key, and must derive a per-session subkey to talk to the server. No clients can pretend to be the server because it does not have the server's private key.

An additional benefit of using asymmetric key pairs is that multiple users can share the same port securely without revealing their own client identifiers.

Update

Please see https://github.com/shadowsocks/shadowsocks-org/issues/54#issuecomment-589549957 for the latest proposal. The following section is the original proposal without forward secrecy protection.

Details (OBSOLETED)

  1. The server randomly generates a pair of keys on Curve25519: a private key sk that must be kept secret, and a public key pk that is distributed to all clients beforehand.

    sk, pk = curve25519_keypair(random_seed)

  2. Each time a client wants to talk to the server, it randomly generates another pair of keys on Curve25519: a private key sk' that must be kept secret, and a public key pk' to be sent to the server.

    sk', pk' = curve25519_keypair(random_seed)

  3. The client computes the shared secret with the server using x25519 ECDH function:

    shared_secret = x25519(sk', pk)

  4. The client derives the per-session subkey using HKDF_SHA256:

    subkey = HKDF_SHA256(shared_secret || pk' || pk)

  5. The client encrypts payload using the subkey and sends the encrypted data to server.

  6. The server receives the client's public key pk' and computes the shared secret using x25519 ECDH function:

    shared_secret = x25519(sk, pk')

  7. The server derives the per-session subkey using HKDF_SHA256:

    subkey = HKDF_SHA256(shared_secret || pk' || pk)

  8. The server uses the subkey to decrypt the encrypted payload.

Note that x25519 ECDH function ensures x25519(sk, pk') == x25519(sk', pk).

To implement secure multiuser support, the client can include its Client Authentication (e.g. username/password) in the payload for the server to verify against a database. Detailed format will be discussed separately.

riobard avatar Feb 24 '17 09:02 riobard

There two key assumptions of shadowsocks protocol:

  1. Users set up shadowsocks themselves on public cloud services.
  2. A user use his service privately or share it with a small group of people.

Based on these two assumptions, I don't think we should worry about the MitM attacks mentioned above. Also, based on these two assumptions, we are able to provide a very easy-to-use tool for our target users.

However, if anyone wants to use shadowsocks as a commercial service and try to let many people connect to one same server port with one same key, the problem above indeed exists.

So, here are some suggestions from my side:

  1. It's important to set up and use shadowsocks "privately". It ensures both security and performance.
  2. For the multi-user usage, we'd better put this kind of proposals (including user authentication) in a new protocol instead, since it's already not a shadowsocks protocol.

madeye avatar Feb 24 '17 11:02 madeye

Indeed. This is strictly necessary only for large-scale (e.g. more than a few trusted friends) deployments (though anyone will benefit from this if they have more than one client).

I don't have stats but from personal observation more friends are using commercial services than running their own for various reasons (e.g. lack of technical acumen, ISP and VPS choices, time constraint etc). This proposal is for them.

I'll implement this in my experimental branch first and see how it goes. But in order for this to be adopted by major clients, we need to make it official. Somehow I think this is the necessary sales pitch for them to accept the new AEAD ciphers. Right now there's a sentiment from client devs that AEAD ciphers do not bring enough benefits to worth the trouble.

I think they'll like the multiuser mode.

riobard avatar Feb 24 '17 11:02 riobard

I think:

  • The link speed is the strongest demand.

  • As for routers, the encrypt/decrypt speed may also a concern.

  • A multi-user mode is a concern only if ports like 80 or 443 are more QoS or firewall friendly.

If people prefer your design, you can actually re-brand it, and it'll be official. Putting it into Shadowsocks here will be confusing for not-tech people.

hellofwy avatar Feb 24 '17 12:02 hellofwy

@hellofwy This proposal will most likely ended up being shadowsocks2. I think there's already enough confusion now, and clean, backward-incompatible revision might be a better way.

riobard avatar Feb 24 '17 14:02 riobard

Just want to point out that, "Diffie–Hellman key exchange" itself doesn't prevent from MITM attacks. Without a certificate authority, there is no way to ensure that the public key gets transferred securely. The DH algorithm only provides forward secrecy.

v2ray avatar Feb 24 '17 23:02 v2ray

@v2ray No. Like SPSK, the server's public key is pre-shared, and CA is not necessary. The additional benefit brought by PKI is icing on the cake.

And the way we use DH does not provide forward secrecy either. In fact, FS is not possible without handshake. We'd like to avoid handshake for obvious reasons.

riobard avatar Feb 25 '17 02:02 riobard

With enough traffic, plain curve25519 public keys are vulnerable to deep packet inspection. See https://elligator.cr.yp.to

The threat that the new protocol try to prevent is not an issue caused by shadowsocks itself, but by user reusing the same public key. The security provided by pre-shared keys is based on the assumption that the user absolutely trust anyone who have the knowledge of the pre-shared keys, and all traffic would be transparent to them.

With all these considered, the "MitM attack" you mentioned is actually unclear. The victim "trust" the attacker by sharing the same PSK. But this doesn't provide the attacker any further advantage to perform attacks. If the attacker somehow controls routing node between the user and shadowsocks server, he/she will be able to capture or interrupt traffics. But that's all. It's basically the same threat as if shadowsocks is not being used. If you insists that current shadowsocks implementation should prevent this from happening, remember that victim actually "trust" the attacker.

That being said, I understand that there are use case for multi-user proxy server. I just don't feel it's necessary to have it in shadowsocks with such distinct purposes.

Popwax avatar Feb 25 '17 05:02 Popwax

@Popwax It's easy to disguise the public key sent from the client to the server by encrypting it with the server's public key using a symmetric cipher (e.g. AES). This does not provide any additional security, but just a means to make the structure of the public key indistinguishable from random noise.

If the attacker somehow controls routing node between the user and shadowsocks server, he/she will be able to capture or interrupt traffics. But that's all.

This is false. With the symmetric PSK, the adversary can perform MitM without being detected.

victim actually "trust" the attacker

This makes no sense security-wise.

riobard avatar Feb 25 '17 06:02 riobard

This is false. With the symmetric PSK, the adversary can perform MitM without being detected.

This makes no sense security-wise.

Maybe I didn't put it straight. The victim shouldn't use the same PSK with others in the first place. Otherwise, the victim should expect those attack being undetected.

Popwax avatar Feb 25 '17 06:02 Popwax

The victim shouldn't use the same PSK with others in the first place.

Right. The sad truth is that currently that's what people are doing in the field. The alternative (one password/cipher/port per user) is such a hassle that it's quite unpopular.

riobard avatar Feb 25 '17 06:02 riobard

That's not shadowsocks anymore... Handshakes will generate extra traffic, which I think is not what shadowsocks wants. And if we need it why not use TLS directly?

shinku721 avatar Feb 25 '17 08:02 shinku721

@shinku721 There's no handshake in this proposal. Server's public key is pre-shared.

riobard avatar Feb 25 '17 08:02 riobard

@shinku721 TLS requires handshake, which we want to avoid.

riobard avatar Feb 25 '17 08:02 riobard

@riobard Your assumption is that the public key can be transferred through a trustable way. In the context of MITM attack, this is not true. The attacker can intercept the public key, replace it with his own public key without the client knowing it. Then the attacker can decrypt traffics from the client with his own keys, and the encrypt it with server's public key. Without a CA's signature, there is no way for the client to check whether the key has been tempered or not.

v2ray avatar Feb 25 '17 09:02 v2ray

@v2ray No.

The assumption is only that the server's public key is pre-shared and thus trusted. The client's public key can be sent in clear text (or disguised as random noise as mentioned in https://github.com/shadowsocks/shadowsocks-org/issues/54#issuecomment-282463141)

The attack you described will not work. The adversary cannot decrypt any traffic because it lacks either the client's or the server's secret key.

riobard avatar Feb 25 '17 09:02 riobard

The "key pre-shared" process is where MITM attack starts. You have to make sure that the public key received by client matches exactly the one sent from the server.

v2ray avatar Feb 25 '17 10:02 v2ray

That's a completely different threat model and way beyond the scope of this project.

In this proposal we assume the client can get a trusted copy of the server's public key (e.g. copy from the proxy service provider's TLS-protected webpage).

riobard avatar Feb 25 '17 11:02 riobard

@riobard Well, you're right. However, it may be a bit too far beyond shadowsocks... If a user know the pre-shared key and is able to perform a MitM attack then why not she/he invite the server provider to tea directly?

shinku721 avatar Feb 26 '17 17:02 shinku721

@shinku721 There are more types of adversaries than just state-level organizations. If you only care about GFW, the original Shadowsocks with stream ciphers is proven to be more than sufficient to get the job done.

My take is that Shadowsocks could be a more general tool to improve security. For example, it could protect you from a local (e.g. company-wide) filtering/monitoring agent.

Remember that HTTPS is still not everywhere yet.

riobard avatar Feb 26 '17 17:02 riobard

I agree with @madeye wholeheartedly and Shadowsocks is never meant to cover the use cases you mentioned. If you want to do that, using other projects or starting off something anew is a better option.

Mygod avatar Feb 28 '17 09:02 Mygod

@Mygod I don't mind if it's called something else.

riobard avatar Feb 28 '17 09:02 riobard

So I think we should close this since it's off-topic. Feel free to continue your discussions.

Mygod avatar Feb 28 '17 09:02 Mygod

I'd like to keep this issue open because it's basically the continuation of https://github.com/shadowsocks/shadowsocks/issues/169 which I just found a couple of days ago.

riobard avatar Feb 28 '17 09:02 riobard

A helpful reminder: keep second-system effect in mind.

Mygod avatar Mar 01 '17 06:03 Mygod

I'd like to suggest refering to the previous TLS 1.3 drafts as what you're trying to eventually (re)invent is very similar to 0-RTT ECDHE-ECDSA-AES128-GCM-SHA256 cipher suite with hardcoded public keys (the "PSK").

Riatre avatar Mar 06 '17 08:03 Riatre

@Riatre So? TLS leaks protocol identity at record layer and it requires handshake even with PSK. None of which is acceptable in the scope of this project.

riobard avatar Mar 06 '17 16:03 riobard

@riobard I mean you may want to check how the cipher suites are designed in TLS (the right combination of key exchange algorithms, authentication algorithms and encryption algorithms to provide identification, authentication, confidentiality and integrity), which doesn't depend on the other parts of the TLS protocol and can be borrowed.

Riatre avatar Mar 07 '17 01:03 Riatre

@riobard The handshake in TLS basically serves ~three~ four purposes:

  1. Confirm the client and the server both talk TLS.
  2. Negotiate a suitable cipher suite supported by both sides.
  3. Identify the server and the client.
  4. Do a secure key exchange.

Clearly 1 and 2 is not needed, we can simply hardcode which cipher suite to use like we currently do. 3 (Identification) is what you're trying to resolve in this issue. 4 (Key Exchange) is required to provide forward secrecy.

TLS 1.3 actually describes a 0-RTT method (which means you can carry data in the first round trip like TCP fast open, and the only scarification is it doesn't guarantee forward secrecy for the first two packets) to accomplish 3 and 4 and it is similar to what you described in this issue, which is the reason why I suggest you reading it.

Riatre avatar Mar 07 '17 02:03 Riatre

@Riatre Ah, I see. I thought you were suggesting that we should just use TLS 1.3. There were suggestions like that before without fully comprehension of the scope of the project. Sorry for the misunderstanding.

riobard avatar Mar 07 '17 05:03 riobard

嗯,插个队,想问下,你们的多用户支持是否也会设计成插件式的,就是好似 @Riatre 那种流程,但 3 的身份识别是可以配置不同 backend 的,好比是类似于 freeradius 的那样的

想做伸手党等个支持 LDAP 认证的模块等好久了,嘿嘿

AlvisZhang avatar Mar 24 '17 15:03 AlvisZhang