Skip to content

Commit af4a041

Browse files
authored
fix: authentication bypass and denial of service (DoS) vulnerabilities in Apple Game Center auth adapter (GHSA-qf8x-vqjv-92gr) (parse-community#7962)
1 parent 852bb47 commit af4a041

File tree

2 files changed

+49
-50
lines changed

2 files changed

+49
-50
lines changed

spec/AuthenticationAdapters.spec.js

+27-40
Original file line numberDiff line numberDiff line change
@@ -1665,11 +1665,7 @@ describe('Apple Game Center Auth adapter', () => {
16651665
bundleId: 'cloud.xtralife.gamecenterauth',
16661666
};
16671667

1668-
try {
1669-
await gcenter.validateAuthData(authData);
1670-
} catch (e) {
1671-
fail();
1672-
}
1668+
await gcenter.validateAuthData(authData);
16731669
});
16741670

16751671
it('validateAuthData invalid signature id', async () => {
@@ -1690,42 +1686,33 @@ describe('Apple Game Center Auth adapter', () => {
16901686
}
16911687
});
16921688

1693-
it('validateAuthData invalid public key url', async () => {
1694-
const authData = {
1695-
id: 'G:1965586982',
1696-
publicKeyUrl: 'invalid.com',
1697-
timestamp: 1565257031287,
1698-
signature: '1234',
1699-
salt: 'DzqqrQ==',
1700-
bundleId: 'cloud.xtralife.gamecenterauth',
1701-
};
1702-
1703-
try {
1704-
await gcenter.validateAuthData(authData);
1705-
fail();
1706-
} catch (e) {
1707-
expect(e.message).toBe('Apple Game Center - invalid publicKeyUrl: invalid.com');
1708-
}
1709-
});
1710-
17111689
it('validateAuthData invalid public key http url', async () => {
1712-
const authData = {
1713-
id: 'G:1965586982',
1714-
publicKeyUrl: 'http://static.gc.apple.com/public-key/gc-prod-4.cer',
1715-
timestamp: 1565257031287,
1716-
signature: '1234',
1717-
salt: 'DzqqrQ==',
1718-
bundleId: 'cloud.xtralife.gamecenterauth',
1719-
};
1720-
1721-
try {
1722-
await gcenter.validateAuthData(authData);
1723-
fail();
1724-
} catch (e) {
1725-
expect(e.message).toBe(
1726-
'Apple Game Center - invalid publicKeyUrl: http://static.gc.apple.com/public-key/gc-prod-4.cer'
1727-
);
1728-
}
1690+
const publicKeyUrls = [
1691+
'example.com',
1692+
'http://static.gc.apple.com/public-key/gc-prod-4.cer',
1693+
'https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg',
1694+
'https://example.com/ \\.apple.com/public_key.cer',
1695+
'https://example.com/ &.apple.com/public_key.cer',
1696+
];
1697+
await Promise.all(
1698+
publicKeyUrls.map(publicKeyUrl =>
1699+
expectAsync(
1700+
gcenter.validateAuthData({
1701+
id: 'G:1965586982',
1702+
timestamp: 1565257031287,
1703+
publicKeyUrl,
1704+
signature: '1234',
1705+
salt: 'DzqqrQ==',
1706+
bundleId: 'com.example.com',
1707+
})
1708+
).toBeRejectedWith(
1709+
new Parse.Error(
1710+
Parse.Error.SCRIPT_FAILED,
1711+
`Apple Game Center - invalid publicKeyUrl: ${publicKeyUrl}`
1712+
)
1713+
)
1714+
)
1715+
);
17291716
});
17301717
});
17311718

src/Adapters/Auth/gcenter.js

+22-10
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,8 @@ const cache = {}; // (publicKey -> cert) cache
1919

2020
function verifyPublicKeyUrl(publicKeyUrl) {
2121
try {
22-
const parsedUrl = new URL(publicKeyUrl);
23-
if (parsedUrl.protocol !== 'https:') {
24-
return false;
25-
}
26-
const hostnameParts = parsedUrl.hostname.split('.');
27-
const length = hostnameParts.length;
28-
const domainParts = hostnameParts.slice(length - 2, length);
29-
const domain = domainParts.join('.');
30-
return domain === 'apple.com';
22+
const regex = /^https:\/\/(?:[-_A-Za-z0-9]+\.){0,}apple\.com\/.*\.cer$/;
23+
return regex.test(publicKeyUrl);
3124
} catch (error) {
3225
return false;
3326
}
@@ -43,7 +36,7 @@ function convertX509CertToPEM(X509Cert) {
4336
return pemPreFix + certBody + pemPostFix;
4437
}
4538

46-
function getAppleCertificate(publicKeyUrl) {
39+
async function getAppleCertificate(publicKeyUrl) {
4740
if (!verifyPublicKeyUrl(publicKeyUrl)) {
4841
throw new Parse.Error(
4942
Parse.Error.OBJECT_NOT_FOUND,
@@ -53,6 +46,25 @@ function getAppleCertificate(publicKeyUrl) {
5346
if (cache[publicKeyUrl]) {
5447
return cache[publicKeyUrl];
5548
}
49+
const url = new URL(publicKeyUrl);
50+
const headOptions = {
51+
hostname: url.hostname,
52+
path: url.pathname,
53+
method: 'HEAD',
54+
};
55+
const headers = await new Promise((resolve, reject) =>
56+
https.get(headOptions, res => resolve(res.headers)).on('error', reject)
57+
);
58+
if (
59+
headers['content-type'] !== 'application/pkix-cert' ||
60+
headers['content-length'] == null ||
61+
headers['content-length'] > 10000
62+
) {
63+
throw new Parse.Error(
64+
Parse.Error.OBJECT_NOT_FOUND,
65+
`Apple Game Center - invalid publicKeyUrl: ${publicKeyUrl}`
66+
);
67+
}
5668
return new Promise((resolve, reject) => {
5769
https
5870
.get(publicKeyUrl, res => {

0 commit comments

Comments
 (0)