เรียกใช้ CodeQL ในฐานข้อมูล

เสร็จสมบูรณ์เมื่อ

ด้วยรหัสของคุณที่ถูกแยกไปยังฐานข้อมูล ตอนนี้คุณสามารถวิเคราะห์ได้โดยใช้คิวรี CodeQL ผู้เชี่ยวชาญ GitHub นักวิจัยด้านการรักษาความปลอดภัย และผู้สนับสนุนชุมชนเขียนและรักษาคิวรี CodeQL เริ่มต้น คุณยังสามารถเขียนคิวรีของคุณเองได้อีกด้วย

คุณสามารถใช้คิวรี CodeQL ในการวิเคราะห์การสแกนโค้ดเพื่อค้นหาปัญหาในโค้ดต้นฉบับของคุณและค้นหาช่องโหว่ด้านความปลอดภัยที่อาจเกิดขึ้น คุณยังสามารถเขียนคิวรีแบบกําหนดเองเพื่อระบุปัญหาสําหรับแต่ละภาษาที่คุณกําลังใช้ในโค้ดต้นฉบับของคุณ

มีคิวรีที่สําคัญสองชนิด:

  • คิวรีการแจ้งเตือน เน้นปัญหาในตําแหน่งเฉพาะของโค้ดของคุณ
  • คิวรีเส้นทาง อธิบายโฟลว์ของข้อมูลระหว่างแหล่งข้อมูลและที่เก็บในโค้ดของคุณ

คิวรี CodeQL อย่างง่าย

โครงสร้างคิวรี CodeQL พื้นฐานมี .ql ส่วนขยายไฟล์และมีส่วนคําสั่ง select นี่คือตัวอย่างโครงสร้างคิวรี:

/**
 *
 * Query metadata
 *
 */
import /* ... CodeQL libraries or modules ... */
/* ... Optional, define CodeQL classes and predicates ... */
from /* ... variable declarations ... /
where / ... logical formula ... /
select / ... expressions ... */

คิวรีเมตาดาต้า

การใช้ CodeQL กับการสแกนโค้ดจะแปลงผลลัพธ์ในลักษณะที่เน้นปัญหาที่อาจเกิดขึ้นซึ่งคิวรีถูกออกแบบมาเพื่อค้นหา คิวรีมีคุณสมบัติเมตาดาต้าที่ระบุว่าควรตีความผลลัพธ์อย่างไร ใช้เมตาดาต้าคิวรีเพื่อ:

  • ระบุคิวรีแบบกําหนดเองของคุณเมื่อคุณเพิ่มลงในที่เก็บ GitHub ของคุณ
  • แสดงข้อมูลเกี่ยวกับวัตถุประสงค์ของคิวรี

ข้อมูลเมตาดาต้าสามารถรวมถึงคําอธิบายของคิวรี รหัสเฉพาะ และชนิดของปัญหาคือ (การแจ้งเตือนหรือเส้นทาง) เมตาดาต้ายังระบุวิธีการแปลและแสดงผลลัพธ์คิวรี

GitHub มีแนวทางสไตล์ที่แนะนําสําหรับเมตาดาต้าคิวรี คุณสามารถค้นหาได้ในเอกสารประกอบ CodeQL

ตัวอย่างนี้แสดงเมตาดาต้าสําหรับคิวรี Java มาตรฐานหนึ่งคิวรี:

สกรีนช็อตที่แสดงเมตาดาต้าของคิวรี

CodeQL ไม่ได้แปลคิวรีที่ไม่มีเมตาดาต้า ซึ่งแสดงผลลัพธ์เหล่านั้นเป็นตาราง และไม่แสดงในโค้ดต้นฉบับ

ไวยากรณ์ QL

QL คือภาษาคิวรีเชิงวัตถุเชิงประกาศ ซึ่งได้รับการปรับให้เหมาะสมเพื่อเปิดใช้งานการวิเคราะห์โครงสร้างข้อมูลแบบลําดับชั้นที่มีประสิทธิภาพ และโดยเฉพาะอย่างยิ่งฐานข้อมูลที่แสดงวัตถุของซอฟต์แวร์

ไวยากรณ์ของ QL นั้นคล้ายกับ SQL แต่ความหมายของ QL นั้นขึ้นอยู่กับ Datalog Datalog เป็นภาษาการเขียนโปรแกรมเชิงประกาศซึ่งมักใช้เป็นภาษาคิวรี เนื่องจาก QL เป็นภาษาเชิงตรรกะเป็นหลัก การดําเนินการทั้งหมดใน QL จึงเป็นการดําเนินการเชิงตรรกะ QL ยังสืบทอดเพรดิเคตแบบเรียกใช้ซ้ําจาก Datalog อีกด้วย QL เพิ่มการสนับสนุนสําหรับการรวมเพื่อให้คิวรีที่ซับซ้อนกระชับและง่ายดาย

ภาษา QL ประกอบด้วยสูตรเชิงตรรกะ ซึ่งใช้การเชื่อมต่อเชิงตรรกะทั่วไป เช่น and, or, และ notพร้อมกับตัววัดปริมาณ เช่น forall และ exists เนื่องจาก QL สืบทอดเพรดิเคตแบบเรียกใช้ซ้ํา คุณสามารถเขียนคิวรีแบบเรียกใช้ซ้ําที่ซับซ้อนโดยใช้ไวยากรณ์ QL พื้นฐานและผลรวมเช่น countsum, และ averageได้

สําหรับข้อมูลเพิ่มเติมเกี่ยวกับภาษา QL โปรดดูเอกสารประกอบ CodeQL

คิวรีเส้นทาง

แนวทางที่ข้อมูลไหลผ่านโปรแกรมเป็นสิ่งสําคัญ ข้อมูลที่ดูเหมือนว่าไม่เป็นพิษเป็นภัยสามารถโฟลว์ในรูปแบบที่ไม่คาดคิดที่อนุญาตให้ใช้เป็นอันตรายได้

การสร้างคิวรีเส้นทางสามารถช่วยให้คุณแสดงภาพโฟลว์ของข้อมูลผ่าน codebase ได้ คิวรีสามารถติดตามเส้นทางที่ข้อมูลใช้จากจุดเริ่มต้นที่เป็นไปได้ (source) ไปยังจุดสิ้นสุดที่เป็นไปได้ (sink) เมื่อต้องการสร้างเส้นทางของแบบจําลอง คิวรีของคุณต้องให้ข้อมูลเกี่ยวกับแหล่งข้อมูล อ่างล้างหน้า และขั้นตอนกระแสข้อมูลที่เชื่อมโยงเหล่านั้น

วิธีที่ง่ายที่สุดในการเริ่มเขียนคิวรีเส้นทางของคุณเองคือการใช้หนึ่งในคิวรีที่มีอยู่เป็นเทมเพลต หากต้องการรับคิวรีสําหรับภาษาที่รองรับ โปรดดูเอกสารประกอบ CodeQL

คิวรีเส้นทางของคุณจําเป็นต้องใช้เมตาดาต้า เพรดิเคตคิวรี และโครงสร้างคําสั่ง select คิวรีเส้นทางที่มีอยู่ภายในจํานวนมากใน CodeQL เป็นไปตามโครงสร้างพื้นฐาน โครงสร้างขึ้นอยู่กับวิธีการที่ CodeQL แบบจําลองภาษาที่คุณกําลังวิเคราะห์

นี่คือตัวอย่างเทมเพลตสําหรับคิวรีเส้นทาง:

/**
 * ...
 * @kind path-problem
 * ...
 */

import <language>
// For some languages (Java/C++/Python/Swift), you need to explicitly import the data-flow library, such as
// import semmle.code.java.dataflow.DataFlow or import codeql.swift.dataflow.DataFlow
...

module Flow = DataFlow::Global<MyConfiguration>;
import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "<message>"

ในเทมเพลตนั้น:

  • MyConfiguration คือโมดูลที่มีเพรดิเคตที่กําหนดวิธีการไหลของข้อมูลระหว่าง source และ sink
  • Flow คือผลลัพธ์ของการคํานวณกระแสข้อมูลตาม MyConfiguration
  • Flow::Pathgraph คือมอดูลกราฟกระแสข้อมูลผลลัพธ์ที่คุณจําเป็นต้องนําเข้าเพื่อรวมคําอธิบายเส้นทางในคิวรี
  • source และ sink คือโหนดในกราฟตามที่กําหนดไว้ในการกําหนดค่าและ Flow::PathNode เป็นประเภทของพวกเขา
  • DataFlow::Global<..> คือการเรียกใช้โฟลว์ข้อมูล คุณสามารถใช้ TaintTracking::Global<..> เพื่อรวมชุดค่าเริ่มต้นของขั้นตอน taint ได้แทน

