Frp的安全之路

3,451 阅读3分钟

前言

因为公司业务接触了一下Frp

一开始我们使用Frp的用法是非常危险的,因为我们是直接把内网机器的端口映射到了公网上,通过访问公网服务器的某一个端口,就直接访问到了内网机器,这种行为其实是非常危险的,因为如果有人去扫描一下公网服务器的端口,就可以直接访问到内网了

这种做法除了危险之外,还有另外一个问题:端口资源
一台服务器一共就只有65535个端口,除去一些系统默认占用的端口之外,其实留给Frps使用的端口已经所剩不多了

网络结构图大概如下:
内网的本地端口local_port通过Frpc_1连接到Frps(服务器),然后服务器会开放一个remote_port端口,这个时候通过访问服务器的remote_port就相当于访问内网的local_port

STCP

后来意识到了这个问题,我们就采用了另外一个做法,STCP(secret TCP)
这次我们双端都使用了Frpc

一开始还是和之前一样,先用frpc_1将本地端口local_port_1映射到frps上,但是这次Frps不开放remote_port了,如果想在另外一台PC上访问local_port_1则需要使用另外一个frpc_2将另外一个local_port_2一样映射到Frps上,然后就可以通过访问local_port_2实现访问local_port_1
其中我们会使用到两个frpc.ini
这里我给个example

# frpc_1.ini
[common]
server_addr = x.x.x.x
server_port = 7000
token = test_token

[stcp_example]
type = stcp
sk = abcdefg
local_ip = 127.0.0.1
local_port = 8080
# frpc_2.ini
[common]
server_addr = x.x.x.x
server_port = 7000
token = test_token

[stcp_example_visitor]
type = stcp
role = visitor
server_name = stcp_example
sk = abcdefg
bind_addr = 127.0.0.1
bind_port = 6000

对应到图中的话:

  • local_port_1 : 8080
  • local_port_2 : 6000

其中会有几个之前没有的配置变量

  • server_name: 服务名,相对于用户名,在frpc_1.ini中[...]中的内容就是server_name
  • sk: 相对于密码
  • role: 这里我们只使用到了visitor

加密Token

到这里,Frp就相对的安全了一点了,但是在解决问题的同时又出现了另外一个问题,frps的token可能暴露了!
场景是这样的:我们想把内网的一个端口暂时暴露到公网上,供客户使用
如果采用了STCP的方式,意味着客户也得使用Frpc,那么在配置文件上,客户就可以看到我们Frps对应的token了!
这同样也是一件非常危险的事情,除了安全方面的问题,还有资源的问题,因为知道了token,意味着客户可以直接连接到Frps,然后就开始白嫖
所以我们得想办法加密token
对此我查了很多Frp的资料,发现没有这个功能,token写在配置文件里,只要打开配置文件就一定能看到token,所以我们换了一个角度:加密配置文件

这个时候,流程应该是这样的:

  • 我们先通过STCP的方式将本地端口映射到了公网上
  • 写出对应的visitor的配置文件frpc.ini
  • 将frpc.ini文件进行加密(比如RSA),得到frpc_encrypt.ini
  • 然后修改Frpc源码,使Frpc读取到frpc_encrypt.ini的时候先进行解密,再执行之后的配置工作
  • 将我们定制化的Frpc_decrypt和加密后的配置文件frpc_encrypt.ini发给客户
  • 此时用户就可以通过frp访问我们的本地端口了

加密传输数据

到这一步,其实Frp还是不安全,因为我们的数据传输过程是有可能被抓包的!
所以我们需要对传输的数据进行加密,这里我使用了frp本身就支持的TLS

我们需要先生成对应的证书,然后在配置文件中配置

# frpc.ini
tls_enable = true
tls_cert_file = client.crt
tls_key_file = client.key
tls_trusted_ca_file = ca.crt
# frps.ini
tls_only = true
tls_enable = true
tls_cert_file = server.crt
tls_key_file = server.key
tls_trusted_ca_file = ca.crt

我们可以选择单向验证也可以选择双向验证,具体的验证就看我们的场景需求了

到这里,我才觉得Frp才相对安全了一点

如发现文章有错误、对内容有疑问,请邮件联系我:maipianlaoxu@gmail.com