zookeeper的未授權訪問漏洞解決


zookeeper的基本情況

zookeeper是分布式協同管理工具,常用來管理系統配置信息,提供分布式協同服務。zookeeper官網下載軟件包,bin目錄下有客戶端腳本和服務端腳本。另外還有個工具對理解和使用zookeeper服務非常有用,即zk-ui,該工具是zk服務端的可視化工具,可在web界面對服務端進行操作。

zookeeper以樹狀結構保存數據,我們完全可以對比linux文件系統理解zookeeper的文件系統。不同點在於linux下的每個目錄名對應一個znode。
zk-file-system.JPG
znode是zk的基本單元,可以存在數據信息、版本信息等等。如圖,/是zookeeper的根節點,A、B、C和D均為znode。

zookeeper存在的問題

我們在使用zookeeper提供的服務的時候會發現,只要知道zk服務端的IP和Port,任務用戶或者客戶端根本不需要任何的認證就可以連上zk的服務端,並且可以對znode進行增刪等操作。這樣數據是非常不安全的,極易被攻擊和篡改。

zookeeper解決這個問題的手段是ACL(access control list)機制,即訪問控制列表。

zookeeper的ACL機制

zookeeper通過ACL機制控制znode節點的訪問權限。

首先介紹下znode的5種操作權限:
CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、刪、改、查、管理權限,這5種權限簡寫為crwda(即:每個單詞的首字符縮寫)
注:這5種權限中,delete是指對子節點的刪除權限,其它4種權限指對自身節點的操作權限

身份的認證有4種方式:
world:默認方式,相當於全世界都能訪問
auth:代表已經認證通過的用戶(cli中可以通過addauth digest user:pwd 來添加當前上下文中的授權用戶)
digest:即用戶名:密碼這種方式認證,這也是業務系統中最常用的
ip:使用Ip地址認證

本文以digest認證方式展開說明。我們在zk的客戶端可以進行節點權限的查看和設置。

[zk: localhost:2181(CONNECTED) 3] create /test data
Created /test
[zk: localhost:2181(CONNECTED) 2] getAcl /test
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 3] addauth digest user:password
[zk: localhost:2181(CONNECTED) 4] setAcl /test auth:user:password:cdrwa
[zk: localhost:2181(CONNECTED) 5] getAcl /test
'digest,'user:V28q/NynI4JI3Rk54h0r8O5kMug=
: cdrwa    

從上述操作可以看出,zk新創建的znode默認訪問方式為world。我們通過addauth和setAcl給/test節點設置訪問權限為digest,操作權限為cdrwa,用戶名為user,密碼為password。

當然,我們使用getAcl是無法獲取可訪問用戶test的明文密碼的(要是可以獲取明文密碼,不又是個漏洞嘛~~)。

另啟zk客戶端,執行ls /test,發現當前用戶已經無法訪問/test節點,提示信息為“Authentication is not valid”。解決方法就是addauth添加認證用戶了,並且必須使用用戶名和密碼明文進行認證。

[zk: localhost:2181(CONNECTED) 0] ls /test
Authentication is not valid : /test
[zk: localhost:2181(CONNECTED) 4] addauth digest user:password
[zk: localhost:2181(CONNECTED) 5] ls /test
[] 
[zk: localhost:2181(CONNECTED) 6] create /test/leaf data
Created /test/leaf
[zk: localhost:2181(CONNECTED) 7] getAcl /test/leaf
'world,'anyone
: cdrwa

addauth添加digest認證用戶user后,即可正常訪問/test節點了。

另外,還有一點需要注意,znode的ACL是相互獨立的。也就是說,任意不同節點可以用不同的acl列表,互不影響,並且ACL是不可被繼承的。
我們在/test下創建leaf節點,可發現,leaf節點的認證方式為world,即任何用戶都有訪問權限。

使用Java解決zk的未授權訪問漏洞

還是以digest為例:

//給密碼加密
public String getDigestUserPswd(String idPassword) throws NoSuchAlgorithmException {
return DigestAuthenticationProvider.generateDigest(idPassword);
}
//獲取ACL列表,這里只設置一個可訪問用戶,用戶名為user,密碼為pswd。如果你需要多個,繼續add即可。
public List<ACL> getAclList() {
String idPassword = "user:pswd";
if (idPassword == null) {
logger.warn("no digest config,so use world scheme");
return ZooDefs.Ids.OPEN_ACL_UNSAFE;
}
List<ACL> aclList = new ArrayList<>();
try {
Id zkUser = new Id("digest", getDigestUserPswd(idPassword));
ACL acl = new ACL(ZooDefs.Perms.ALL, zkUser);
aclList.add(acl);
} catch (NoSuchAlgorithmException e) {
logger.error(e);
}
return aclList;
}
//給znode設置權限,只有aclList的用戶可以訪問
public void addDigestScheme(){
zk.setACL(znode, aclList, -1);
}
//創建znode的時候設置ACL
zk.create(znode,data,aclList,...)
//如何訪問加密的znode
public void client(){
ZooKeeper zk = new ZooKeeper(xxx)
zk.addAuthInfo("digest","user:pswd".getBytes())
//然后zk就可以訪問加密znode了
}

上面的代碼僅說明了核心步驟。
我在這里遇到個大坑,就是idpasswod為空的情況,之前直接給返回空了,feature正常啟動,但是服務沒有成功的發布出去。重要的是構建環境把我在配置文件配置的user信息給刪掉了(我不知道)才開始跑的,更坑的是它運行完后直接清除日志了,哪里錯了都看不到。
最后哼哧哼哧的啃代碼,終於定位到位置。
血淚教訓:空指針情況需要正確處理,別TMD的隨便返回空,運行沒問題不代表功能沒問題。

到這里,我們在create節點的時候可以為所有的znode設置訪問權限,理論上可以保護我們的數據安全了。

漏洞掃描驗證

上面我們在創建znode的時候進行了加密,這樣總可以通過漏洞掃描了吧。
重點來了。

但是如果你使用漏洞掃描工具掃描的話,還是有未授權訪問漏洞的,為啥呢?
不知道你有沒有注意,zk服務端啟動后,默認會啟動這幾個具有world和cdrwa權限的znode,“/” "/zookeeper" "/zookeeper/config"和"/zookeeper/quota"(根據zookeeper的版本不同可能存在不同,並且這幾個節點雖然具有world和cdrwa權限,但是是無法刪除的,不知道為什么,好在我們可以給它設置ACL列表。另外,官網對着幾個節點也沒有特別說明,估計和zk本身的一些配置相關吧,不刪除最好)。就是這幾個znode,會導致你的產品無法通過安全工具的漏洞掃描,你說坑不坑,解決辦法也是很簡單的,用我們前面說過的zk.setACL為這幾個節點設置權限就OK了,千萬別忘記根節點"/"了。

好了,到這里,才是真正的解決了這個未授權訪問漏洞問題了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM