نظارت بر پیکربندی امنیتی زیربنایی برنامه جاوا، بینشهایی درباره قدرت کلی آن در زمینه استانداردهای رمزنگاری ارائه میدهد. JDK 12 چهار رویداد JFR Security معرفی کرد که بهصورت پیشفرض در فایلهای پیکربندی default.jfc و profile.jfc غیرفعال هستند:
jdk.SecurityPropertyModification برای ثبت فراخوانیهای متود Security.setProperty(String key, String value)jdk.TLSHandshake برای پیگیری فعالیت Handshake TLSjdk.X509Validation برای ثبت جزئیات گواهینامههای X.509 در تأیید موفقیتآمیزjdk.X509Certificate برای ثبت جزئیات گواهینامههای X.509این رویدادها همچنین به Oracle JDK 11.0.5 و 8u231 Backport شدهاند.
دو رویداد رمزنگاری JFR دیگر نیز اطلاعاتی درباره Property های اولیه امنیت JDK (jdk.InitialSecurityProperty) و تعداد فراخوانیهای متود Provider (jdk.SecurityProviderService) ارائه میدهند.
این آموزش نحوه استفاده از این رویدادهای JFR Security و سایر ابزارهای JDK (keytool, JDK Flight Recorder, JDK Mission Control) برای نظارت بر امنیت برنامه جاوا را نشان میدهد.
jdk.InitialSecurityProperty در JDK 20 معرفی شد تا جزئیات Property های اولیه امنیتی را هنگام بارگذاری ثبت کند.
همچنین میتوانید Property های اولیه امنیتی را به جریان خطای استاندارد چاپ کنید:
java -Djava.security.debug=properties
jdk.InitialSecurityProperty بهصورت پیشفرض در فایلهای پیکربندی JFR فعال است. اگر jdk.SecurityPropertyModification را نیز فعال کنید، میتوانید تنظیمات اولیه تمام Property های امنیتی و تغییرات بعدی را رصد کنید.
روشهای متعددی برای دریافت نمای کامل تغییرات وجود دارد:
jdk.SecurityPropertyModification و jdk.SecurityProviderService در پیکربندی JFR$JAVA_HOME/bin/jfr configure jdk.SecurityPropertyModification#enabled=true jdk.SecurityProviderService#enabled=true
-XX:StartFlightRecording با تنظیمات پیشفرضjava -XX:StartFlightRecording:settings=default,duration=60s,+jdk.SecurityPropertyModification#enabled=true,+jdk.SecurityProviderService#enabled=true
میتوانید ضبط را از JMC یا خط فرمان شروع کنید:
-XX:StartFlightRecording یاjcmdjcmd llvmid JFR.start duration=60s filename=recording.jfr
پس از دریافت فایل ضبط ".jfr"، میتوانید رویدادها را با ابزار jfr چاپ کنید:
$JAVA_HOME/bin/jfr print --events "*Security*" /tmp/recording.jfr
jdk.InitialSecurityProperty {
startTime = 20:15:48.871 (2023-11-29)
key = "keystore.type"
value = "pkcs12"
eventThread = "main" (javaThreadId = 1)
}
....
jdk.SecurityPropertyModification {
startTime = 20:15:48.944 (2023-11-29)
key = "keystore.type"
value = "jks"
eventThread = "main" (javaThreadId = 1)
stackTrace = [
java.security.Security.setProperty(String, String) line: 762
CryptoExample.main() line: 26
]
}
jdk.SecurityProviderService {
startTime = 20:15:50.630 (2023-11-29)
type = "SecureRandom"
algorithm = "NativePRNG"
provider = "SUN"
eventThread = "Attach Listener" (javaThreadId = 37)
stackTrace = [
java.security.Provider.getService(String, String) line: 1298
java.security.SecureRandom.getDefaultPRNG(boolean, byte[]) line: 279
java.security.SecureRandom.<init>() line: 225
java.rmi.server.UID.<init>() line: 112
java.rmi.server.ObjID.<clinit>() line: 88
...
]
}
...
#output trimmed from a total of 98 security related events
با تحلیل خروجی این دستور، میتوانید تغییرات هر Property امنیتی بین مقادیر اولیه و تغییرات بعدی را مشاهده کنید.
همچنین میتوانید تکامل رویدادهای ضبطشده را در JDK Mission Control با بارگذاری فایل ضبط و ناوبری به بخش Event Browser > Java Development Kit > Security مشاهده و تجسم کنید.
JMC علاوه بر نمای جدولی رویدادها، بینشهای تحلیل عملکرد را از طریق نماهای خود ارائه میدهد:
برای دریافت اطلاعات پروتکل TLS، میتوانید ابزار تحلیل پروتکل شبکه را به Interface شبکه متصل کنید. به دنبال رکورد "Server Hello" و مقدار نسخه همراه بگردید.
اما روشی دوستدار توسعهدهندهتر، بررسی لاگهای Debug JDK است. با فعال کردن Property javax.net.debug به مقدار ssl:handshake، مقدار نسخه TLS دریافت میشود:
java -Djavax.net.debug=ssl:handshake
نمونه خروجی ServerHello در JDK 21:
"ServerHello": {
"server version" : "TLSv1.2",
"random" : "D36A78A81EA96FA48CAA23D0397E2EDD1FBA783D2B105A8C00D58D7EE74E24A4",
"session id" : "A998EB34379D24829F6E8884D4D2BCC39BACEF6D77C4B9435D104779DC6003CD",
"cipher suite" : "TLS_AES_256_GCM_SHA384(0x1302)",
"compression methods" : "00",
"extensions" : [
"supported_versions (43)": {
"selected version": [TLSv1.3]
},
"key_share (51)": {
"server_share": {
"named group": x25519
"key_exchange": {
0000: 39 EC 40 25 89 1A 75 FF EF 53 0C 36 58 57 1F F8 9.@%..u..S.6XW..
0010: 23 F6 07 D6 9E A8 E4 43 F1 6C 20 F7 AE 5E B1 79 #......C.l ..^.y
}
},
}
]
}
خروجی بالا نشان میدهد TLSv1.3 برای این اتصال خاص استفاده شده است.
در بلندمدت، بازرسی لاگها کار خستهکنندهای است. گزینه ارزشمندی برای دریافت اطلاعات TLS از طریق JDK Flight Recorder است. رویداد jdk.TLSHandshake اطلاعات اصلی هر Handshake TLS انجامشده توسط JDK را ضبط میکند. فعال کردن آن به روشهای زیر است:
jdk.TLSHandshake به true در فایل پیکربندی JFR:<event name="jdk.TLSHandshake">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>
jfr configure:$JAVA_HOME/bin/jfr configure jdk.TLSHandshake#enabled=true jdk.TLSHandshake#stackTrace=true
-XX:StartFlightRecording:java -XX:StartFlightRecording:settings=default,duration=60s,+jdk.TLSHandshake#enabled=true,+jdk.TLSHandshake#stackTrace=true
jcmd llvmid JFR.start duration=60s filename=/tmp/TLS.jfr
با دریافت ضبط، میتوانید رویداد TLSHandshake را با jfr یا در JMC تحلیل کنید:
$JAVA_HOME/bin/jfr print --events "TLS*" /tmp/TLS.jfr
jdk.TLSHandshake {
startTime = 15:28:42.949 (2023-11-30)
peerHost = "google.com"
peerPort = 443
protocolVersion = "TLSv1.3"
cipherSuite = "TLS_AES_128_GCM_SHA256"
certificateId = 587815551
eventThread = "main" (javaThreadId = 1)
stackTrace = [
sun.security.ssl.Finished.recordEvent(SSLSessionImpl) line: 1165
sun.security.ssl.Finished$T13FinishedProducer.onProduceFinished(ClientHandshakeContext, SSLHandshake$HandshakeMessage) line: 767
sun.security.ssl.Finished$T13FinishedProducer.produce(ConnectionContext, SSLHandshake$HandshakeMessage) line: 672
sun.security.ssl.SSLHandshake.produce(ConnectionContext, SSLHandshake$HandshakeMessage) line: 437
sun.security.ssl.Finished$T13FinishedConsumer.onConsumeFinished(ClientHandshakeContext, ByteBuffer) line: 1030
...
]
}
فیلدهای رویداد قابل مشاهده در خروجی:
گواهینامههای X.509 بهطور گسترده در برنامههای JDK برای پشتیبانی از احراز هویت و سایر قابلیتها استفاده میشوند. یک گواهینامه X.509 مجموعهای از فیلدها مطابق RFC 1422 دارد و شامل موارد زیر است:
مقدار این فیلدها بر پیکربندی امنیتی زیربنایی تأثیر میگذارد. بهعنوان مثال، دوره اعتبار گواهینامه دادهای حیاتی است، چون گواهینامههای منقضیشده میتوانند باعث قطعی برنامه شوند.
از نظر تحلیل استاتیک، میتوانید از keytool برای پرسوجوی گواهینامهها استفاده کنید:
$JAVA_HOME/bin/keytool -cacerts -list -v
برای دریافت جزئیات گواهینامههای واقعاً مورد استفاده یک برنامه، Property های Debug زیر را تنظیم کنید:
java -Djava.security.debug=certpath -Djavax.net.debug=all
نمونه خروجی گواهینامههای X.509 در حین تلاشهای تأیید مسیر گواهی:
Trusted CA cert: [
[
Version: V3
Subject: CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 2048 bits
params: null
modulus: 28559384442792876273280274398620578979733786817784174960112400169719065906301471912340204391164075730987771255281479191858503912379974443363319206013285922932969143082114108995903507302607372164107846395526169928849546930352778612946811335349917424469188917500996253619438384218721744278787164274625243781917237444202229339672234113350935948264576180342492691117960376023738627349150441152487120197333042448834154779966801277094070528166918968412433078879939664053044797116916260095055641583506170045241549105022323819314163625798834513544420165235412105694681616578431019525684868803389424296613694298865514217451303
public exponent: 65537
Validity: [From: Fri Nov 10 00:00:00 UTC 2006,
To: Mon Nov 10 00:00:00 UTC 2031]
Issuer: CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
SerialNumber: [ 083be056 904246b1 a1756ac9 5991c74a]
===
Certificate details are also printed during TLS handshake messages. e.g.:
"certificate" : {
"version" : "v3",
"serial number" : "083BE056904246B1A1756AC95991C74A",
"signature algorithm": "SHA1withRSA",
"issuer" : "CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US",
"not before" : "2006-11-10 24:00:00.000 UTC",
"not after" : "2031-11-10 24:00:00.000 UTC",
"subject" : "CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US",
"subject public key" : "RSA",
لاگ verbose سیستمها را کند میکند. میتوانید دادههای مرتبط با گواهینامههای X.509 را با دو رویداد JFR Security بهصورت هوشمندانه ضبط کنید:
jdk.X509Validation: جزئیات گواهینامههای X.509 در تأییدهای موفقیتآمیز را ثبت میکند.jdk.X509Certificate: اطلاعات هر گواهینامه X.509 تولیدشده توسط کتابخانههای امنیتی JDK را ضبط میکند.فعال کردن این رویدادها:
<event name="jdk.X509Certificate">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.X509Validation">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>
jfr configure:$JAVA_HOME/bin/jfr configure jdk.X509Certificate#enabled=true jdk.X509Validation#enabled=true
-XX:StartFlightRecording:java -XX:StartFlightRecording:settings=default,duration=60s,+jdk.X509Certificate#enabled=true,+jdk.X509Validation#enabled=true
jcmd llvmid JFR.start duration=60s filename=/tmp/myTLSApp.jfr
نمونه خروجی گواهینامههای X.509:
$JAVA_HOME/bin/jfr print --events jdk.X509Certificate /tmp/myTLSApp.jfr
jdk.X509Certificate {
startTime = 09:59:25.672 (2022-11-10)
algorithm = "SHA1withRSA"
serialNumber = "18dad19e267de8bb4a2158cdcc6b3b4a"
subject = "CN=VeriSign Class 3 Public Primary Certification Authority - G5, OU="(c) 2006 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US"
issuer = "CN=VeriSign Class 3 Public Primary Certification Authority - G5, OU="(c) 2006 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US"
keyType = "RSA"
keyLength = 2048
certificateId = 303010488
validFrom = 00:00:00.000 (2006-11-08)
validUntil = 23:59:59.000 (2036-07-16)
eventThread = "main" (javaThreadId = 1)
stackTrace = [
sun.security.jca.JCAUtil.tryCommitCertEvent(Certificate) line: 126
java.security.cert.CertificateFactory.generateCertificate(InputStream) line: 356
sun.security.pkcs12.PKCS12KeyStore.loadSafeContents(DerInputStream) line: 2428
sun.security.pkcs12.PKCS12KeyStore.engineLoad(InputStream, char[]) line: 2038
sun.security.util.KeyStoreDelegator.engineLoad(InputStream, char[]) line: 228
java.security.KeyStore.load(InputStream, char[]) line: 1500
java.security.KeyStore.getInstance(File, char[], KeyStore$LoadStoreParameter, boolean) line: 1828
java.security.KeyStore.getInstance(File, char[]) line: 1709
sun.security.tools.KeyStoreUtil.getCacertsKeyStore() line: 137
sun.security.tools.keytool.Main.buildTrustedCerts() line: 5072
sun.security.tools.keytool.Main.doCommands(PrintStream) line: 1122
sun.security.tools.keytool.Main.run(String[], PrintStream) line: 419
...
]
}
JDK Flight Recorder دادههای غنی و ساختاریافته مانند Stack Trace و مقادیر زماندار و پشتیبانی API از جریان رویدادها ارائه میدهد.
از JDK 16، میتوانید رویدادهای ضبطشده را بهصورت برنامهای از طریق شبکه با RemoteRecordingStream منتقل کنید:
String host = "com.example";
int port = 7091;
String url = "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";
JMXServiceURL u = new JMXServiceURL(url);
JMXConnector c = JMXConnectorFactory.connect(u);
MBeanServerConnection connection = c.getMBeanServerConnection();
try (RemoteRecordingStream stream = new RemoteRecordingStream(connection)) {
stream.enabled("jdk.X509Certificate").withStackTrace();
stream.onEvent("jdk.X509Certificate", IO::println),
stream.start();
}
از ابزارها و API های JDK موجود استفاده کنید و تنظیمات امنیتی و دادههای گواهینامه را در Runtime تحلیل کنید تا برنامه خود را ایمن نگه دارید!
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/