ตรวจสอบ Lib ที่ใช้ว่ามีช่องโหว่หรือไม่ด้วย Dependency check

ตรวจสอบ Lib Java ที่ใช้ว่ามีช่องโหว่หรือไม่ด้วย Dependency check

Dependency Check

ในปัจจุบันเรื่อง Security ของ Software นั้นเริ่มถูกสนใจมากยิ่งขึ้น นั่นก็เพราะ Software นั้นถูกใช้กันมากขึ้น และถูกใช้ในงานสำคัญเรื่อยๆ เช่น ธุรกรรมทางการเงิน ดังนั้นถ้าตัว Software มีช่องโหว่ที่ให้ผู้ไม่หวังเข้ามาโจมตีได้ก็จะสร้างความเสียหายให้กับตัวผู้ใช้งานและบริษัทที่พัฒนา Software

ช่องโหว่หนึ่งที่ Developer นั้นต้องรับผิดชอบคือช่องโหว่ที่มาจาก Lib ที่เรานำมาใช้ ซึ่งเป็นเรื่องปกติในการพัฒนา Software ที่เราต้องนำ Lib มาใช้เพื่อลดเวลาในการพัฒนา แต่ Lib มันก็เป็น Code ซึ่งมันก็มีโอกาสจะมีช่องโหว่ ซึ่งเมื่อมันมีช่องโหว่ Software ที่เราเขียนก็จะมีช่องโหว่ตามทันที ซึ่งวิธีการแก้ไขสำหรับการที่ Lib มีช่องโหว่นั้นก็ไม่ยากครับ ก็แค่เข้าไป Update ตัว Lib ให้เป็น version ที่เขาแก้ไขเรื่องช่องโหว่นั้นแล้ว ซึ่ง Lib ที่เป็นที่นิยมมากๆ และมีผู้พัฒนาคอยดูแลจะรีบเข้ามาแก้ไขอย่างรวดเร็ว ดังนั้นถ้าเลือกจะใช้ Lib ตัวไหน จำนวนคนที่ใช้งานและจำนวนผู้พัฒนานั้นเป็นตัวเลือกที่สำคัญในการพัฒนา

แต่ปัญหานี้ยังถูกแก้ไม่หมดเสียทีเดียวเพราะ Developer เวลาพัฒนา Software นั้นก็ไม่ทราบว่า Lib ตัวไหนนั้นมีช่องโหว่อะไร และส่วนใหญ่ก็อาจจะค้นใน Internet ว่าเขาเขียนยังไง ใช้ Lib อะไรแล้วก็ copy ตัว Lib จากตัวอย่างใน Inernet มาใช้เลย ซึ่ง Version lib ในตัวอย่างนั้นอาจเก่าแล้วและมีช่องโหว่ ซึ่งจากปัญหาที่เกิดขึ้นแบบนี้เหล่า Developer ก็เลยสร้างเครื่องมือขึ้นมาเพื่อ Scan ตัว Lib ใน Version ที่ใช้ในการพัฒนาว่ามีช่องโหว่หรือไม่ ซึ่งนั้นก็คือ Dependency Check

Dependency check with MAVEN

ท่านสามารถดูตัวอย่าง Source code ได้ที่ https://github.com/surapong-taotiamton/test-dependency-check

ตัว Dependecncy check นั้นสามารถนำไปได้ใช้ได้หลายรูปแบบทั้งแบบ command line หรือเป็น Jenkins Plugin แต่ตัวอย่างนี้จะแสดงวิธีการใช้ด้วย MAVEN (พอดีผมเขียน Project java โดยใช้ MAVEN เป็นตัว Build และ จัดการ Dependency )

โดยวิธีการใช้นั้นง่ายมากเพียงท่าน copy ส่วนที่เป็น profile ไปวางในไฟล์ pom.xml ของ project

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<project>
<dependencies>
...
</dependencies>

<!-- >
เพิ่มตรงส่วน profile เข้าไป
-->
<profiles>
<profile>
<id>scan-vulnerability</id>
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>6.5.0</version>
<configuration>
<failBuildOnCVSS>1</failBuildOnCVSS>
</configuration>