วิธีการเขียนคิวรีเส้นทาง

คิวรีของคุณต้องคํานวณกราฟเส้นทางเพื่อสร้างคําอธิบายเส้นทาง เมื่อต้องการทําเช่นนั้น ให้กําหนดเพรดิเคตคิวรีที่เรียกว่า edges เพรดิเคตคิวรี่เป็นเพรดิเคตที่ไม่ใช่ตัวเลขที่มีคําอธิบายประกอบ query คําอธิบายประกอบคิวรี่จะส่งกลับทูเพิลทั้งหมดที่เพรดิเคตประเมิน

เพรดิเคต edges จะกําหนดความสัมพันธ์ของขอบของกราฟที่คุณกําลังคํานวณ ซึ่งใช้ในการคํานวณเส้นทางที่เกี่ยวข้องกับผลลัพธ์แต่ละรายการที่คิวรีของคุณสร้างขึ้น คุณยังสามารถนําเข้าเพรดิเคต edges ที่กําหนดไว้ล่วงหน้าจากโมดูลกราฟเส้นทางในหนึ่งไลบรารีโฟลว์ข้อมูลมาตรฐาน

ไลบรารีโฟลว์ข้อมูลประกอบด้วยคลาส เพรดิเคต และโมดูลอื่น ๆ ที่มักใช้ในการวิเคราะห์กระแสข้อมูลนอกเหนือจากโมดูลกราฟเส้นทาง ฟังก์ชันไลบรารีโฟลว์ข้อมูล CodeQL โดยการสร้างแบบจําลองกราฟกระแสข้อมูลหรือการใช้การวิเคราะห์กระแสข้อมูล ไลบรารีโฟลว์ข้อมูลปกติจะใช้ในการวิเคราะห์โฟลว์ข้อมูลที่มีการรักษาค่าข้อมูลในแต่ละขั้นตอน

นี่คือตัวอย่างคําสั่งที่นําเข้าโมดูล pathgraph จากไลบรารีกระแสข้อมูล (DataFlow.qll) ซึ่ง edges ถูกกําหนดไว้:

import DataFlow::PathGraph

คุณสามารถนําเข้าไลบรารีอื่นๆ อีกมากมายที่มาพร้อมกับ CodeQL คุณยังสามารถนําเข้าไลบรารีที่ได้รับการออกแบบโดยเฉพาะเพื่อใช้การวิเคราะห์กระแสข้อมูลในเฟรมเวิร์กและสภาพแวดล้อมทั่วไปต่าง ๆ ได้

คลาส PathNode ถูกออกแบบมาเพื่อใช้การวิเคราะห์กระแสข้อมูล เสริม Node ด้วยบริบทการเรียก (ยกเว้นสําหรับจม) เส้นทางการเข้าถึง และการกําหนดค่า PathNode เฉพาะค่าที่สามารถเข้าถึงได้จากแหล่งข้อมูลเท่านั้นที่จะถูกสร้างขึ้น

นี่คือตัวอย่างของเส้นทางการนําเข้า:

import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl

คุณสามารถเลือกที่จะกําหนดเพรดิเคตคิวรี nodes ซึ่งระบุโหนดของกราฟเส้นทางสําหรับภาษาทั้งหมด เมื่อคุณกําหนด nodesโหนดที่เลือกจะกําหนดเฉพาะขอบที่มีจุดสิ้นสุดเท่านั้น เมื่อคุณไม่ได้กําหนด nodesคุณจําเป็นต้องเลือกจุดสิ้นสุดที่เป็นไปได้ทั้งหมดของ edges

การวิเคราะห์ฐานข้อมูล

เมื่อคุณใช้คิวรีเพื่อวิเคราะห์ฐานข้อมูล CodeQL คุณจะได้รับผลลัพธ์ที่มีความหมายในบริบทของซอร์สโค้ด ผลลัพธ์จะถูกจัดรูปแบบเป็นการแจ้งเตือนหรือเส้นทางใน SARIF หรือรูปแบบที่ถูกแปลอื่น

