De General Availability announcement van GitHub Advanced Security voor Azure DevOps is inmiddels al ruim twee jaar geleden, maar in de praktijk lijkt het of weinig ontwikkelaars deze features van Azure DevOps al hebben ontdekt. In de laatste blog van deze driedelige serie, leek het mij een mooi moment om hier verandering in te brengen en mede-ontwikkelaars een aantal praktische handvatten te bieden om de kwaliteit van software nog verder te verbeteren!
GitHub Advanced Security (GHAS) voor Azure DevOps (ADO) is een applicatie security testing service, welke diverse tools biedt om DevSecOps op een eenvoudige manier meer prioriteit te geven binnen de ontwikkeling van software. De tooling suite van GHAS bestaat uit 3 key features die ik in deze blog zal toelichten, te weten:
- Secret scanning
- Dependency scanning
- Code scanning
Alvast een kleine disclaimer voor de rest van deze blog; ik heb voor de features niet gekeken naar de benodigde rechten om ze te kunnen activeren en/of gebruiken. In het project dat ik in deze blog gebruik heb ik administrator rechten, waardoor alles standaard beschikbaar is. Mocht dat bij jou niet het geval zijn, controleer dan hier of je de benodigde rechten hebt voor de feature die je probeert.
De tools van GHAS zijn standaard uitgeschakeld voor alle repositories, maar individueel worden ingeschakeld en op project- en organisatieniveau kan worden ingesteld dat nieuwe repositories wel Secret Protection en/of Code Security geactiveerd krijgen, mits je de rechten hebt om dit te configuren natuurlijk.
Om de tooling te activeren, ga je naar Settings > Repositories. Om GHAS op nieuwe repositories in te schakelen, ga je naar het tabblad Settings, waar je Advanced Security direct bovenaan ziet staan.

Om GHAS voor een bestaande repository te activeren, ga je naar het tabblad Repositories en selecteer je de gewenste repository. Ook hier zie je direct de opties om GHAS te activeren.

Wanneer je GHAS voor een repository inschakelt, heb je een aantal extra opties waar ik verder in de blog op terugkom.
Wanneer je een van de GHAS-services voor een repository activeert, krijg je een vraag/waarschuwing of je akkoord gaat met betaling voor de dienst. Betalen gaat per actieve ‘committer’ voor de gehele organisatie. Hoe dit exact wordt gerekend, vind je hier. Om akkoord te gaan, moet op organisatieniveau Billing zijn geregeld.

Secret scanning
Met de Secret scanning feature, scant GHAS de code in je repositories op meer dan 200 verschillende soorten ingecheckte secrets, zoals keys en tokens van specifieke diensten. Daarnaast kan je ook je eigen ‘patronen’ van secrets opgeven, waarmee je nog specifieker je code kan scannen op gelekte secrets. Op deze pagina vind je een overzicht van alle type secrets waarop standaard wordt gescand.
Wanneer een (potentiële) hit wordt gedetecteerd, ontvang je hier als repository administrator een melding van. Dit is een indicator om bijvoorbeeld een key te roteren of een wachtwoord te wijzigen. Laten we snel kijken hoe we dit gebruiken.
In mijn geval heb ik al een repository die ik in mijn eerdere blogs heb gebruikt en activeer ik hiervoor feature Secret Protection plan. Hiermee scan je direct een keer de code (inclusief alle historische commits). Ook activeert standaard de Push Protection feature, waarover later meer.
Secret alerts
Wanneer de initiële scan van de repository is afgerond, vind je eventueel gevonden secrets terug in je project onder Repos > Advanced Security, onder het tabblad Secrets. Als de scan nog niet is afgerond, zie je dat hier duidelijk.

In dit geval zijn er in de repository direct 3 secrets gedetecteerd. De tool geeft aan in welke file en op welke regel het secret gevonden is. Aan de hand van het gedetecteerde patroon geeft de tool zelfs aan welk type secret wordt vermoed dat er is gelekt.
In het overzicht klik je op de regel om meer details zichtbaar te krijgen, zoals het verwachtte Severity niveau en de geschatte overtuiging waarmee de tool zeker denkt te weten dat het daadwerkelijk om een secret gaat en geen false-positive is. Ook is er voor elk type gevonden secret een omschrijving beschikbaar waarmee je het gelekte secret kan herstellen.