<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>

จากนั้นสั่ง run ด้วย command

1
mvn -P scan-vulnerability dependency-check:check

ซึ่งในการสั่งครั้งแรกอาจจะกินเวลานานหน่อยเพราะตัว Dependency check จะทำการ Download ตัวฐานข้อมูลช่องโหว่ที่มีอยู่ใน NATIONAL VULNERABILITY DATABASE มาไว้ที่เครื่องเพื่อใช้ในการ Scan ซึ่งผลลัพธ์ที่ได้ประมาณนี้

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[ERROR] Failed to execute goal org.owasp:dependency-check-maven:6.5.0:check (default-cli) on project testdependencycheck:
[ERROR]
[ERROR] One or more dependencies were identified with vulnerabilities that have a CVSS score greater than or equal to '1.0':
[ERROR]
[ERROR] log4j-core-2.14.1.jar: CVE-2021-44228, CVE-2021-45105, CVE-2021-45046
[ERROR]
[ERROR] See the dependency-check report for more details.
[ERROR]
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

ซึ่งการดูอย่างนี้อาจไม่ละเอียดตัว Dependency check จะมีการออก Report ให้ดูแบบละเอียดได้ โดยเข้าไปที่ folder : target ที่อยู่ใน folder project

Path to Dependency check report

โดยตัวอย่าง Report จะหน้าตาแบบนี้ โดยในกรอบสีแดงจะเป็น Lib ที่มีช่องโหว่

Dependency check report

จะรู้ได้ไงว่ามาจากไหน

จากตัวอย่างจะเห็นว่ามีช่องโหว่จาก Lib : log4j 2.14.1 ซึ่งถ้าเราลองไปหาใน pom.xml จะไม่มีตรงไหนเรียกใช้ตัว log4j แบบตรงๆเลย ซึ่งจะทำให้เรางงว่าจะต้องแก้ตรงไหน ดังนั้นจึงต้องสั่งด้วยคำสั่ง

1
mvn dependency:tree

ซึ่งจะได้ผลลัพธ์ดังนี้

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
[INFO] --- maven-dependency-plugin:3.2.0:tree (default-cli) @ testdependencycheck ---
[INFO] blog.surapong.example:testdependencycheck:jar:0.0.1-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:2.6.1:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.6.1:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:2.6.1:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.6.1:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.6.1:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.2.7:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.2.7:compile
[INFO] | | | \- org.apache.logging.log4j:log4j-to-slf4j:jar:2.14.1:compile
[INFO] | | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.29:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-json:jar:2.6.1:compile
[INFO] | | +- com.fasterxml.jackson.core:jackson-databind:jar:2.13.0:compile
[INFO] | | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.13.0:compile
[INFO] | | | \- com.fasterxml.jackson.core:jackson-core:jar:2.13.0:compile
[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.13.0:compile
[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.13.0:compile
[INFO] | | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.13.0:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.6.1:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.55:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-el:jar:9.0.55:compile
[INFO] | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.55:compile
[INFO] | +- org.springframework:spring-web:jar:5.3.13:compile
[INFO] | | \- org.springframework:spring-beans:jar:5.3.13:compile
[INFO] | \- org.springframework:spring-webmvc:jar:5.3.13:compile
[INFO] | +- org.springframework:spring-aop:jar:5.3.13:compile
[INFO] | +- org.springframework:spring-context:jar:5.3.13:compile
[INFO] | \- org.springframework:spring-expression:jar:5.3.13:compile
[INFO] +- org.springframework.boot:spring-boot-starter-log4j2:jar:2.6.1:compile
[INFO] | +- org.apache.logging.log4j:log4j-slf4j-impl:jar:2.14.1:compile
[INFO] | | +- org.slf4j:slf4j-api:jar:1.7.32:compile
[INFO] | | \- org.apache.logging.log4j:log4j-api:jar:2.14.1:compile
[INFO] | +- org.apache.logging.log4j:log4j-core:jar:2.14.1:compile
[INFO] | +- org.apache.logging.log4j:log4j-jul:jar:2.14.1:compile
[INFO] | \- org.slf4j:jul-to-slf4j:jar:1.7.32:compile
[INFO] +- org.projectlombok:lombok:jar:1.18.22:compile (optional)
[INFO] \- org.springframework.boot:spring-boot-starter-test:jar:2.6.1:test
[INFO] +- org.springframework.boot:spring-boot-test:jar:2.6.1:test
[INFO] +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.6.1:test
[INFO] +- com.jayway.jsonpath:json-path:jar:2.6.0:test
[INFO] | \- net.minidev:json-smart:jar:2.4.7:test
[INFO] | \- net.minidev:accessors-smart:jar:2.4.7:test
[INFO] | \- org.ow2.asm:asm:jar:9.1:test
[INFO] +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:test
[INFO] | \- jakarta.activation:jakarta.activation-api:jar:1.2.2:test
[INFO] +- org.assertj:assertj-core:jar:3.21.0:test
[INFO] +- org.hamcrest:hamcrest:jar:2.2:test
[INFO] +- org.junit.jupiter:junit-jupiter:jar:5.8.1:test
[INFO] | +- org.junit.jupiter:junit-jupiter-api:jar:5.8.1:test
[INFO] | | +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO] | | +- org.junit.platform:junit-platform-commons:jar:1.8.1:test
[INFO] | | \- org.apiguardian:apiguardian-api:jar:1.1.2:test
[INFO] | +- org.junit.jupiter:junit-jupiter-params:jar:5.8.1:test
[INFO] | \- org.junit.jupiter:junit-jupiter-engine:jar:5.8.1:test
[INFO] | \- org.junit.platform:junit-platform-engine:jar:1.8.1:test
[INFO] +- org.mockito:mockito-core:jar:4.0.0:test
[INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.22:test
[INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.22:test
[INFO] | \- org.objenesis:objenesis:jar:3.2:test
[INFO] +- org.mockito:mockito-junit-jupiter:jar:4.0.0:test
[INFO] +- org.skyscreamer:jsonassert:jar:1.5.0:test
[INFO] | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] +- org.springframework:spring-core:jar:5.3.13:compile
[INFO] | \- org.springframework:spring-jcl:jar:5.3.13:compile
[INFO] +- org.springframework:spring-test:jar:5.3.13:test
[INFO] \- org.xmlunit:xmlunit-core:jar:2.8.3:test

ซึ่งจากผลลัพธ์เราจะเห็นว่า log4j นั้นมาจาก org.springframework.boot:spring-boot-starter-log4j2 ซึ่งพอเรารู้ว่ามาจาก spring-boot ที่ใช้ log4j ซึ่งวิธีแก้ของแต่ละ lib ที่ใช้ก็จะแตกต่างกันไป แต่โดยส่วนใหญ่จะคือการอัพ Version ตัว Lib เป็นหลัก โดยของ spring-boot-starter-log4j2 อันนี้จะสามารถแก้ได้โดยทำตาม Link นี้ https://spring.io/blog/2021/12/10/log4j2-vulnerability-and-spring-boot ซึ่งคือการเพิ่ม properties ในไฟล์ pom.xml

1
2
3
4
<properties>
<java.version>11</java.version>
<log4j2.version>2.17.0</log4j2.version>
</properties>

ซึ่งเมื่อเพิ่มแล้วจะเห็นว่าไม่มีช่องโหว่แล้ว แต่สำหรับ Lib อื่นนั้นอาจจะมีวิธีแก้ต่างกันไป อาจจะอัพเดทตัว lib นั้นเป็น version ที่ใหม่กว่าเลยเป็นต้น

สรุป

สำหรับบทความนี้ก็ได้สอนวิธีการใช้ Dependecny check เพื่อหา Lib ที่มีช่องโหว่นะครับว่าทำยังไง และได้สาธิตการหาว่า Lib ที่มีช่องโหว่นั้นมาจากการประกาศตัวไหน และวิธีแก้ไขเบื้องต้น