Today we use codeql to analyze security issues such as "cookie is not enabled httponly", thereby deepening our use of codeql. If it works well, you can consider fixing other vulnerabilities in Vulnerability-goapp.
You must additionally download codeql-go when analyzing go programs
Vulnerability-goapp: Vulnerable golang Web application for education.
Since all cookies in this project do not have the http-only attribute set, we need to modify them first before comparison. The following is a rewrite of the original sentence: Record modification: Add http-only option to some cookie settings.
pkg\admin\admin.go is modified as follows.
pkg\login\login.go is modified as follows.
pkg\register\register.go is modified as follows.
Remember to regenerate the database after modification (if you need to overwrite the old DATabase, you need to delete the old one first and then generate a new one.
It is to use the codeql script to find the loopholes in which httponly is not set and httponly is set but the value of httponly is false (usually this is not the case, but it is not guaranteed).
Sink is very simple. When setting Cookie, you need to use the http.SetCookie method, and the Cookie value that needs to be set is the second parameter of this function, and then we can write Find the query statement of Sink like this.
import go from DataFlow::Node sink where exists(DataFlow::CallNode c | c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = sink ) select sink
After running, you can get the following results. Click on any item to jump to the code segment of the compound requirement.
We will It is converted into a Sink class, as follows.
private class Sink extends DataFlow::Node { Sink() { exists(DataFlow::CallNode c | c.getTarget().hasQualifiedName("net/http", "SetCookie") and c.getArgument(1) = this ) } }
In this way, if we define a variable as Sink, it refers to all code fragments that meet the conditions, for example:
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
After running, we will get The same result.
Then let’s determine the Source. Judging from the parameters received by the http.SetCookie method, the actual second parameter is a pointer to the structure that receives a Cookie.
So we first need to find such a structure. We can first list all the structures in the project.
About the structure in codeql-go The definition of the body is as follows.
So our query script for example.
import go from StructLit source select source
also lists all the structures as we expected.
Then the next step is to eliminate other irrelevant content and restrict the type.
Regarding the hasQualifiedName method, various types in various Codeql-go have the same method. The definition is as follows. The marked object belongs to which package and what is its name.
If you are not sure, you can print the relevant fields through getPackage and getName, for example.
import go from StructLit source // where source.getType().hasQualifiedName("net/http", "Cookie") select source.getType().getPackage(), source.getType().getName()
The results are as follows.
We can find the source definition, for example.
import go from StructLit source where source.getType().hasQualifiedName("net/http", "Cookie") select source
It is also converted into a subclass of DataFlow::Node.
private class Source extends DataFlow::Node { Source() { exists(StructLit s | s.getType().hasQualifiedName("net/http", "Cookie") and this.asExpr() = s) } }
With Source and Sink, simply define TaintConfig to get all the data flow from Source to 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
The results are as follows:
However, we still have not removed the part where httponly=true is set. Therefore, it is necessary to add limiting conditions, that is, data streams with the HttpOnly attribute set to true are excluded from the results.
We can use TaintTracking::isSanitizer provided by CodeQL to filter harmless nodes:
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"
最终筛选出存在问题的内容。
The above is the detailed content of How Codeql analyzes the problem of cookie not enabling httponly. For more information, please follow other related articles on the PHP Chinese website!