นี่คือตัวอย่างของคําสั่งฐานข้อมูล CodeQL ที่วิเคราะห์ฐานข้อมูลโดยการเรียกใช้คิวรีที่เลือกและตีความผลลัพธ์:

codeql database analyze --format=<format> ---output=<output> [--threads=<num>] [--ram=<MB>] <options>... -- <database> <query|dir|suite>...

คําสั่งนี้รวมผลของ codeql database run-queries และ codeql database interpret-results คําสั่งประปา

อีกวิธีหนึ่งคือ คุณสามารถเรียกใช้คิวรีที่ไม่ตรงตามข้อกําหนดสําหรับการแปลเป็นการแจ้งเตือนซอร์สโค้ด เมื่อต้องการทําเช่นนั้น ให้ใช้ codeql-database run-queries หรือ codeql query run จากนั้นใช้ codeql bqrs decode เพื่อแปลงผลลัพธ์ดิบเป็นสกุลที่สามารถอ่านได้

คุณสามารถรับรายการทั้งหมดของคําสั่ง CodeQL CLI ที่พร้อมใช้งานใน คู่มือ CodeQL CLI

ใช้ไฟล์ SARIF กับประเภท

CodeQL สนับสนุน SARIF สําหรับการแชร์ผลลัพธ์การวิเคราะห์แบบคงที่ SARIF ถูกออกแบบมาเพื่อแสดงผลลัพธ์ของเครื่องมือการวิเคราะห์แบบคงที่ที่หลากหลาย

คุณจําเป็นต้องระบุประเภทเมื่อใช้ผลลัพธ์ SARIF สําหรับการวิเคราะห์ CodeQL หมวดหมู่สามารถแยกความแตกต่างระหว่างการวิเคราะห์หลายรายการที่ดําเนินการในที่เก็บที่ยอมรับเดียวกันและในภาษาอื่นหรือส่วนต่าง ๆ ของโค้ด อย่างไรก็ตามไฟล์ SARIF ที่มีประเภทเดียวกันจะเขียนทับกัน

คุณสามารถสแกนแต่ละไฟล์ผลลัพธ์ SARIF โดยใช้ CodeQL เพื่อวิเคราะห์ภาษาต่างๆ ภายในฐานโค้ดเดียวกันเมื่อค่าประเภทสอดคล้องกันระหว่างการเรียกใช้การวิเคราะห์ เราขอแนะนําให้คุณใช้ภาษาที่สแกนเป็นตัวระบุสําหรับหมวดหมู่

นี่คือหนึ่งตัวอย่าง ค่าประเภทจะปรากฏขึ้น (ด้วยเครื่องหมายทับต่อท้ายถ้าไม่มีอยู่) เป็นคุณสมบัติ <run>.automationId ใน SARIF v1 คุณสมบัติ <run>.automationLogicalId ใน SARIF v2 และคุณสมบัติ <run>.automationDetails.id ใน SARIF v2.1.0

โพสต์ผลลัพธ์ SARIF ไปยัง GitHub

หลังจากที่ฐานข้อมูลพร้อมแล้ว คุณสามารถคิวรีแบบโต้ตอบได้ หรือคุณสามารถเรียกใช้ชุดคิวรีเพื่อสร้างชุดผลลัพธ์ในรูปแบบ SARIF และอัปโหลดผลลัพธ์ไปยังที่เก็บเป้าหมาย GitHub.com:

codeql github upload-results --sarif=<file> [--github-auth-stdin] [--github-url=<url>] [--repository=<repository-name>] [--ref=<ref>] [--commit=<commit>] [--checkout-path=<path>] <options>...

หากต้องการอัปโหลดผลลัพธ์ไปยัง GitHub ตรวจสอบให้แน่ใจว่าแต่ละเซิร์ฟเวอร์การผสานรวม (CI) มีแอป GitHub หรือโทเค็นการเข้าถึงส่วนบุคคลสําหรับ CodeQL CLI ที่จะใช้ คุณต้องใช้โทเค็นการเข้าถึงหรือแอป GitHub ที่มีสิทธิ์ในการเขียน security_events

คุณอาจอนุญาตให้ CodeQL CLI ใช้โทเค็นเดียวกันได้ถ้าเซิร์ฟเวอร์ CI ใช้โทเค็นกับขอบเขตนี้เพื่อตรวจสอบพื้นที่เก็บข้อมูลจาก GitHub มิฉะนั้น สร้างโทเค็นใหม่ด้วยสิทธิ์การเขียน security_events และเพิ่มโทเค็นนี้ไปยังที่เก็บความลับของระบบ CI แนวทางปฏิบัติที่ดีที่สุดสําหรับการรักษาความปลอดภัยคือการตั้งค่าสถานะ --github-auth-stdin และส่งผ่านโทเค็นไปยังคําสั่งผ่านการป้อนข้อมูลมาตรฐาน

อัปโหลดผลลัพธ์ SARIF

สําหรับการสแกนโค้ดเพื่อแสดงผลลัพธ์จากเครื่องมือการวิเคราะห์แบบคงที่ที่ไม่ใช่ของ Microsoft ในที่เก็บ GitHub ของคุณ ผลลัพธ์ของคุณต้องถูกจัดเก็บในไฟล์ SARIF ที่สนับสนุนชุดย่อยเฉพาะของสคีมา SARIF 2.1.0 JSON คุณสามารถอัปโหลดผลลัพธ์ได้โดยใช้ API สแกนโค้ดหรือ CodeQL CLI

แต่ละครั้งที่คุณอัปโหลดผลลัพธ์ของการสแกนรหัสใหม่ CodeQL จะประมวลผลผลลัพธ์และเพิ่มการแจ้งเตือนไปยังที่เก็บ เพื่อป้องกันการแจ้งเตือนซ้ําสําหรับปัญหาเดียวกัน การสแกนโค้ดใช้คุณสมบัติ partialFingerprints SARIF เพื่อให้ตรงกับผลลัพธ์ในการใช้งานต่าง ๆ เพื่อให้ปรากฏเพียงครั้งเดียวในการเรียกใช้ล่าสุดสําหรับสาขาที่เลือก การตัดข้อมูลซ้ําออกจะทําให้สามารถจับคู่ข้อความแจ้งเตือนกับบรรทัดที่ถูกต้องของรหัสเมื่อแก้ไขไฟล์

ID กฎสําหรับผลลัพธ์จะต้องเหมือนกันในการวิเคราะห์ ข้อมูลลายนิ้วมือจะรวมอยู่ในไฟล์ SARIF ที่สร้างขึ้นผ่านเวิร์กโฟลว์การวิเคราะห์ CodeQL หรือตัวเรียกใช้ CodeQL โดยอัตโนมัติ

ข้อกําหนด SARIF ใช้ชื่อคุณสมบัติ JSON partialFingerprintsพจนานุกรมจากชนิดลายนิ้วมือที่มีชื่อถึงลายนิ้วมือ คุณสมบัตินี้ประกอบด้วยอย่างน้อยที่สุด ค่าสําหรับ primaryLocationLineHashซึ่งมีลายนิ้วมือที่ยึดตามบริบทของตําแหน่งที่ตั้งหลัก

GitHub พยายามรวบรวมเขตข้อมูล partialFingerprints จากไฟล์ต้นฉบับถ้าคุณอัปโหลดไฟล์ SARIF โดยใช้การดําเนินการ upload-sarif และข้อมูลนี้จะขาดหายไป นอกจากนี้ หากคุณอัปโหลดไฟล์ SARIF โดยไม่มีข้อมูลลายนิ้วมือโดยใช้จุดสิ้นสุด /code-scanning/sarifs API ผู้ใช้อาจเห็นการแจ้งเตือนซ้ําเมื่อมีการประมวลผลและแสดงการแจ้งเตือนการสแกนโค้ด

เพื่อหลีกเลี่ยงการดูการแจ้งเตือนซ้ําในขณะที่คุณกําลังทํางานกับเครื่องมือการวิเคราะห์แบบคงที่ ให้คํานวณข้อมูลลายนิ้วมือและเติมคุณสมบัติ partialFingerprints ก่อนที่คุณจะอัปโหลดไฟล์ SARIF จุดเริ่มต้นที่เป็นประโยชน์คือการใช้สคริปต์เดียวกันกับการดําเนินการ upload-sarif