-
Notifications
You must be signed in to change notification settings - Fork 883
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove Naive server and use padding auto-negotiation #76
Comments
Compatibility test cases during the transition:
Verify:
Padding negotiation:
The padding byte format:
The purpose of this padding is to protect the payload with TLS handshakes of known sizes. Therefore only the first 4 reads/writes are padded. The choice of the padding length is based on #1 (comment) and also not to make it too large resulting in detectable entropy. But the padding is currently limited to size of [0, 255], away from the ideal distribution of [200, 800]. Changing this is a wire format breaking change though. Security issues:
|
In fact we have a different approach to do padding. I found it after more reading of h2 specs, and would like to propose it here. H2's HEADERS and DATA frame types have built-in padding feature. Existing h2 implementations can handle received padded frames, however no one use it (or expose the interface) in sending frames. Since you are planning to maintain both client and server, why not take advantage of this native padding feature? Using h2 native padding have at least such pros:
and cons:
I've tried and found it not difficult in chrome's h2 stack. I implemented it in a new branch https://github.com/darhwa/naiveproxy/tree/native_padding and tested it for more than 1 day without problem. I can see in wireshark that it works as expected. The patch can even be applied to chrome itself. Please have a look at it when you have time. But things are not so simple for caddy (golang). I found golang maintainers have explicitly rejected a proposal from sergeyfrolov for supporting padding at higher layer (golang/go#26900). However, it's still doable considering that you are going to maintain your own caddy and users don't need to build it themselves. I'm also going to dive into haproxy source as soon as I have time. What's your opinion? |
Does v83.0.4103.61-2 support this RFC ? I found padding is not working |
@emacsenli Did you download the released pkg or compiled from master yourself? The released version changed nothing about padding. |
I am with released binary file .I will try compile later. |
No. What in this RFC is not implemented.
You should double-check your config, and your server status. v83.0.4103.61-2 is not broken. To compile your own binary won't hopefully help. |
https://www.mail-archive.com/[email protected]/msg28781.html I tried this a while ago. The attitude of Go devs reveals a deeper truth: H2 padding is used by no one and not well tested and it's an uphill battle if you try to use it against server bugs (even with pure client side padding). This project is structured to minimize maintenance work so it's more likely to survive. Forking forwardproxy the plugin is already the limit. Forking Go's H2 stack is not sustainable. As for the RFC, there's quite some as hoc guesswork accumulated in the paddings all around. It's time to collect some new histograms, update the lit review, and do some big self purging before committing to any particular padding methods. This may take a few months. |
Resolutions of the above security issues: Even though # 1 listed many characteristics, most would appear in regular H2 connections. Real issues of H2 tunnels basically boil down to two:
Direction n-gram analysis (poor man's LTSM) - there is no practical countermeasure to this analysis more advanced than simple padding. Thus it remains a pure theoretical exercise useful for evaluating obfuscation strength. Packet timing (in particular CONNECT and ClientHello back-to-back). This is expensive to remove. Hopefully H2 multiplexing combined length padding to both of the two packets can mitigate this to an extent. New padding scheme:
|
To replace RST_STREAM frames with END_STREAM flagged DATA frames is not a good idea. If you begin downloading a large file then cancel it, naiveproxy will still receive the entire file from server. It's also not feasible to make server treat END_STREAM as RST_STREAM, because normal requests also mark the last frame as END_STREAM. Chrome's h2 implementation shoots exactly one TLS record for one h2 frame. That's not mandatory. I think we can modify it to merge multiple h2 frames in one TLS record in some cases. For each RST_STREAM we can precede it with a pure padding DATA. (Update: I've implemented it in my native_padding branch. It proves to work!) |
It's not inherently a bad idea as it merely enters half-closed local state, which is the usual process of a request. Though half-closed states are less clean to manage than resets. It seems you're putting two frames of content into one SpdySerializedFrame. If this can somehow cheat the existing spdy traffic accounting then it'll be cleaner than END_STREAM. I accept the general direction of this. You can send a PR. Though I don't like the extra steps to get at this. You don't need all that padding infrastructure or buffer bouncing. Just patch CreateRstStream and put in an even manually crafted byte sequence and it will work. |
I've created a PR (#85). Besides the changes mentioned above, I also padded WINDOW_UPDATE like what I did for RST_STREAM. |
Except for the camouflage preamble, the padding protocol and other UI are updated:
To test: I created a build here https://github.com/klzgrad/naiveproxy/releases/tag/v83.0.4103.61-3. Run with bare options:
Build and run Caddy v2 with naivety:
caddy.json (static certificate): {
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [":443"],
"routes": [{
"handle": [{
"handler": "forward_proxy",
"hide_ip": true,
"hide_via": true,
"auth_user": "user",
"auth_pass": "pass",
"probe_resistance": {"domain": "secret.localhost"}
}]
}, {
"match": [{"host": ["example.com"]}],
"handle": [{
"handler": "file_server",
"root": "/var/www/html"
}],
"terminal": true
}],
"tls_connection_policies": [{
"match": {"sni": ["example.com"]},
"certificate_selection": {"any_tag": ["cert0"]}
}]
}
}
},
"tls": {
"certificates": {
"load_files": [{
"certificate": "example.com.crt",
"key": "example.com.key",
"tags": ["cert0"]
}]
}
}
}
} caddy.json (Let's Encrypt): {
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [":443"],
"routes": [{
"handle": [{
"handler": "forward_proxy",
"hide_ip": true,
"hide_via": true,
"auth_user": "user",
"auth_pass": "pass",
"probe_resistance": {"domain": "secret.localhost"}
}]
}, {
"match": [{"host": ["example.com", "www.example.com"]}],
"handle": [{
"handler": "file_server",
"root": "/var/www/html"
}],
"terminal": true
}],
"tls_connection_policies": [{
"match": {"sni": ["example.com", "www.example.com"]}
}]
}
}
},
"tls": {
"automation": {
"policies": [{
"subjects": ["example.com", "www.example.com"],
"issuer": {
"email": "[email protected]",
"module": "acme"
}
}]
}
}
}
} |
Haven't got any issues since update. Considered fixed. |
#72 discovers Caddy does not pass backend server's headers in 200 OK response, rendering header padding ineffectual. To actually fix this I have to create a Naive fork of Caddy's forwardproxy. Since I'm to fork and maintain a Naive forwardproxy, it's possible to simply subsume all of Naive server into this forwardproxy, i.e. the payload padding encapsulation layer.
This means a major architectural change, as the most users should no longer need to run the naive binary on their server but have to build the Naive fork of Caddy forwardproxy, which though is easy to do. Now it is:
[Browser → Naïve (client)] ⟶ Censor ⟶ [Caddy with forwardproxy Naïve fork] ⟶ Internet
Also the use case of HAProxy still has to be supported, as it doesn't do forward proxying itself. So the old functionality will not be removed.
[Browser → Naïve (client)] ⟶ Censor ⟶ [HAProxy → Naïve (server)] ⟶ Internet
Another medium change I'm testing is turning on payload padding by default through auto-negotiation.
Padding: ...
does not turn on payload padding server-side.With these I'm removing the option
--padding
, which proves to be error-prone for some users.The text was updated successfully, but these errors were encountered: