Spring boot-da Log-ların bəzi arqumentlərini necə şifrələyə bilərik?

Ali Sahib
PASHA Bank
Published in
4 min readFeb 10, 2020

Hamıya salam, bu məqalədə sizə məlumatları loglayarkən bəzi sensitive informasiyaları necə şifrələyə biləcəyimiz haqqında danışmaq istəyirəm.
Aydındır ki,istifadəçiyə məxsus bəzi datalar var ki,məsələn-(card number,fin və.s) bu məlumatları açıq halda log-da saxlamaq düzgün deyil.

Bəs bunu necə aradan qaldıra bilərik? Bunun üçün araşdırdığım 2 üsul haqqında məlumat verəcəm.

  1. Logstash istifadə etməklə
    LogstashEncoder-in 6.3 versiyasında MaskingJsonGeneratorDecorator əlavə olunub ki,bunun vasitəsi ilə sensitive data-nı şifrələmək olar
    Bu encoderi aşağıdakı şəkildə istifadə edə bilərik

1.1 pom.xml faylına aşağıdakı dependency-ni əlavə etməklə

1.2. resources-daxilində aşağıdakı xml faylını əlavə etməklə

Yuxarıdakı şəkildə

  1. <defaultMask> → dedikdə sensitive datanı default olaraq hansı şəkildə şifrələnməsi göstərilir
  2. <value> → teqi daxilində müxtəlif regex-lər (email,card-number və.s) istifadə olunur
  3. <values> → ayrılıqda <value> əvəzinə regexləri ümumi <values> teqi daxilində vermək olar
  4. <valuesMask> →bununla xüsusi regexləri fərqli mask-lara görər ayırmaq olar .

- Bu üsulların birindən istifadə etməklə sensitive data-ları şifrələmək olar -

2. 2-ci varianda custom olaraq filter yazmaqla verilən sensitive dataları şifrələyəcəyik.

Gəlin birlikdə Bu filteri yazaq.

Bunun üçün biz öz config classı-mızı yaradıb aşağıdakı 2 class-dan extends edə bilərik

1)PatternLayout
2)JsonLayout

Ümumiyyətlə əksər log monitorinq sistemləri datanı-json formatında qəbul edir.Buna görə də logları json formatında print edə bilək deyə burada JsonLayout-un implementasiyasını göstərəcəm

JsonLayoutu istifadə etmək üçün aşağıdakı 2 dependency-ni pom.xml-ə əlavə etmək lazımdır

Bu dependency-ləri əlavə edəndən sonra logback.xml aşağıdakı şəkildə verə bilərik

Buradakı layout class bizim öz təyin etdiyimiz custom classdır və aşağıdakı şəkildə konfiqurasiya etmişik.

import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.contrib.json.classic.JsonLayout
import java.util.LinkedHashMap
import java.util.regex.Pattern
import java.util.stream.Collectors
import java.util.stream.IntStream
import java.util.stream.Stream

class LogConfig : JsonLayout() {

override fun toJsonMap(event: ILoggingEvent): LinkedHashMap<String, Any> {

val map = LinkedHashMap<String, Any>()
addTimestamp(TIMESTAMP_ATTR_NAME, this.includeTimestamp, event.timeStamp, map)
add(LEVEL_ATTR_NAME, this.includeLevel, event.level.toString(), map)
add(THREAD_ATTR_NAME, this.includeThreadName, event.threadName, map)
add(FORMATTED_MESSAGE_ATTR_NAME, this.includeFormattedMessage, getFormattedMessage(event), map)
addCustomDataToJsonMap(map, event)

return map
}

companion object {
private var maskPatterns = ArrayList<String>()
}

private var multilinePattern: Pattern? = null

private fun addMaskPattern() {
maskPatterns.add("^\\w+@[a-zA-Z_]+?\\.[a-zA-Z]{2,3}\$")
maskPatterns.add("\\b(\\+?\\d{1,3}[- ]?)?\\d{9,10}\\b")
multilinePattern = Pattern.compile(
maskPatterns.stream()
.collect(Collectors.joining("|")),
Pattern.MULTILINE
)
}

fun getFormattedMessage (event: ILoggingEvent): String {
addMaskPattern()
var formattedMessage = event.formattedMessage
for(i in event.argumentArray.indices) {
val result = mask(event.argumentArray[i] as String)
formattedMessage = formattedMessage.replace(event.argumentArray[i].toString(), result)
}
return formattedMessage
}

private fun mask (defaultParsedEvent: String): String{
return Stream.of(*defaultParsedEvent.split("\\s+".toRegex())
.dropLastWhile { it.isEmpty() }
.toTypedArray())
.map { this.maskMessage(it) }
.collect(Collectors.joining(" "))
}

private fun maskMessage(event: String): String {
val matcher = multilinePattern?.matcher(event)

return if (matcher?.matches() == true)
IntStream.range(0, event.length)
.mapToObj { "*" }
.collect(Collectors.joining())
else
event
}

}

Yuxarıdakı kodu Kotlin-də yazmışıq.Gəlin ilk öncə kodda istifadə olunan methodları izah edək.

  1. toJsonMap -log-ları json formatında print etmək üçün istifadə edirik və super class olan JsonLayout-dan override olunub
  2. addMaskPattern -bu methodda biz öz regex-lərimizi vermişik və log-dakı argumentlər bu regex-lər ilə yoxlanılır
  3. getFormattedMessage-Ümumiyyətlə biz log-a nəsə yazdıqda logback bu log-a yazılacaq məlumatları event olaraq IloggingEvent interface-i və LoggingEvent class-ı vasitəsi ilə handle edir. Bu classın source koduna baxsaq burada log-a yazılan message,log üçün istifadə olunan log level ,argumentArray (göndərdiyimiz arqumentlər) və ən əsası formattedMessage var ki,bu bizim gördüyümüz outputdur.Biz yuxarıdakı kodda log-a göndərdiyimiz hər bir argumenti ilk öncə regex ilə yoxlayırıq,əgər bu sensitive data-dırsa o zaman message-də həmin hissəni mask olunmuş nəticə ilə replace edirik. Nəticədə jsonToMap metodunda formattedMessage olaraq mask olunmuş data ötürülür

kodu run edib yoxlayaq

@GetMapping("/test")
fun test() {
val email = "ali@gmail.com"
val mobile = "994500000000"
logger.info("email {} mobile {}",email,mobile)
logger.info("mobile {}", mobile)
}

Bu endpointi çağıraq.

Göründüyü kimi həm log json formatında həm də sensitive data-lar mask olunmuş şəkildədir

Verdikləri dəstəyə görə Emin Yahyayev Elvin Mahmudov-a təşəkkürlər

Diqqətinizə görə təşəkkürlər.

Originally published at https://medium.com on February 10, 2020.

--

--