802.1X 账号密码+设备信息双重认证
名词解释
802.1X:
IEEE802 LAN/WAN 委员会为解决无线局域网网络安全问题,提出了 802.1X 协议。后来,802.1X协议作为局域网端口的一个普通接入控制机制在以太网中被广泛应用,主要解决以太网内认证和安全方面的问题。802.1X 协议是一种基于端口的网络接入控制协议(port based network access control protocol)。“基于端口的网络接入控制”是指在局域网接入设备的端口这一级对所接入的用户设备进行认证和控制。连接在端口上的用户设备如果能通过认证,就可以访问局域网中的资源;如果不能通过认证,则无法访问局域网中的资源。
EAP协议:
EAP(Extensible Autllentication Protocol) 协议是 802.1X协议定义的一种报文封装格式,主要用于在客户端和设备端之间传送EAP协议报文,以允许EAP协议报文在LAN上传送。目前可以采用的EAP类型包括:EAP-MD5、LEAP、PEAP、EAP—TLS 等方式。本文主要使用的协议为EAP-MD5协议,进行802.1X网络认证。
FreeRADIUS:
RADIUS认证服务器(Remote Authentication Dial In User Service,远程用户拨号认证系统)是目前应用最广泛的AAA协议(AAA=authentication、Authorization、Accounting,即认证、授权、计费),FreeRADIUS包含一个radius服务器和radius-client,可以对支持radius协议的网络设备进行鉴权记账。
RSA非对称加密:
对称加密算法在加密和解密时使用的是同一个秘钥,与对称加密算法不同,非对称加密算法需要两个密钥:[公开密钥](publickey)和[私有密钥](privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
问题&&现状
目前802.1X网络认证中EAP-MD5协议主要流程就是客户端发送用户账号和经过MD5加密后的密码到RADIUS认证服务器,RADIUS服务通过调用 OpenLDAP/Mysql 等组件,查询对应的用户是否存在,并校验密码是否一致,来判断用户是否合法,然后回应认证成功/失败报文到接入设备,接入设备根据RADIUS服务器响应的报文,向客户端响应成功/失败状态,并设置端口授权状态,从而控制用户是否能通过端口访问网络。
仅针对账号密码进行认证,这样的验证方式单一而且缺乏力度,忽略了终端安全。按照目前的认证方式,一旦用户密码不慎泄漏,非法用户可以通过不可信的终端接入网内,从而存在很多安全隐患。在服务器、网络、终端这样三位一体的计算机通信系统上,终端是最为庞大的主体,而且攻击也多半是从终端发起的。终端将成为我们安全体系中越发不可忽视的一环。只要保证目前系统中的所有主体都是安全可信赖的,而新的主体在加入网络时也通过了安全性验证,那么无论网络规模如何发展,整个体系的安全性都能得到很好的保障。
技术方案
为了满足网络准入安全性要去,本方案主要是在原有的EAP-MD5协议账号密码单一认证基础上,再通过验证终端设备的硬件信息(MAC地址,CPU序列号,硬盘序列号等),在进行802.1X网络认证的时,实现 账号密码+设备信息 双重认证,从而提高对接入用户与终端设备的可信性和可控性管理。
整体设计
802.1X账号密码+设备信息认证总体可以划分为下面几个模块
- 设备认证服务模块:进行设备信息的录入,并进行设备证书校验等功能
- 客户端模块:客户端模块主要是发起经过改造后的802.1X认证协议
- freeradius认证服务模块:freeradius服务模块主要负责调用OpenLDAP/Mysql进行账号密码认证,并转发EAP-MD5协议相关信息到设备认证服务端模块,从而完成设备信息的校验
改造前 EAP-MD5认证 流程如下
如图所示:
- 当用户有访问网络需求时打开 802.1X 客户端程序,输入已经申请、登记过的用户名和码,发起连接请求(EAPOL-Start 报文)。此时,客户端程序将发出请求认证的报文给设备端,开始启动一次认证过程。
- 设备端收到请求认证的数据帧后,将发出一个请求帧(EAP-Request/Identity 报文)要求用户的客户端程序发送输入的用户名。
- 客户端程序响应设备端发出的请求,将用户名信息通过数据帧(EAP-Response/Identity 报文)发送给设备端。设备端将客户端发送的数据帧经过封包处理后(RADIUS Access-Request 报文)送给认证服务器进行处理。
- RADIUS 服务器收到设备端转发的用户名信息后,将该信息与数据库中的用户名表对比,找到该用户名对应的密码信息,用随机生成的一个加密字对它进行加密处理,同时也将此加密字通过 RADIUS Access-Challenge 报文发送给设备端,由设备端转发给客户端程序。
- 客户端程序收到由设备端传来的加密字(EAP-Request/MD5 Challenge 报文)后,用该加密字对密码部分进行加密处理(此种加密算法通常是不可逆的),生成 EAP-Response/MD5 Challenge 报文,并通过设备端传给认证服务器。
- RADIUS 服务器将收到的已加密的密码信息(RADIUS Access-Request 报文)和本地经过加密运算后的密码信息进行对比,如果相同,则认为该用户为合法用户,反馈认证通过的消息(RADIUS Access-Accept 报文和 EAP-Success 报文)。
改造后 账号密码+设备信息认证 流程如下
如图所示:
- 在客户端发起802.1X网络认证之前,设备认证服务端需要主动采集终端设备相关信息(MAC地址,CPU序列号,硬盘序列号等),通过SHA256摘要算法,生成设备信息摘要,将设备相关信息保存到设备认证服务端数据库中
- 通过改造原有802.1X 客户端程序,在改造前流程第5步,发送 MD5 Challenge 的同时,读取出端设备相关信息并生成设备信息摘要,并通过RSA非对称加密算法,加密摘要信息,通过EAP-MD5协议扩展字段 EAP-MD5 Extra Data 传递到freeradius认证服务器
- freeradius认证服务器通过配置开启自身rlm_rest模块,将加密后的设备摘要信息转发到设备认证服务模块
- 设备认证服务模块读取加密后的摘要信息,通过私钥进行解密,然后在认证服务端数据库中进行查询,如果可以匹配到同样的摘要信息,则表示该终端设备信息合法,返回成功状态码给freeradius认证服务器,freeradius认证服务器会结合账号密码认证结果统一判断,如果 账号密码+设备信息 都认证通过,则允许用户访问网络,否则不允许用户访问网络
关键代码
客户端相关代码
func (h *Handle) SendResponseMD5Chall(id uint8, salt, user, pass []byte) error {
// MD5 Challenge MD5加密后的密码信息
plain := []byte{id}
plain = append(plain, pass...)
plain = append(plain, salt[:0x10]...)
cipher := md5.Sum(plain)
data := append([]byte{uint8(len(cipher))}, cipher[:]...)
//初始化密钥对
rsaCryptoKey := GenRasKeyFromPem("./config/client_private.pem", "./config/server_public.pem")
//获取设备信息摘要(64位字符串)
deviceInfo := GetSHA256DeviceInfo()
//加密设备信息摘要
enc, err := rsaCryptoKey.RsaEncrypt([]byte(deviceInfo))
if err != nil {
return err
}
//base64编码
base64ExtarData := base64.StdEncoding.EncodeToString(enc)
//将加密后的设备信息摘要加入到EAP请求包内
data = append(data, []byte(base64ExtarData)...)
eth := layers.Ethernet{
SrcMAC: h.srcMacAddr,
DstMAC: h.dstMacAddr,
EthernetType: layers.EthernetTypeEAPOL,
}
eapol := layers.EAPOL{
Version: 0x01,
Type: layers.EAPOLTypeEAP,
Length: uint16(5 + len(data)),
}
eap := layers.EAP{
Code: layers.EAPCodeResponse,
Id: id,
Type: layers.EAPTypeOTP,
TypeData: data,
Length: eapol.Length,
}
if err := h.send(ð, &eapol, &eap, &fillLayer); err != nil {
return err
}
return nil
}
设备认证服务模块
//授权操作
// http.StatusNoContent 对应freeradius ok 状态
// http.StatusUnauthorized 对应freeradius reject 状态
func Authorize(c *gin.Context) {
var (
err error = nil
data []byte = nil
auth model.AuthorizeInfo = model.AuthorizeInfo{}
)
//读取freeradius rest模块转发的消息
data, err = ioutil.ReadAll(c.Request.Body)
if err != nil {
c.JSON(http.StatusUnauthorized, model.ErrResultModel(model.ParamErr, err.Error()))
return
}
log.Info("Authorize data:%v", string(data))
err = json.Unmarshal(data, &auth)
if err != nil {
c.JSON(http.StatusUnauthorized, model.ErrResultModel(model.ParamErr, err.Error()))
return
}
if len(auth.EAPMessage) <= 46 {
c.JSON(http.StatusUnauthorized, model.ErrResultModel(model.ParamErr, "Authorize wrong length"))
return
}
//EAPMessage消息结构:前14位为请求头信息,15-46中间32位是MD5加密后的密码信息,46-末尾是Extra Data 扩展字段的信息
extarData := auth.EAPMessage[46:]
log.Info("Authorize extarData:%v", extarData)
//解析设备摘要信息
decHexExtarData, err := hex.DecodeString(extarData)
if err != nil {
c.JSON(http.StatusUnauthorized, model.ErrResultModel(model.ParamErr, err.Error()))
return
}
decBase64ExtarData, err := base64.StdEncoding.DecodeString(decHexExtarData)
if err != nil {
c.JSON(http.StatusUnauthorized, model.ErrResultModel(model.ParamErr, err.Error()))
return
}
//解密设备摘要信息
deviceInfo, err := config.RsaCryptoKey.RsaDecrypt(decBase64ExtarData)
if err != nil {
c.JSON(http.StatusUnauthorized, model.ErrResultModel(model.ParamErr, err.Error()))
return
}
//校验设备摘要信息是否在数据库内
_, err = dao.IsExist(deviceInfo)
if err != nil {
c.JSON(http.StatusUnauthorized, model.ErrResultModel(model.ParamErr, err.Error()))
return
}
//校验设备摘要信息通过则响应 http.StatusNoContent 204 状态
c.JSON(http.StatusNoContent, model.OkResultModel("success"))
return
}
freeradius认证服务模块
freeradius认证服务模块主要为相关配置,以实现认证消息转发的功能
编译安装
rlm_rest 模块默认是不会安装的,需要手动编译安装 freeradius 服务,编译安装freeradius-rest 模块时,需要进入 /src/modules/rlm_rest 模块执行 ./configure 检查是否确少依赖包,安装完对应的依赖包,否则会导致 freeradius 安装成功,而 rlm_rest 模块编译失败的问题
配置rest模块
- 通过软连接启用rest模块
ln -s /usr/local/etc/raddb/mods-available/rest /usr/local/etc/raddb/mods-enabled/rest
- 修改rest相关配置
vim /usr/local/etc/raddb/mods-enabled/rest
//设备认证服务网关
connect_uri = "http://127.0.0.1:2017"
//设置转发参数和具体URL
authorize {
uri = "${..connect_uri}/user/authorize"
method = \'post\'
body = \'json\'
data = \'{"user_name":"%{User-Name}","eap_message":"%{EAP-Message}","mac_id":"%{Calling-Station-Id}"}\'
tls = ${..tls}
}
- 修改default文件,认证流程添加rest模块
vim /usr/local/etc/raddb/sites-enabled/default
authorize{
rest
}
authenticate{
Auth-Type rest{
rest
}
}
accouting{
rest
}
- 重启 freeradius认证服务 即可实现认证请求的转发
实验验证
环境搭建:需要一台UOS客户端两台(一台设备信息已录入,一台设备信息未录入,安装经过改造后的802.1X协议的客户端),一台UOS服务端(编译安装freeradius),一台支持802.1X协议交换机(设置端口开启802.1X协议认证)
场景一:测试 账号密码正确+设备信息未登记 场景
如图所示:
客户端2发起802.1X认证请求,账号为:yangyi,密码为:testing,设备摘要信息为:a4de2da2c87361550d24110dd18eb4c021e20db461102e677683aab5490e3484,在 EAP-MD5 请求 Response MD5-Challenge 节点,将经过加密后的摘要信息发送到设备认证服务器端,因为此设备摘要信息未在设备认证服务器端登记,所以交换机响应 Failure 状态,用户入网失败
场景二:测试 账号密码正确+设备信息已登记 场景
如图所示:
客户端1发起802.1X认证请求,账号为:yangyi,密码为:testing,设备摘要信息为:21e20db461102e677683aab5490e3484a4de2da2c87361550d24110dd18eb4c0,在EAP-MD5 请求 Response MD5-Challenge 节点,将经过加密后的摘要信息发送到设备认证服务器端,因为此设备摘要信息已在设备认证服务器端登记,所以交换机响应 Success 状态,用户入网成功
小结
以上介绍了802.1X基于端口访问控制协议的相关技术,提出并实现基于账号密码+设备信息双重认证的方案,以及对802.1X协议的扩展。整个系统是在Linux平台上基于802.1X认证的开源客户端工具和服务器端软件FreeRADIUS进行新功能开发和改进,并最终实现了用户账号密码认证和设备信息双重认证的功能。所实现的系统在用户发起入网请求时,客户端软件会自动获取终端设备信息,并生成设备信息摘要,和经过MD5加密后密码,一起发送到FreeRADIUS认证服务器,使得FreeRADIUS服务器在完成对用户身份验证的同时也可以完成对终端设备信息的可信认证。最后的测试结果显示,该方案可以满足可信网络连接的基本功能需求,将来可以实际应用于可信网络的接入控制。
但目前的该方案也存在以下一些不足之处:
- EAP—MD5协议目前只能支持有线网络接入认证,对于无限网络接入认证,还需要进一步的研究
- 设备扩展信息虽然通过RSA非对称加密后的密文进行传输,但公钥保存在客户端,可能存在泄漏的风险,可以考虑对公钥进行密码保护,或者需要管理员权限才可以读取到公钥信息,或者直接由服务端下发证书给客户端的方案