JWT Token Otomasyonu (Bölüm 2)
  1. Anasayfa
  2. DevOps

JWT Token Otomasyonu (Bölüm 2)

0

İlk bölümde tasarımını yaptığımız JWT Token otomasyonunun ikinci bölümünde çözüm sürecini hayata geçiriyoruz. İlk bölüme https://www.mshowto.org/jwt-token-otomasyonu-bolum-1.html Linkinden ulaşabilirsiniz.

  1. JWT Token yapısı; (*1)

    Öncelikle arayacağımız JWT Token’ın yapısını incelememiz gerekiyor. JWT Token aaaaa.bbbb.ccccc şeklinde nokta ile ayrılmış üç bölümden oluşmaktadır. Bu üç bölümden her bir bölüm Base64 formatında Encode edilmiştir.

    1. İlk bölüm Header olarak konumlandırılmıştır. Header Decode edilirse içerisinde JSON tipinde formatında iki adet parametre barındırdığı anlaşılır. Bunlardan bir tanesi olan “alg:” Token’ın hangi güvenlik algoritması ile şifrelendiğinin standardının yazdığı parametredir. Diğer parametre olan “typ:” içerisinde de Token tipi yazmaktadır ki bu da sabit olarak “JWT” olarak belirlenmiştir.
      {"typ":"JWT","alg":"HS256"}
    2. İkinci bölüm Payload olarak adlandırılmıştır. Payload Decode edildiğinde içerisinde JSON formatında; Token’ı üreten “iss:” (Issuer), Konusu “sub:” (Subject), Amaçlanan kullanıcı kitlesi “aud:” (Audience), Geçerlilik başlangıç tarihi “nbf: ” (Not Before), Son kullanma tarihi “exp:” (Expiration Time), Verildiği tarih “iat:” (Issued at), Serbest olarak tanımlanabilecek kimlik numarası “jti:”(JWT ID) ve isteğe bağlı tanımlanabilecek diğer parametreler bulunan bölümdür.
      {
      "iss": "https://auth.domain.com",
      "aud": "https://api.domain.com",
      "nbf": 1576680628,
      "exp": 1609372800,
      "sub": "8e01d6ae-5f59-4593-aaba-3cc75c6d00ce",
      "role": "backoffice:app"
      }
    3. Son bölüm Code olarak adlandırılmıştır. Bu bölümde Header “alg:” parametresindeki şifreleme formatı ile formatın tipine göre, Gizli Kelime (Secret Key) veya Sertifika (Certificate) ile şifrelenmiş içerik bulunmaktadır. Bu içerik ilgili şifre çözme algoritmasından geçirilerek Token’ın geçerli olup olmadığını anlamak için kullanılır.
  1. Powershell’de (*2) RegEx (*3) Kullanarak dosyaların içerisinde arama yapmak;

    Genel olarak yapısını öğrendiğimiz JWT Token’ları dosyalar içinde bulmak için RegEx kullanacağız. RegEx’i verdiğimiz koşula ve kurala uygun olan desenleri metin içerisinde bulmamızı sağlayan hızlı ve kullanışlı algoritma türü şeklinde basitçe tanımlayabiliriz. Regex desenleri ilk bakışta çok karışık ve kafa karıştırıcı görünmekle beraber programlamada birçok yerde hayat kolaylaştırıcı ve olmazsa olmazlardandır.

    Bizim aradığımız JWT Token’lar aşağıdakine benzer şekilde;

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI

 

  • “ey” harfleri ile başlayan,
  • Daha önce bahsettiğimiz gibi
    a.b.c
    şeklinde nokta ile ayrılmış Base64 kodlanmış üç bölümden oluşan
  • Her bölümünde içeriğinde Büyük Harf, Küçük Harf, Rakam, _, = olmak üzere en az 15 karakter barındıran ve uzunluğu en az baştaki “ey” ve iki adet nokta ile birlikte 49 karakter olan, ayrıca başlangıç ve bitişinde tek tırnak veya çift tırnak arasındaki karakterlerden oluşmuş desendir. Anlatması bile karışık ve beni yoran bu deseni dosyaların içerisinde aramak için PowerShell kullanacağız. Bunun için kullanacağımız Script aşağıdadır.
###########################################################################  

## Find-Token  

## Author: Bora Tercan  

## Date : 21.01.2021  

## Version : 1.0   

## Email: btercan@hotmail.com  

###########################################################################  



# Main Arrays Settings  

$BasePath=$PSScriptRoot  

$TokenFilePaths=Join-Path -Path $BasePath -ChildPath "FindTokenConfig.txt"  

$TokenFiles=Join-Path -Path $BasePath -ChildPath "TokenConfig.txt"  

$TokenFilesExtra=Join-Path -Path $BasePath -ChildPath "FixTokenConfig.txt"  

$TokenConfigs = Import-Csv -Path $TokenFilePaths  

$NewTokenConfigs=@()  

# Done  



Import-module (Join-Path -Path $BasePath -ChildPath "IsJwtToken.ps1") -Force  



foreach($TokenConfig in $TokenConfigs){  

    switch ($TokenConfig.Type) {  

        "findtoken" {  

            if (Test-Path $TokenConfig.Path) {  

                $ScriptPaths=Get-ChildItem -Path $TokenConfig.Path  -Recurse -Include *.ps1, *.txt, *.config, *.json #, *.js      

                $Node=$TokenConfig.Node  

                foreach($ScriptPath in $ScriptPaths.FullName) {    

                    $TokenLine=Select-String -path $ScriptPath -Pattern '[ey]{2,2}[A-Za-z0-9-_=]{15,}[.][A-Za-z0-9-_=]{15,}[.][A-Za-z0-9-_.+/=]{15,}[''"]'  

                    if ($ScriptPath -like "*\tokenPolicies.config" ) {$TokenLine="Exeption [*\tokenPolicies.config]"}  

                    if ($ScriptPath -like "*\tokenPolicies.csv" )    {$TokenLine="Exeption [*\tokenPolicies.csv]"}  

                    foreach($ScriptContent in $TokenLine) {                   

                        if ($ScriptContent -notmatch 'Exeption(.*?)') {  

                            $OldToken = $ScriptContent.Matches.Value.Replace('"','').Replace("'","")  

                            $ParsedToken=IsJwtToken -Token $OldToken -iss "api.paximum.com" -Verbose  

                            if ($ParsedToken) {  

                                Write-Host "Findtoken: $ScriptPath ->" $OldToken.Substring(0,4) "..." $OldToken.Substring(250) -ForegroundColor Magenta  

                                Write-Host "-End----------------------------------------------------------------------------" -ForegroundColor Green  

                                switch ((dir $ScriptPath).Extension) {  

                                    ".config" {   

                                        $Type = "XML"   

                                        $ScriptContent | ForEach-Object { if ( ($_ -match 'key="(.*?)"') -or ($_ -match "key='(.*?)'") ) { $Node=$Matches[1] } }  

                                    }  

                                    ".json"   {   

                                        $Type = "json"   

                                        $ScriptContent | ForEach-Object { if ( ($_ -match '"(.*?)"') -or ($_ -match "'(.*?)'") ) { $Node=$Matches[1] } }    

                                    }  

                                    ".txt"    { $Type = "PowerShell"; $Node="" }  

                                    ".ps1"    { $Type = "PowerShell"; $Node="" }  

                                    default   { $Type = "Unknown"; $Node="Unknown"}  

                                }  



                                $AddTokenConfig = new-object psobject -property @{  

                                    Name       ="Autogenerate"  

                                    Path       ="$ScriptPath"  

                                    Type       ="$Type"    

                                    Node       ="$Node"  

                                    Environment="live"  

                                    Role       =$ParsedToken.role  

                                    ApiID      =$ParsedToken.sub  

                                    ExpireDate =([datetime]([timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($ParsedToken.exp)))).ToString("yyyy-MM-dd")  

                                    LastToken  =""  

                                }  

                                $NewTokenConfigs += $AddTokenConfig  

                            } else {   

                                Write-Host " #-> This is not JWT Token [$ScriptPath] [$OldToken] !!!" -ForegroundColor Red  

                                #pause  

                            }  

                        } else {   

                            Write-Host " #-> There is no token in $ScriptPath or Exeption. Nothing to do !!!" -ForegroundColor Red  

                        }  

                    }  

                }  

            }  

        }  

    }  

    Write-Host " * -> Type:"$TokenConfig.Type" - Path:"$TokenConfig.Path" - Node:"$Node $TokenConfig.ApiID $TokenConfig.Environment $TokenConfig.Role $TokenConfig.ExpireDate -ForegroundColor Cyan  

}  



if (Test-Path ( $TokenFiles + "_old" ) ) {Remove-Item -LiteralPath (  $TokenFiles + "_old" ) }  

if (Test-Path $TokenFiles) {Rename-Item -LiteralPath $TokenFiles -NewName ((Split-Path $TokenFiles -leaf) + "_old") }  



$TokenConfigs = Import-Csv -Path $TokenFilesExtra  

foreach($TokenConfig in $TokenConfigs){  

    $NewTokenConfigs += $TokenConfig  

    Write-Host " * -> [Add Extra Config] Type:"$TokenConfig.Type" - Path:"$TokenConfig.Path" - Node:"$Node $TokenConfig.ApiID $TokenConfig.Environment $TokenConfig.Role $TokenConfig.ExpireDate -ForegroundColor Cyan  

}  



$NewTokenConfigs | Select-Object Name,Path,Type,Node,Environment,Role,ApiID,ExpireDate,LastToken | Export-Csv -Path $TokenFiles -NoTypeInformation
  • Powershell Script’in 27. Satırındaki Select-String fonksiyonunun –Patern parametresinde ‘[ey]{2,2}[A-Za-z0-9-_=]{15,}[.][A-Za-z0-9-_=]{15,}[.][A-Za-z0-9-_.+/=]{15,}[””]’
    RegEx formülünü kullanarak dosyaların içerisinde Token’ları bulacağız.
  • Bulduğumuz Token adaylarının gerçekten JWT Token olup olmadığını da ayrı bir Powershell Function ile sınayacağız. İlgili Function Find-Token.ps1 isimli scriptin 18. Satırında import edilip 33. Satırda da kullanılmaktadır. IsJwtToken isimli Function da aşağıdadır.
###########################################################################  

## IsJwtToken  

## Author: Bora Tercan  

## Date : 21.01.2021  

## Version : 1.0   

## Email: btercan@hotmail.com  

# https://www.michev.info/Blog/Post/2140/decode-jwt-access-and-id-tokens-via-powershell  

###########################################################################  





function IsJwtToken {  

    Param(          

        [Parameter(Mandatory=$True, ParameterSetName="Token")]  

        [string]$Token,  

        [string]$iss="auth.domain.com"  

        )  

    Process {  

    $Istokenheader = $false  

    $IstokenPayload = $false  

    if ($token.Split(".").Count -ne 3) {return $false}  

    if ($token.Length -lt 250) {return $false}  

    #Header  

    $tokenheader = $token.Split(".")[0].Replace('-', '+').Replace('_', '/')  

    while ($tokenheader.Length % 4) { Write-Verbose "Invalid length for a Base-64 char array or string, adding ="; $tokenheader += "=" }  

    $Istokenheader=$tokenheader -match '^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$'  

    Write-Verbose $Istokenheader  

    if ($Istokenheader) {  

        $tokenheaderArray = [System.Text.Encoding]::ASCII.GetString([system.convert]::FromBase64String($tokenheader))  

        Write-Verbose $tokenheaderArray  

        if ($tokenheaderArray -like "*JWT*") {  

            #Payload  

            $tokenPayload = $token.Split(".")[1].Replace('-', '+').Replace('_', '/')  

            while ($tokenPayload.Length % 4) { Write-Verbose "Invalid length for a Base-64 char array or string, adding ="; $tokenPayload += "=" }  

            $IstokenPayload=$tokenPayload -match '^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$'  

            Write-Verbose $IstokenPayload  

            $tokenByteArray = [System.Convert]::FromBase64String($tokenPayload)  

            $tokenArray = [System.Text.Encoding]::ASCII.GetString($tokenByteArray)  

            Write-Verbose $tokenArray  

            if ($tokenArray -notlike "*$iss*") {  

                $IstokenPayload = $false  

                Write-Verbose "There is not $iss JWT Token !!!"  

            } else {  

                $tokobj = $tokenArray | ConvertFrom-Json  

                Write-Verbose $tokobj  

            }  



        }  

    }  

    if ($Istokenheader -and $IstokenPayload) {return $tokobj} else {return $false}  

    }  

}
  • 1. maddede bahsettiğim JWT Token yapısına uygun olarak nokta olan yerlerden fonksiyonun 20. satırında parçalayıp, parçaların sayısının üç adet olduğunu doğrulayacağım.
  • Sınamayı geçen ilk parça olan Header bölümünün Base64 (*4) deseninde olduğunu ‘^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$’ 25. Satırda RegEx ile doğrulayacağım.
  • Header bölümü kontrolü başarılı şekilde geçerse 28. satırda Base64 olarak Decode ettikten sonra 30. Satırda Header kısmında “JWT” olup olmadığına bakacağım.
  • Başarılı olursa 34. Satırda ikinci parça olan Paylod bölümünün de Base64 deseninde olduğunu ‘^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$’ RegEx ile doğrulayıp, başarılı ise 36. Satırda Base64 olarak Decode edeceğim.
  • Decode ettiğim Payload kısmında “iss:” (Issuer) bizim aradığımız üretici (varsayılan olarak “auth.domain.com”) olup olmadığına bakacağım.
  • Son olarak tüm kontrolleri geçen Token’ı Json’dan Powershell Object’e 43. Satırda çevirerek sonucu geri göndereceğim.
  • Bu iki scripti kullanarak FindTokenConfig.txt dosyasında tanımlanış klasörlerin içindeki dosyalarda Token aradık ve bulduğumuz Tokenları TokenConfig.txt dosyasına yazdık ve FixTokenConfig.txt dosyasında olan ekstra Token bilgilerini de bulduğumuz dosyaya ekledik.

