今天我們利用codeql分析下「cookie未啟用httponly「這類的安全性問題,因此加深自己對codeql的使用。如果效果良好,可以考慮修復Vulnerability-goapp的其他漏洞。
分析go程式時必須額外下載codeql-go
Vulnerability-goapp:Vulnerable golang Web application for education。
由於在該專案中所有的 Cookie 都沒有設定 http-only 屬性,因此我們需要先修改才能進行比較。以下是對原句的重寫: 記錄修改:在某些cookie設定中增加http-only選項。
pkg\admin\admin.go修正如下。
pkg\login\login.go修正如下。
pkg\register\register.go修改如下。
修改後記得重新產生一次database(如果需要覆蓋舊的DATabase的話,則需要先刪除舊的再產生新的。
就是透過codeql腳本來發現其中未設定httponly和設定了httponly的但httponly的值為false(一般不會這樣,但保不齊有)的這樣存在漏洞的點。
Sink很簡單,設定Cookie時,需要用到http.SetCookie方法,而需要設定的Cookie值是這個函數的第二個參數,然後我們可以寫出找到類似這樣Sink的查詢語句。
import go from DataFlow::Node sink where exists(DataFlow::CallNode c | c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = sink ) select sink
運行後可獲得以下結果,點擊任意條目都會跳到複合要求的程式碼段下。
private class Sink extends DataFlow::Node { Sink() { exists(DataFlow::CallNode c | c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = this ) } }這樣之後我們透過將變數定義成Sink的話,就是指符合條件的所有程式碼片段,例如:
import go private class Sink extends DataFlow::Node { Sink() { exists(DataFlow::CallNode c | c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = this ) } } from Sink s select s運行後會獲得同樣的結果。Source定義然後我們再來確定Source,從http.SetCookie方法接收的參數來看,實際第二個參數是接收一個Cookie的結構體的指標。
import go from StructLit source select source也如我們預期的一樣列出了所有的結構體。
import go from StructLit source // where source.getType().hasQualifiedName("net/http", "Cookie") select source.getType().getPackage(), source.getType().getName()結果如下。
import go from StructLit source where source.getType().hasQualifiedName("net/http", "Cookie") select source
同樣轉換成DataFlow::Node的子類別。
private class Source extends DataFlow::Node { Source() { exists(StructLit s | s.getType().hasQualifiedName("net/http", "Cookie") and this.asExpr() = s) } }TaintConfig定義簡單的資料流有了Source和Sink,簡單定義TaintConfig,就能獲得所有從Source到Sink的資料流。
import go private class Source extends DataFlow::Node { Source() { exists(StructLit s | s.getType().hasQualifiedName("net/http", "Cookie") and this.asExpr() = s) } } private class Sink extends DataFlow::Node { Sink() { exists(DataFlow::CallNode c | c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = this ) } } class Configuration extends TaintTracking::Configuration { Configuration() { this = "HttpOnly" } override predicate isSource(DataFlow::Node source) { source instanceof Source } override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } } from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink where cfg.hasFlowPath(source, sink) select source, sink結果如下:
override predicate isSanitizer(DataFlow::Node node) { exists(Write w, Field f, DataFlow::Node rhs | f.hasQualifiedName("net/http", "Cookie", "HttpOnly") and w.writesField(node, f, rhs) and rhs.getBoolValue() = true ) }
运行结果如下,但有一处地方需要注意。
红框中实际有对HttpOnly进行设置,但我们的脚本并不能识别这样的一个数据流。后面试了各种方法,最终找到一种解决方式,将isSanitizer修改成以下内容。
override predicate isSanitizer(DataFlow::Node node) { exists(Write w, Field f, DataFlow::Node n, DataFlow::Node rhs | f.hasQualifiedName("net/http", "Cookie", "HttpOnly") and w.writesField(n, f, rhs) and rhs.getBoolValue() = true and node = n.getAPredecessor*()n ) }
其中node=n.getAPredecessor*()
是说node是n的前置数据流节点,数据可以在0个或多个步骤中从node流到n。
加上一些信息,模仿官方的示例,最终脚本如下。
/** * @name Cookie未设置httponly * @description Cookies包含一个HTTPOnly的设置选项,可以使此cookie不能被js读取,而只能用于HTTP请求。 * @kind path-problem * @problem.severity error * @precision low * @id go/Cookie-not-set-httponly * @tags security */ import go import DataFlow::PathGraph private class Source extends DataFlow::Node { Source() { exists(StructLit s | s.getType().hasQualifiedName("net/http", "Cookie") and this.asExpr() = s) } } private class Sink extends DataFlow::Node { Sink() { exists(DataFlow::CallNode c | c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = this ) } } class Configuration extends TaintTracking::Configuration { Configuration() { this = "HttpOnly" } override predicate isSource(DataFlow::Node source) { source instanceof Source } override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } override predicate isSanitizer(DataFlow::Node node) { exists(Write w, Field f, DataFlow::Node n, DataFlow::Node rhs | f.hasQualifiedName("net/http", "Cookie", "HttpOnly") and w.writesField(n, f, rhs) and rhs.getBoolValue() = true and node = n.getAPredecessor*() ) } } from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink where cfg.hasFlowPath(source, sink) select sink.getNode(), source, sink, "Cookie-not-set-httponly in $@.", source.getNode(), "here"
最终筛选出存在问题的内容。
以上是Codeql如何分析cookie未啟用httponly的問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!