Skip to content

Commit 592fde0

Browse files
authored
Address CVE-2025-59250: Fix spoofing vulnerability in JDBC Driver for SQL Server due to improper input validation and update version to 12.8.2 (#2804)
1 parent 0cd21dc commit 592fde0

File tree

17 files changed

+215
-65
lines changed

17 files changed

+215
-65
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file.
33

44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55

6+
## [12.8.2] Hotfix & Stable Release
7+
### Fixed issues
8+
- **Address a hostname validation vulnerability by securely parsing certificate common names.**
9+
**What was fixed**: Secure hostname validation is enforced by replacing the vulnerable CN parsing logic in SQLServerCertificateUtils.java, preventing spoofing attacks.
10+
**Who benefits**: All users of the SQL Server JDBC driver, especially those relying on TLS for secure connections, benefit from improved certificate validation.
11+
612
## [12.8.1] Hotfix & Stable Release
713
### Changed
814
- Changed MSAL logging from FINER to FINEST [#2491](https://github.com/microsoft/mssql-jdbc/pull/2491)

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ We're now on the Maven Central Repository. Add the following to your POM file to
8383
<dependency>
8484
<groupId>com.microsoft.sqlserver</groupId>
8585
<artifactId>mssql-jdbc</artifactId>
86-
<version>12.8.1.jre11</version>
86+
<version>12.8.2.jre11</version>
8787
</dependency>
8888
```
8989
The driver can be downloaded from [Microsoft](https://aka.ms/downloadmssqljdbc). For driver version 12.1.0 and greater, please use the jre11 version when using Java 11 or greater, and the jre8 version when using Java 8.
@@ -94,7 +94,7 @@ To get the latest version of the driver, add the following to your POM file:
9494
<dependency>
9595
<groupId>com.microsoft.sqlserver</groupId>
9696
<artifactId>mssql-jdbc</artifactId>
97-
<version>12.8.1.jre11</version>
97+
<version>12.8.2.jre11</version>
9898
</dependency>
9999
```
100100

@@ -129,7 +129,7 @@ Projects that require either of the two features need to explicitly declare the
129129
<dependency>
130130
<groupId>com.microsoft.sqlserver</groupId>
131131
<artifactId>mssql-jdbc</artifactId>
132-
<version>12.8.1.jre11</version>
132+
<version>12.8.2.jre11</version>
133133
<scope>compile</scope>
134134
</dependency>
135135

@@ -147,7 +147,7 @@ Projects that require either of the two features need to explicitly declare the
147147
<dependency>
148148
<groupId>com.microsoft.sqlserver</groupId>
149149
<artifactId>mssql-jdbc</artifactId>
150-
<version>12.8.1.jre11</version>
150+
<version>12.8.2.jre11</version>
151151
<scope>compile</scope>
152152
</dependency>
153153

@@ -174,7 +174,7 @@ When setting 'useFmtOnly' property to 'true' for establishing a connection or cr
174174
<dependency>
175175
<groupId>com.microsoft.sqlserver</groupId>
176176
<artifactId>mssql-jdbc</artifactId>
177-
<version>12.8.1.jre11</version>
177+
<version>12.8.2.jre11</version>
178178
</dependency>
179179

180180
<dependency>

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
apply plugin: 'java'
1313

14-
version = '12.8.1'
14+
version = '12.8.2'
1515
def releaseExt = ''
1616
def jreVersion = ""
1717
def testOutputDir = file("build/classes/java/test")

mssql-jdbc_auth_LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
MICROSOFT SOFTWARE LICENSE TERMS
2-
MICROSOFT JDBC DRIVER 12.8.1 FOR SQL SERVER
2+
MICROSOFT JDBC DRIVER 12.8.2 FOR SQL SERVER
33

44
These license terms are an agreement between you and Microsoft Corporation (or one of its affiliates). They apply to the software named above and any Microsoft services or software updates (except to the extent such services or updates are accompanied by new or additional terms, in which case those different terms apply prospectively and do not alter your or Microsoft’s rights relating to pre-updated software or services). IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS.
55

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<modelVersion>4.0.0</modelVersion>
66
<groupId>com.microsoft.sqlserver</groupId>
77
<artifactId>mssql-jdbc</artifactId>
8-
<version>12.8.1</version>
8+
<version>12.8.2</version>
99
<packaging>jar</packaging>
1010
<name>Microsoft JDBC Driver for SQL Server</name>
1111
<description>

src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
final class SQLJdbcVersion {
99
static final int MAJOR = 12;
1010
static final int MINOR = 8;
11-
static final int PATCH = 1;
11+
static final int PATCH = 2;
1212
static final int BUILD = 0;
1313
/*
1414
* Used to load mssql-jdbc_auth DLL.

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCertificateUtils.java

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
import javax.crypto.spec.SecretKeySpec;
5151
import javax.net.ssl.KeyManager;
5252
import javax.net.ssl.KeyManagerFactory;
53+
import javax.naming.ldap.LdapName;
54+
import javax.naming.ldap.Rdn;
55+
import javax.security.auth.x500.X500Principal;
5356

5457
import org.bouncycastle.openssl.PEMDecryptorProvider;
5558
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
@@ -77,45 +80,6 @@ static KeyManager[] getKeyManagerFromFile(String certPath, String keyPath,
7780
}
7881
}
7982

80-
/**
81-
* Parse name in RFC 2253 format Returns the common name if successful, null if failed to find the common name. The
82-
* parser tuned to be safe than sorry so if it sees something it can't parse correctly it returns null
83-
*
84-
* @param distinguishedName
85-
* server name to parse
86-
* @return subject name
87-
*/
88-
static String parseCommonName(String distinguishedName) {
89-
int index;
90-
// canonical name converts entire name to lowercase
91-
index = distinguishedName.indexOf("cn=");
92-
if (index == -1) {
93-
return null;
94-
}
95-
distinguishedName = distinguishedName.substring(index + 3);
96-
// Parse until a comma or end is reached
97-
// Note the parser will handle gracefully (essentially will return empty string) , inside the quotes (e.g
98-
// cn="Foo, bar") however
99-
// RFC 952 says that the hostName cant have commas however the parser should not (and will not) crash if it
100-
// sees a , within quotes.
101-
for (index = 0; index < distinguishedName.length(); index++) {
102-
if (distinguishedName.charAt(index) == ',') {
103-
break;
104-
}
105-
}
106-
String commonName = distinguishedName.substring(0, index);
107-
// strip any quotes
108-
if (commonName.length() > 1 && ('\"' == commonName.charAt(0))) {
109-
if ('\"' == commonName.charAt(commonName.length() - 1))
110-
commonName = commonName.substring(1, commonName.length() - 1);
111-
else {
112-
// Be safe the name is not ended in " return null so the common Name wont match
113-
commonName = null;
114-
}
115-
}
116-
return commonName;
117-
}
118-
11983
/**
12084
* Validate server name in certificate matches hostname
12185
*
@@ -183,7 +147,11 @@ static boolean validateServerName(String nameInCert, String hostName) {
183147
* @throws CertificateException
184148
*/
185149
static void validateServerNameInCertificate(X509Certificate cert, String hostName) throws CertificateException {
186-
String nameInCertDN = cert.getSubjectX500Principal().getName("canonical");
150+
// Use RFC2253 format and secure CN parsing to avoid ambiguities introduced by
151+
// the "canonical" format (which lowercases and reverses RDN order). We rely
152+
// on LdapName/Rdn to securely parse the DN and extract the CN attribute only.
153+
X500Principal subjectPrincipal = cert.getSubjectX500Principal();
154+
String nameInCertDN = subjectPrincipal.getName(X500Principal.RFC2253);
187155

188156
if (logger.isLoggable(Level.FINER)) {
189157
logger.finer(logContext + " Validating the server name:" + hostName);
@@ -194,7 +162,13 @@ static void validateServerNameInCertificate(X509Certificate cert, String hostNam
194162
String dnsNameInSANCert = "";
195163

196164
// the name in cert is in RFC2253 format parse it to get the actual subject name
197-
String subjectCN = parseCommonName(nameInCertDN);
165+
String subjectCN = parseCommonNameSecure(cert);
166+
// X.509 certificate standard requires domain names to be in ASCII.
167+
// Even IDN (Unicode) names will be represented here in Punycode (ASCII).
168+
// Normalize case for comparison using English to avoid case issues like Turkish i.
169+
if (subjectCN != null) {
170+
subjectCN = subjectCN.toLowerCase(Locale.ENGLISH);
171+
}
198172

199173
isServerNameValidated = validateServerName(subjectCN, hostName);
200174

@@ -530,4 +504,35 @@ private static InputStream fileToStream(String fname) throws IOException, SQLSer
530504
private static String getStringFromFile(String filePath) throws IOException {
531505
return new String(Files.readAllBytes(Paths.get(filePath)));
532506
}
507+
508+
/**
509+
* Securely parse the Common Name (CN) from the certificate subject using
510+
* LdapName/Rdn APIs and RFC2253 string representation to avoid mis-parsing
511+
* values that appear inside other attributes when canonical form is used.
512+
*/
513+
static String parseCommonNameSecure(X509Certificate cert) {
514+
try {
515+
X500Principal subjectPrincipal = cert.getSubjectX500Principal();
516+
String dn = subjectPrincipal.getName(X500Principal.RFC2253);
517+
518+
LdapName ldapDN = new LdapName(dn);
519+
for (Rdn rdn : ldapDN.getRdns()) {
520+
if ("CN".equalsIgnoreCase(rdn.getType())) {
521+
String cnValue = rdn.getValue().toString();
522+
return cnValue;
523+
}
524+
}
525+
526+
if (logger.isLoggable(Level.FINER)) {
527+
logger.finer(logContext + " No CN found in certificate subject");
528+
}
529+
return null;
530+
531+
} catch (Exception e) {
532+
if (logger.isLoggable(Level.WARNING)) {
533+
logger.warning(logContext + " Error parsing certificate: " + e.getMessage());
534+
}
535+
return null;
536+
}
537+
}
533538
}

src/samples/adaptive/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<dependency>
1616
<groupId>com.microsoft.sqlserver</groupId>
1717
<artifactId>mssql-jdbc</artifactId>
18-
<version>12.8.0.jre11</version>
18+
<version>12.8.2.jre11</version>
1919
</dependency>
2020
</dependencies>
2121
<profiles>

src/samples/alwaysencrypted/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<dependency>
1616
<groupId>com.microsoft.sqlserver</groupId>
1717
<artifactId>mssql-jdbc</artifactId>
18-
<version>12.8.0.jre11</version>
18+
<version>12.8.2.jre11</version>
1919
</dependency>
2020
</dependencies>
2121
<profiles>

src/samples/azureactivedirectoryauthentication/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<dependency>
1515
<groupId>com.microsoft.sqlserver</groupId>
1616
<artifactId>mssql-jdbc</artifactId>
17-
<version>12.8.0.jre11</version>
17+
<version>12.8.2.jre11</version>
1818
</dependency>
1919
</dependencies>
2020
<profiles>

0 commit comments

Comments
 (0)