Bu bölümü de uzamaması için burada kesiyorum. Makalenin sonraki bölümünde de bulduğumuz ve TokenConfig.txt içerisine kaydettiğimiz Token’lardan değişmesi gerekenleri tespit ederek değiştirecek otomasyonu inceleyeceğiz. Ayrıca bir sonraki bölümde tüm dosyaları içeren Github Repository (*5) linkini de paylaşacağım. İlginizi çektiyse üçüncü bölümde görüşmek üzere.

Referanslar

  1. JWT (JSON Web Token): https://tr.wikipedia.org/wiki/JSON_Web_Token
  2. PowerShell: https://www.mshowto.org/powershell-nedir.html, https://www.mshowto.org/windows-powershell-nedir-ne-amacla-kullanilir-komutlar-nelerdir.html, https://docs.microsoft.com/tr-tr/powershell/
  3. RegEx (Regular expression): https://en.wikipedia.org/wiki/Regular_expression
  4. Base64: https://medium.com/@gokhansengun/base64-encoding-nedir-ve-nerelerde-kullan%C4%B1l%C4%B1r-d82f5307ea6d
  5. Github: https://www.mshowto.org/github-nedir-uyelik-nasil-olusturulur.html
  6. www.mshowto.org

TAGs: Otomasyon, JWT Token, DevOps, API, Powershell, AWS Powershell, XML, JSON, CSV, TEXT.

Bu İçeriğe Tepkin Ne Oldu?
  • 2
    harika_
    Harika!!
  • 0
    be_enmedim
    Beğenmedim
  • 0
    _ok_iyi
    Çok iyi
  • 0
    sevdim_
    Sevdim!
  • 0
    bilemedim_
    Bilemedim!
  • 0
    olmad_
    Olmadı!
  • 0
    k_zd_m_
    Kızdım!

Bora Tercan 1974 yılında Ankara’da doğdu. 20 seneden fazla bilişim sektöründe profesyonel iş deneyimi içerisinde; Marka bağımsız Sunucu, Yazılım, Firewall, Router, Veritabanı, Kablosuz Ağ, İnternet, Lan ve Wan vs. her türlü bilişim alt yapısının kurgulanması, projelendirilmesi, sorunsuz çalıştırılmasının sağlanması, merkezi olarak takibi, yönetilmesi ve sorunların tespit edilerek düzeltilmesi/düzelttirilmesi. Bilişim satın alma süreçlerinin takibi ve organizasyonun yapılması, Bilişim personelinin yönetilmesi, bilişim firmalarıyla işbirliği kurarak en iyi çözüme ilk elden ulaşılmasını sağlaması, iş analizi ve proje yönetimi gibi konularda 15 seneden fazlası Turizmde olmak üzere bilişim profesyoneli olarak sektör tecrübesi bulunmaktadır. Kariyerini bulut bilişim konusunda önemli projeleri olan Turizm Yazılımları geliştiren bir firmada Bilgi İşlem Yöneticisi olarak devam ettiriyor. Sahip olduğu sertifikalar; MCP MCSE MCSA MCDBA’dir.

Yazarın Profili
İlginizi Çekebilir

Bültenimize Katılın

Tıklayın, üyemiz olun ve yeni güncellemelerden haberdar olan ilk kişi siz olun.

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir