今日は、codeql を使用して「Cookie が httponly で有効になっていない」などのセキュリティ問題を分析し、codeql の使用法を深めます。うまく機能する場合は、Vulnerability-goapp の他の脆弱性を修正することを検討できます。
go プログラムを分析する場合は、codeql-go をさらにダウンロードする必要があります
Vulnerability-goapp: 教育用の脆弱な golang Web アプリケーション。
このプロジェクトのすべての Cookie には http のみの属性が設定されていないため、比較する前にまずそれらを変更する必要があります。以下は元の文を書き直したものです。 レコードの変更: 一部の Cookie 設定に http のみのオプションを追加します。
pkg\admin\admin.go を次のように変更します。
pkg\login\login.go を次のように変更します。
pkg\register\register.go を次のように変更します。
変更後に必ずデータベースを再生成してください (古い DATAbase を上書きする必要がある場合は、まず古い DATAbase を削除してから、新しい DATAbase を生成する必要があります。
codeql スクリプトを使用して、httponly が設定されていない、および httponly が設定されているが httponly の値が false である抜け穴を見つけることです (通常はそうではありませんが、保証されません)。
シンクは非常にシンプルで、Cookie を設定するときは、http.SetCookie メソッドを使用する必要があります。設定するのはこの関数の 2 番目のパラメータで、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
実行後、次の結果が得られます。任意の項目をクリックすると、その項目にジャンプします。
次のように 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
実行後、同じ結果が得られます。
次に、ソース。http.SetCookie メソッドで受け取ったパラメータから判断すると、実際の 2 番目のパラメータは、Cookie を受け取る構造体へのポインタです。
したがって、最初に見つける必要があります。まずはプロジェクト内のすべての構造をリストします。
codeql-go の構造について本文の定義は次のとおりです。たとえば、クエリ スクリプトです。
import go from StructLit source select source
次に、他の無関係なコンテンツを削除し、タイプを制限します。
hasQualifiedName メソッドについては、さまざまな Codeql-go のさまざまな型が同じメソッドを持っていますが、定義は次のようになります マークされたオブジェクトはどのパッケージに属し、その名前は何であるかということです。
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) } }
単純なデータ フロー
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
CodeQL が提供する TaintTracking::isSanitizer を使用して、無害なノードをフィルタリングできます:
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 中国語 Web サイトの他の関連記事を参照してください。