Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RSA validation to Criteo FastBid #3110

Merged
merged 7 commits into from
Oct 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 45 additions & 4 deletions modules/criteoBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { registerBidder } from 'src/adapters/bidderFactory';
import { parse } from 'src/url';
import * as utils from 'src/utils';
import find from 'core-js/library/fn/array/find';
import JSEncrypt from 'jsencrypt/bin/jsencrypt';
import sha256 from 'crypto-js/sha256';

const ADAPTER_VERSION = 11;
const ADAPTER_VERSION = 14;
const BIDDER_CODE = 'criteo';
const CDB_ENDPOINT = '//bidder.criteo.com/cdb';
const CRITEO_VENDOR_ID = 91;
Expand All @@ -17,6 +19,13 @@ const PROFILE_ID_PUBLISHERTAG = 185;
// Unminified source code can be found in: https://github.com/Prebid-org/prebid-js-external-js-criteo/blob/master/dist/prod.js
const PUBLISHER_TAG_URL = '//static.criteo.net/js/ld/publishertag.prebid.js';

export const FAST_BID_PUBKEY = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDO1BjAITkFTtP0IMzmF7qsqhpu
y1dGaTPHnjMU9mRZsrnfR3C0sEN5pYEzEcFRPnkJjJuhH8Rnh5+CE+LcKg0Z8ZZ7
OmOSj0/qnYTAYCu0cR5LiyWG79KlIgUyMbp92ulGg24gAyGrVn4+v/4c53WlOEUp
4YWvb82G0CD5NcDNpQIDAQAB
-----END PUBLIC KEY-----`;

/** @type {BidderSpec} */
export const spec = {
code: BIDDER_CODE,
Expand Down Expand Up @@ -251,20 +260,52 @@ function createNativeAd(id, payload, callback) {
</script>`;
}

export function cryptoVerify(key, hash, code) {
var jse = new JSEncrypt();
jse.setPublicKey(key);
return jse.verify(code, hash, sha256);
}

function validateFastBid(fastBid) {
// The value stored must contain the file's encrypted hash as first line
const firstLineEnd = fastBid.indexOf('\n');
const firstLine = fastBid.substr(0, firstLineEnd).trim();
if (firstLine.substr(0, 9) !== '// Hash: ') {
utils.logWarn('No hash found in FastBid');
return false;
}

// Remove the hash part from the locally stored value
const fileEncryptedHash = firstLine.substr(9);
const publisherTag = fastBid.substr(firstLineEnd + 1);

// Verify the hash using cryptography
try {
return cryptoVerify(FAST_BID_PUBKEY, fileEncryptedHash, publisherTag);
} catch (e) {
utils.logWarn('Failed to verify Criteo FastBid');
return undefined;
}
}

/**
* @return {boolean}
*/
function tryGetCriteoFastBid() {
try {
const fastBid = localStorage.getItem('criteo_fast_bid');
if (fastBid !== null) {
eval(fastBid); // eslint-disable-line no-eval
return true;
if (validateFastBid(fastBid) === false) {
utils.logWarn('Invalid Criteo FastBid found');
localStorage.removeItem('criteo_fast_bid');
} else {
utils.logInfo('Using Criteo FastBid');
eval(fastBid); // eslint-disable-line no-eval
}
}
} catch (e) {
// Unable to get fast bid
}
return false;
}

registerBidder(spec);
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@
"dependencies": {
"babel-plugin-transform-object-assign": "^6.22.0",
"core-js": "^2.4.1",
"crypto-js": "^3.1.9-1",
"gulp-sourcemaps": "^2.6.0",
"jsencrypt": "^3.0.0-rc.1",
"just-clone": "^1.0.2"
}
}
19 changes: 18 additions & 1 deletion test/spec/modules/criteoBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'chai';
import { spec } from 'modules/criteoBidAdapter';
import { cryptoVerify, spec, FAST_BID_PUBKEY } from 'modules/criteoBidAdapter';
import * as utils from 'src/utils';

describe('The Criteo bidding adapter', function () {
Expand Down Expand Up @@ -294,4 +294,21 @@ describe('The Criteo bidding adapter', function () {
expect(bids[0].height).to.equal(90);
});
});

describe('cryptoVerify', function () {
const TEST_HASH = 'vBeD8Q7GU6lypFbzB07W8hLGj7NL+p7dI9ro2tCxkrmyv0F6stNuoNd75Us33iNKfEoW+cFWypelr6OJPXxki2MXWatRhJuUJZMcK4VBFnxi3Ro+3a0xEfxE4jJm4eGe98iC898M+/YFHfp+fEPEnS6pEyw124ONIFZFrcejpHU=';

it('should verify right signature', function () {
expect(cryptoVerify(FAST_BID_PUBKEY, TEST_HASH, 'test')).to.equal(true);
});

it('should verify wrong signature', function () {
expect(cryptoVerify(FAST_BID_PUBKEY, TEST_HASH, 'test wrong')).to.equal(false);
});

it('should return undefined with incompatible browsers', function () {
// Here use a null hash to make the call to crypto library fail and simulate a browser failure
expect(cryptoVerify(FAST_BID_PUBKEY, null, 'test')).to.equal.undefined;
});
});
});