Harden JavaScript SDK
4 minute read
This guide shows you how to harden the JavaScript SDK by reducing its reliance on external resources that could be used to disrupt the events flow or compromise your app. For a hardened setup, RudderStack recommends installing the SDK using NPM. If you load the SDK from the RudderStack CDN, see Harden the CDN loading snippet.
SDK hardening steps
- Use a named export that bundles plugins while packaging RudderStack directly in your project using NPM. By using the NPM package export that bundles the plugins code, the SDK does not make any request to dynamically download the plugins.
You can also:
- Self-host the plugin files and integration SDKs and serve them over your domain.
- Proxy the RudderStack domains and use your own domain to serve the JavaScript SDK.
import { RudderAnalytics } from '@rudderstack/analytics-js/bundled';var RudderAnalytics = require("@rudderstack/analytics-js/bundled");- Override/mock source configuration data. By mocking the source configuration response to the SDK, RudderStack does not make any source configuration request again.
rudderAnalytics.load(WRITE_KEY, DATA_PLANE_URL, {
polyfillIfRequired: false,
getSourceConfig: () => ({
updatedAt: new Date().toISOString(),
source: {
// Use relevant valid values from the RudderStack dashboard
name: SOURCE_NAME,
id: SOURCE_ID,
workspaceId: WORKSPACE_ID,
writeKey: WRITE_KEY,
updatedAt: new Date().toISOString(),
config: {
statsCollection: {
errors: {
enabled: false
},
metrics: {
enabled: false
}
}
},
enabled: true,
destinations: []
}
})
});- Disable the loading of polyfills by setting
polyfillIfRequiredtofalse. By disabling polyfills, the SDK does not dynamically add a polyfills script from a third-party domain.
rudderAnalytics.load(WRITE_KEY, DATA_PLANE_URL, {
polyfillIfRequired: false
});RudderStack also recommends using only the cloud mode destinations to avoid loading any other third-party SDKs on the website.
Harden the CDN loading snippet
RudderStack recommends the NPM installation described above. If you must load the SDK using the CDN loading snippet, apply the following edits to the Original snippet before adding it to your website. The examples pin version 3.35.2; replace it with the latest SDK version.
Pin the SDK version
Change only sdkVersion, and leave sdkFileName unchanged. Keep sdkBaseUrl unchanged unless you serve the SDK from your own domain:
var sdkBaseUrl = "https://cdn.rudderlabs.com";
var sdkVersion = "3.35.2"; // Changed: pin an exact SDK version.
var sdkFileName = "rsa.min.js";The snippet builds the URL from these values and the build type it selects for the browser, resulting in https://cdn.rudderlabs.com/3.35.2/modern/rsa.min.js or https://cdn.rudderlabs.com/3.35.2/legacy/rsa.min.js.
Plugins and integration SDKs
The SDK derives the plugins and device mode integration SDK paths from the core SDK’s own script URL, so pinning sdkVersion also pins them — provided you do not set destSDKBaseURL or pluginsSDKBaseURL in your load options.
If your snippet sets either option (some RudderStack sample apps do), update it to the pinned version as well, or remove it so the SDK derives the path itself.
To avoid downloading these files from the RudderStack CDN, use the bundled NPM export in step 1 or self-host the plugins and integration SDKs.
Device mode destinations such as Google and Meta also load vendor SDKs from the vendors’ own CDNs, typically through unversioned URLs that RudderStack cannot pin. Use cloud mode destinations to avoid loading these vendor SDKs on your website.
A pinned SDK version does not receive fixes or security updates automatically. Upgrade to the latest version frequently: watch the JavaScript SDK releases and updatesdkVersionwith each release.
Final instrumentation
For CDN installations, pin sdkVersion in the loading snippet as described above and replace its existing load call with the one in the CDN tab.
import { RudderAnalytics } from '@rudderstack/analytics-js/bundled';
const rudderAnalytics = new RudderAnalytics();
rudderAnalytics.load(WRITE_KEY, DATA_PLANE_URL, {
polyfillIfRequired: false,
getSourceConfig: () => ({
updatedAt: new Date().toISOString(),
source: {
// Use relevant valid values from the RudderStack dashboard
name: SOURCE_NAME,
id: SOURCE_ID,
workspaceId: WORKSPACE_ID,
writeKey: WRITE_KEY,
updatedAt: new Date().toISOString(),
config: {
statsCollection: {
errors: {
enabled: false
},
metrics: {
enabled: false
}
}
},
enabled: true,
destinations: []
}
})
});
export { rudderAnalytics };var RudderAnalytics = require("@rudderstack/analytics-js/bundled");
const rudderAnalytics = new RudderAnalytics();
rudderAnalytics.load(WRITE_KEY, DATA_PLANE_URL, {
polyfillIfRequired: false,
getSourceConfig: () => ({
updatedAt: new Date().toISOString(),
source: {
// Use relevant valid values from the RudderStack dashboard
name: SOURCE_NAME,
id: SOURCE_ID,
workspaceId: WORKSPACE_ID,
writeKey: WRITE_KEY,
updatedAt: new Date().toISOString(),
config: {
statsCollection: {
errors: {
enabled: false
},
metrics: {
enabled: false
}
}
},
enabled: true,
destinations: []
}
})
});
exports.rudderAnalytics = rudderAnalytics;// In the loading snippet
var sdkVersion = "3.35.2"; // Changed: pin an exact SDK version.
// Replace the snippet's existing load call
rudderanalytics.load(WRITE_KEY, DATA_PLANE_URL, {
polyfillIfRequired: false,
getSourceConfig: () => ({
updatedAt: new Date().toISOString(),
source: {
// Use relevant valid values from the RudderStack dashboard
name: SOURCE_NAME,
id: SOURCE_ID,
workspaceId: WORKSPACE_ID,
writeKey: WRITE_KEY,
updatedAt: new Date().toISOString(),
config: {
statsCollection: {
errors: {
enabled: false
},
metrics: {
enabled: false
}
}
},
enabled: true,
destinations: []
}
})
});