Vanuit het scherm kan je ook direct een actie ondernemen, zoals aangeven dat het secret is ingetrokken (Revoked), dat het risico is geaccepteerd dat het secret in code beschikbaar is (bijv. als het gaat om een token dat voor integratietests wordt gebruikt in een Docker Compose file), of dat het gevonden patroon geen echt secret is, maar een False Positive. Let bij het controleren op een false-positive extra op in welke commit het secret werd gevonden. Het kan namelijk goed zijn iemand per ongeluk in een commit ooit een secret in had gecheckt, maar inmiddels alweer heeft verwijderd in een latere commit. Hierdoor kan het lijken of er nooit een secret is gelekt, terwijl dit dus wel is gebeurd.

Push protection
Zoals gezegd staat standaard ook de Push Protection aan bij het activeren van Secret Scanning. Met deze feature scant GitHub eerst alle commits die met een git push worden verzonden op potentieel gelekte secrets, voordat de commits worden toegevoegd aan de commit historie. Net als in de vorige sectie, kan het zijn dat GitHub ook hier false-positives signaleert. Om te laten zien hoe het werkt, ga ik proberen een ‘per ongeluk’ committed secret in te checken en hoe je de blokkade van false-positives omzeilt.
Als voorbeeld voeg ik een .env bestand toe aan een nieuwe branch waar ik aan werk. In deze file heb ik ‘per ongeluk’ een Azure DevOps PAT (Personal Access Token) opgenomen, die ik in mijn solution gebruik om de applicatie bij de ADO API te laten authenticeren.

Wanneer we deze commit naar ADO pushen, zien we nu onderstaande foutmelding.

Zoals je ziet, weigerde ADO het push commando en geeft direct een link naar een webpagina om je te helpen het probleem op te lossen.
In mijn geval accepteer ik het risico accepteer dat het secret in de repository komt te staan en wil ik toch het push commando forceren de commit te accepteren. Om dit te doen, dien je de tekst skip-secret-scanning:true in je commit message te hebben staan. Zoals de tekst al omschrijft, zorgt dit ervoor dat de content van deze specifieke commit niet op secrets zal worden gescand door de Push Protection feature.
Aangezien mijn secret in de laatste commit staat, hoef ik slechts de commit message aan te passen middels een git commit –amend command.

Wanneer ik nu opnieuw het push commando uitvoer, negeert ADO het secret in de commit en staat de code in de commit history op de server.

De secret scanning in ADO negeert echter niet het ingecheckte secret op de achtergrond en maakt er geen alert van, zoals we eerder ook al hadden gezien.

Om Push Protection in z’n geheel uit te zetten, vind je bij de repository settings extra opties, waar je met met een simpel vinkje de feature uitschakelt.

Code Scanning
De tweede feature die GitHub Advanced Security ons brengt, is een statische code scanning feature, die de code in je repository scant op vulnerabilities en code-technische fouten. De analyse van de code wordt door een CodeQL engine uitgevoerd. CodeQL is een taal en toolset, speciaal ontwikkeld om queries op code uit te voeren.
Aangezien elke taal anders werkt en andere features kent, worden momenteel alleen de volgende programmeertalen door CodeQL ondersteund: C/C++, C#, Go, Java/Kotlin, JavaScript/TypeScript, Python, Ruby en Swift.
Onderdeel van de CodeQL toolset is een open-source library met de queries die Code Scanning feature op je code uitvoert om de alerts te genereren. Het staat je vrij hier pull-requests te openen voor nieuwe queries of, als je een grotere uitdaging aan wilt gaan, een toolset voor het ondersteunen een nieuwe programmeertaal.
In tegenstelling tot Secret Scanning werkt de Code Scanning feature niet rechtstreeks op de ingecheckte code, maar vanuit je build-pipeline. Om de scanning uit te voeren, dien je een tweetal taken in je pipeline op te nemen. Een basis configuratie ziet er ongeveer zo uit:
1. trigger: none 2. 3. pool: 4. vmImage: windows-latest 5. 6. steps: 7. - task: AdvancedSecurity-Codeql-Init@1 8. displayName: Initialize CodeQL 9. inputs: 10. languages: 'csharp, java' 11. buildtype: 'none' 12. 13. // perform build steps here… 14. 15. - task: AdvancedSecurity-Codeql-Analyze@1 16. displayName: Perform CodeQL Analysis
De built type variabele is afhankelijk van welke programmeertaal/-talen je in de pipeline wil analyseren:
- none: de alerts worden gegenereerd zonder de code te builden in de tussenliggende stappen (bijv. voor alle geïnterpreteerde talen, zoals JavaScript, maar ook C++, Java en C# worden ondersteund).
- manual: je definieert zelf de build-steps in de tussenliggende taken
- autobuild: CodeQL detecteert de meest voor de hand liggende build-methode en probeert je project hiermee te builden voor de analyse stap
Meer informatie over de verschillen tussen de built types vind je op GitHub.
In dit voorbeeld wordt in de eerste stap de code scanning geactiveerd voor twee verschillende programmeertalen. Na deze stap voeg je de overige taken toe die je applicatie builden, waarna je in de laatste stap GitHub opdracht geeft om analyse op je code uit te voeren.
Met een uitgevoerde pipeline vind je eventueel gevonden issues terug onder Repos > Advanced Security, onder het tabblad Code Scanning.

In mijn geval zijn er 3 potentiële hard-coded credentials gedetecteerd en een command-line injection vulnerability.
Als we de eerste alert bekijken, zien we wederom in welke branch, in welke file en op welk regelnummer de scanner de detectie vond. De scanner zoek in de code o.a. naar keywords zoals ‘password’ en kijkt vervolgens of daar een constante waarde aan wordt gegeven, wat in dit geval zo is.

Nadat je de alert hebt bekeken, kan je 3 dingen doen:
- Het gevonden probleem oplossen in een nieuwe commit
- Het risico accepteren wat de gevonden fout met zich meebrengt
- De alert markeren als false-positive
In de laatste twee gevallen, gebruik je in het detailscherm van de alert de Close alert button.
Het grootste deel van de tijd, maak je een nieuwe commit waarin het probleem wordt opgelost. Wanneer het probleem niet langer naar voren komt in de CodeQL analyse, verdwijnt de alert automatisch. Het alert overzicht toont alleen de resultaten van de laatste pipeline run.

Een groot voordeel van het uitvoeren van de CodeQL analyse in een pipeline, is de feature die zorgt dat gevonden issues automatisch als review comments op een pull-request komen. In onderstaand voorbeeld heeft iemand bijvoorbeeld gebruik gemaakt van een cryptografisch algoritme dat niet langer als veilig wordt beschouwd.

Nog een extra voordeel van het uitvoeren van de scan in de pipeline, is dat je extra configuratie kan toepassen. Een goed voorbeeld hiervan is het gebruik van custom CodeQL queries. Met custom CodeQL queries neem je volledig controle over welke queries tijdens een analyse worden uitgevoerd. Je schakelt hiermee bijvoorbeeld de standaard set aan queries volledig uit, of voegt juist extra queries toe die voor jou project van belang zijn. De details hiervan behandel ik verder niet in deze blog, maar als je dit wil gebruiken, is de documentatie van Microsoft een goed startpunt.
Dependency scanning
Een van de lastigere taken in DevSecOps, is het managen van vulnerabilities in (open source) componenten van derde partijen. Aangezien er tegenwoordig zoveel packages worden gebruikt, is het onmogelijk geworden om alle componenten met enige regelmaat te controleren op nieuw gevonden of in nieuwe versie geïntroduceerde vulnerabilities.
De Code Scan feature van GHAS helpt hier enorm bij, door middel van de Dependency Scanning tool. Van elke gevonden vulnerability in je repositories, wordt een alert opgenomen. Deze alerts vind je terug onder Repos > Advanced Security onder het tabblad Dependencies.

Ook hier kan je voor elke alert een detailscherm openen, met meer informatie over de detectie(s), een omschrijving van de vulnerability en een aanbeveling hoe de vulnerability op te lossen (indien beschikbaar).

Ook de Dependency Scan werkt vanuit een taak in je build-pipeline. Om de scan uit te voeren, kan je de pipeline zoals weergegeven onder Code Scanning, uitbreiden met een extra taak vóór of na de CodeQL analyse:
1. - task: AdvancedSecurity-Dependency-Scanning@1
Hiermee wordt, net als bij Code Scanning, ook voor dependency waarschuwingen een review comment in pull-requests neergezet. Mocht je in een situatie zitten waarbij je urgent een build moet uitvoeren die wordt geblokkeerd door één of meerdere alerts, dan kan je een zogenaamde Break-glass procedure uitvoeren. Het enige dat je hoeft te doen, is een pipeline variabele mee te geven aan een nieuwe run van de pipeline, die ervoor zorgt dat de dependency scan niet wordt uitgevoerd: DependencyScanning.Skip: true.
Meer informatie
Deze blog is (deels) gebaseerd op de officiele documentatie van Microsoft over GitHub Advanced Security. Je kan hier meer informatie vinden over een aantal opties die ik niet in deze blog heb behandeld, over de benodigde rechten per feature en hoe de betaling in elkaar zit.