---
title: 使用DOH和ECH防止DNS污染和劫持
published: 2025-03-14
tags: [网络, 教程]
category: 教程
draft: false
---

### 前言
DoH（DNS over HTTPS）是一种安全协议，它通过 HTTPS（TLS 加密）传输 DNS 解析请求，防止 DNS 查询被窃听、篡改或屏蔽。

正常情况下，当你访问一个 `HTTPS` 网站时，需要先向 `DNS` 发送查询该域名 `IP` 地址的请求。由于传统 `DNS` 查询是明文传输的，ISP 或其他网络中间人可以看到你查询了哪个域名。虽然 `HTTPS` 内容是加密的，无法看到具体内容，但可以得知你在访问什么网站。

另外，访问 `HTTPS` 网站需要进行 `TLS` 握手，握手时会携带域名信息，也就是 `SNI`（Server Name Indication）。`ISP` 同样能看到这个信息。

此时，中间人可以劫持并篡改 `DNS` 查询返回的 `IP` 地址。

当我们使用加密 `DNS` 后，可以隐藏 `DNS` 查询的域名信息，但由于 `TLS` 握手仍会携带 `SNI` 信息，中间人依然能够监控。为了进一步保护隐私，网站可以配置 `ECH`（Encrypted Client Hello）来加密 `SNI` 信息。

目前 `Cloudflare` 已支持 `ECH`，使用其 `CDN` 服务的网站会默认开启此功能。`ECH` 会加密真实的 `SNI` 信息，只有支持 `ECH` 的服务器才能解密，从而实现对真实访问目标的隐藏。

`ECH` 配合 `DoH` 加密 DNS 可以提供更完整的隐私保护。

### 享受ECH加密的一些注意事项

- 服务端域名开启了ECH
- 服务端域名支持TLS 1.3
- 域名托管商支持HTTPS解析
- 客户端使用国际主流浏览器
- 客户端不要用代理
- 客户端使用加密DNS

### `Windows 11` 配置加密 DNS

由于网络环境限制，国外的加密 `DNS` 服务可能无法直接访问。本文教大家配置阿里云的加密 `DNS` 服务。

**系统设置方法：**
1. 打开 `设置` > `网络和 Internet`
2. 找到当前网络连接的 `属性`
3. 在 `DNS 服务器分配` 中选择 `手动`
4. 首选 DNS 填入 `223.5.5.5`
5. `DNS over HTTPS` 选择 `手动模板`
6. 模板地址填入 `https://dns.alidns.com/dns-query`

**控制面板配置方法：**
1. 打开控制面板，选择 `查看网络状态和任务`
2. 点击你的网卡连接
3. 选择 `属性`
4. 双击 `Internet 协议版本 4 (TCP/IPv4)`
5. 点击 `属性`
6. 首选 DNS 填入 `223.5.5.5`，备选 DNS 填入 `8.8.8.8`
7. 点击 `确定` 保存设置

配置完成后，你的 DNS 查询将通过加密通道进行，提升上网安全性和隐私保护。


### 验证是否开启

访问域名 https://cdnjs.com/cdn-cgi/trace 查看`sni`参数，如果是图中这样，则代表开启成功。

![图片](./png/dns2.jpg)


或者也可以访问https://tls-ech.dev 如果如图所示则代表开启成功。

![图片](./png/dns1.jpg)


### 服务端域名配置ECH教程

由于Cloudflare CDN国内访问速度慢，所以我介绍一下不使用CDN的教程

我使用caddy较多，所以用caddy做演示，其他同理

`caddy`的 [2.10.0正式版本](https://github.com/caddyserver/caddy/releases/tag/v2.10.0) 已经加入了ECH的支持，需要使用`2.10.0`以上的版本。

要使用`caddy`自动配置`ech`则需要编译`cloudflare dns`插件，用于caddy自动添加DNS记录，官方编译的包默认不带此插件。

### 编译`caddy`并加入`cloudflare DNS`插件的支持
1：安装`go`环境

2：下载`xcaddy`
```
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
```
3：编译带`cloudflare dns`插件的`caddy`
```
xcaddy build --with github.com/caddy-dns/cloudflare
```
此时你的当前目录会生成一个已经包含了`cloudflare dns`插件的`caddy`二进制文件

### 以`debug`模式测试运行
创建`/etc/caddy/Caddyfile`文件，并写入以下文件
```
{
    debug
    dns cloudflare xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    ech ech.example.com

    log {
        output stdout
        format console
        level DEBUG
    }
}

test.example.com {
    root * /tmp
    file_server browse
    encode zstd gzip
}
```
#### 配置说明
`dns cloudflare`这个配置填CF的`区域 DNS API 令牌`

`ech ech.example.com`替换你的任意二级域名，并且解析到你的IP，用于发布ECH公钥，或者第三方提供的ECH配置服务域名也行。

`test.example.com`替换你的域名，并且解析到你的IP，不要开小黄云。

`root * /tmp`这个配置代表以网页浏览文件的形式，浏览你服务器的`/tmp`目录，这里只是测试为了方便起服务。

### 启动`caddy`
```
sudo ./caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
```
不出意外的话就启动成功了，然后可以访问你的域名看下是否能成功访问

并且`/root/.local/share/caddy`目录会生成一个锁文件，如果需要重新部署则需要删除这个目录

CF里也会自动添加一个类型为`HTTPS`的解析记录

### 检查`ech`是否配置成功
```
dig test.example.com TYPE65
```
执行这个命令后，在输出的信息里可以看到有如下类似的信息就代表启用成功
```
ech=AEr+DEBUGAgACBDEMOnixVAfd/ASODJKADKSODAPSkZIwAMADQABAAIASDASDADHw9lY2guZXhhbXBsZS5jb20AAA==
```

### 生产环境
移动二进制文件
```
mv ~/caddy /usr/bin/caddy
```
创建系统服务配置
```
touch /etc/systemd/system/caddy.service
```
写入配置
```
[Unit]
Description=Caddy Web Server
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Wants=network-online.target

[Service]
User=root
Group=root
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
```
管理命令
```
# 重新加载 systemd 配置
sudo systemctl daemon-reexec
sudo systemctl daemon-reload

# 启动 Caddy
sudo systemctl start caddy

# 设置开机启动
sudo systemctl enable caddy

# 查看运行状态
sudo systemctl status caddy
```

### 生产环境反代配置示例
```
{
	dns cloudflare xxxxxxxxxxxxxxxxxxxxx
	ech ech.example.com
}

test.example.com {
	reverse_proxy localhost:8080
}
```

重新部署记得删除：`/root/.local/share/caddy`锁文件，和CF里的`HTTPS`解析记录
