Skip to content

Commit

Permalink
Add multiple captcha options for form submission
Browse files Browse the repository at this point in the history
-Supported captchas include Google reCaptcha v3 (score-based), Google reCaptcha v2 (checkbox and invisible) and hCaptcha ("Always Challenge" mode)
-Captcha options are global to the explorer even though the only form submission page is the "Claim Address" feature which takes full advantage of the new captcha options
  • Loading branch information
joeuhren committed Mar 21, 2024
1 parent 5d960ce commit cf9dce3
Show file tree
Hide file tree
Showing 5 changed files with 345 additions and 67 deletions.
157 changes: 125 additions & 32 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,43 +97,135 @@ app.use('/', routes);

// post method to claim an address using verifymessage functionality
app.post('/claim', function(req, res) {
// check if the bad-words filter is enabled
if (settings.claim_address_page.enable_bad_word_filter == true) {
// initialize the bad-words filter
var bad_word_lib = require('bad-words');
var bad_word_filter = new bad_word_lib();

// clean the message (Display name) of bad words
var message = (req.body.message == null || req.body.message == '' ? '' : bad_word_filter.clean(req.body.message));
} else {
// Do not use the bad word filter
var message = (req.body.message == null || req.body.message == '' ? '' : req.body.message);
}
// validate captcha if applicable
validate_captcha(settings.claim_address_page.enable_captcha, req.body, function(captcha_error) {
// check if there was a problem with captcha
if (captcha_error) {
// show the captcha error
res.json({'status': 'failed', 'error': true, 'message': 'The captcha validation failed'});
} else {
// check if the bad-words filter is enabled
if (settings.claim_address_page.enable_bad_word_filter == true) {
// initialize the bad-words filter
var bad_word_lib = require('bad-words');
var bad_word_filter = new bad_word_lib();

// clean the message (Display name) of bad words
var message = (req.body.message == null || req.body.message == '' ? '' : bad_word_filter.clean(req.body.message));
} else {
// do not use the bad word filter
var message = (req.body.message == null || req.body.message == '' ? '' : req.body.message);
}

// check if the message was filtered
if (message == req.body.message) {
// call the verifymessage api
lib.verify_message(req.body.address, req.body.signature, req.body.message, function(body) {
if (body == false)
res.json({'status': 'failed', 'error': true, 'message': 'Invalid signature'});
else if (body == true) {
db.update_claim_name(req.body.address, req.body.message, function(val) {
// check if the update was successful
if (val == '')
res.json({'status': 'success'});
else if (val == 'no_address')
res.json({'status': 'failed', 'error': true, 'message': 'Wallet address ' + req.body.address + ' is not valid or does not have any transactions'});
else
// check if the message was filtered
if (message == req.body.message) {
// call the verifymessage api
lib.verify_message(req.body.address, req.body.signature, req.body.message, function(body) {
if (body == false)
res.json({'status': 'failed', 'error': true, 'message': 'Invalid signature'});
else if (body == true) {
db.update_claim_name(req.body.address, req.body.message, function(val) {
// check if the update was successful
if (val == '')
res.json({'status': 'success'});
else if (val == 'no_address')
res.json({'status': 'failed', 'error': true, 'message': 'Wallet address ' + req.body.address + ' is not valid or does not have any transactions'});
else
res.json({'status': 'failed', 'error': true, 'message': 'Wallet address or signature is invalid'});
});
} else
res.json({'status': 'failed', 'error': true, 'message': 'Wallet address or signature is invalid'});
});
} else
res.json({'status': 'failed', 'error': true, 'message': 'Wallet address or signature is invalid'});
});
} else {
// message was filtered which would change the signature
res.json({'status': 'failed', 'error': true, 'message': 'Display name contains bad words and cannot be saved: ' + message});
}
}
});
});

function validate_captcha(captcha_enabled, data, cb) {
// check if captcha is enabled for the requested feature
if (captcha_enabled == true) {
// determine the captcha type
if (settings.captcha.google_recaptcha3.enabled == true) {
if (data.google_recaptcha3 != null) {
const request = require('postman-request');

request({uri: 'https://www.google.com/recaptcha/api/siteverify?secret=' + settings.captcha.google_recaptcha3.secret_key + '&response=' + data.google_recaptcha3, json: true}, function (error, response, body) {
if (error) {
// an error occurred while trying to validate the captcha
return cb(true);
} else if (body == null || body == '' || typeof body !== 'object') {
// return data is invalid
return cb(true);
} else if (body.score == null || body.score < settings.captcha.google_recaptcha3.pass_score) {
// captcha challenge failed
return cb(true);
} else {
// captcha challenge passed
return cb(false);
}
});
} else {
// a captcha response wasn't received
return cb(true);
}
} else if (settings.captcha.google_recaptcha2.enabled == true) {
if (data.google_recaptcha2 != null) {
const request = require('postman-request');

request({uri: 'https://www.google.com/recaptcha/api/siteverify?secret=' + settings.captcha.google_recaptcha2.secret_key + '&response=' + data.google_recaptcha2, json: true}, function (error, response, body) {
if (error) {
// an error occurred while trying to validate the captcha
return cb(true);
} else if (body == null || body == '' || typeof body !== 'object') {
// return data is invalid
return cb(true);
} else if (body.success == null || body.success == false) {
// captcha challenge failed
return cb(true);
} else {
// captcha challenge passed
return cb(false);
}
});
} else {
// a captcha response wasn't received
return cb(true);
}
} else if (settings.captcha.hcaptcha.enabled == true) {
if (data.hcaptcha != null) {
const request = require('postman-request');

request({uri: 'https://hcaptcha.com/siteverify?secret=' + settings.captcha.hcaptcha.secret_key + '&response=' + data.hcaptcha, json: true}, function (error, response, body) {
if (error) {
// an error occurred while trying to validate the captcha
return cb(true);
} else if (body == null || body == '' || typeof body !== 'object') {
// return data is invalid
return cb(true);
} else if (body.success == null || body.success == false) {
// captcha challenge failed
return cb(true);
} else {
// captcha challenge passed
return cb(false);
}
});
} else {
// a captcha response wasn't received
return cb(true);
}
} else {
// no captcha options are enabled
return cb(false);
}
} else {
// message was filtered which would change the signature
res.json({'status': 'failed', 'error': true, 'message': 'Display name contains bad words and cannot be saved: ' + message});
// captcha is not enabled for this feature
return cb(false);
}
});
}

// extended apis
app.use('/ext/getmoneysupply', function(req, res) {
Expand Down Expand Up @@ -859,6 +951,7 @@ app.set('markets_page', settings.markets_page);
app.set('api_page', settings.api_page);
app.set('claim_address_page', settings.claim_address_page);
app.set('orphans_page', settings.orphans_page);
app.set('captcha', settings.captcha);
app.set('labels', settings.labels);
app.set('default_coingecko_ids', settings.default_coingecko_ids);
app.set('api_cmds', settings.api_cmds);
Expand Down
50 changes: 49 additions & 1 deletion lib/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,10 @@ exports.claim_address_page = {
"show_header_menu": true,
// enable_bad_word_filter: Enable/disable the "bad word" filter for claimed addresses, so that trying to claim an address with a bad word like "ash0le" will fail
// This feature uses the default blacklist from the "bad-words" plugin from here: https://www.npmjs.com/package/bad-words
"enable_bad_word_filter": true
"enable_bad_word_filter": true,
// enable_captcha: Enable/disable using captcha security when filling out and submitting the claim address form
// NOTE: you must also configure and enable one of the options in the main "captcha" settings for this option to function correctly
"enable_captcha": false
};

// orphans_page: a collection of settings that pertain to the orphans page
Expand Down Expand Up @@ -1273,6 +1276,51 @@ exports.sync = {
"supply": "GETINFO"
};

// captcha: a collection of settings that pertain to the captcha security used by different elements of the explorer
// NOTE: only 1 captcha option can be enabled and used at any given time. If you enable 2 or more options the explorer will use the first available enabled option it finds
exports.captcha = {
// google_recaptcha3: a collection of settings that pertain to using Google reCAPTCHA v3. Signup to get your site and secret keys here: https://www.google.com/recaptcha/admin
"google_recaptcha3": {
// enabled: Enable/disable the use of Google reCAPTCHA v3 (true/false)
// If set to false, Google reCAPTCHA v3 will be completely disabled as a security feature
"enabled": false,
// pass_score: A numeric score between 0.0 and 1.0 used to deteremine if a particular captcha request passed or failed
// Google reCAPTCHA v3 returns a score value for every captcha request. 1.0 is very likely a good interaction whereas 0.0 is very likely a bot
// Google recommends using 0.5 by default but you may increase the passing score if you are receiving too many automated bot submissions or lower the score if legitimate users are having troubles passing the captcha challenge
"pass_score": 0.5,
// site_key: Enter the SITE KEY value from your Google reCAPTCHA v3 settings here
"site_key": "",
// secret_key: Enter the SECRET KEY value from your Google reCAPTCHA v3 settings here
"secret_key": ""
},
// google_recaptcha2: a collection of settings that pertain to using Google reCAPTCHA v2. Signup to get your site and secret keys here: https://www.google.com/recaptcha/admin
"google_recaptcha2": {
// enabled: Enable/disable the use of Google reCAPTCHA v2 (true/false)
// If set to false, Google reCAPTCHA v2 will be completely disabled as a security feature
"enabled": false,
// captcha_type: Determine the type of captcha to use for security validation
// Valid options:
// checkbox: The "I'm not a robot" Checkbox requires the user to click a checkbox indicating the user is not a robot. This will either pass the user immediately (with No CAPTCHA) or challenge them to validate whether or not they are human
// invisible: The invisible reCAPTCHA badge does not require the user to click on a checkbox. By default only the most suspicious traffic will be prompted to solve a captcha
"captcha_type": "checkbox",
// site_key: Enter the SITE KEY value from your Google reCAPTCHA v2 settings here
"site_key": "",
// secret_key: Enter the SECRET KEY value from your Google reCAPTCHA v2 settings here
"secret_key": ""
},
// hcaptcha: a collection of settings that pertain to using hCaptcha. Signup to get your site and secret keys here: https://dashboard.hcaptcha.com/signup
// NOTE: Only the free "Always Challenge" mode is currently supported
"hcaptcha": {
// enabled: Enable/disable the use of hCaptcha (true/false)
// If set to false, hCaptcha will be completely disabled as a security feature
"enabled": false,
// site_key: Enter the SITE KEY value from your hCaptcha settings here
"site_key": "",
// secret_key: Enter the SECRET KEY value from your hCaptcha settings here
"secret_key": ""
}
};

// labels: a collection of settings that pertain to the list of customized wallet address labels
// Adding entries to this section will display a custom label beside each affected wallet address when displayed in the explorer
// NOTE: You can add as many address labels as desired
Expand Down
50 changes: 49 additions & 1 deletion settings.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -1296,7 +1296,10 @@
"show_header_menu": true,
// enable_bad_word_filter: Enable/disable the "bad word" filter for claimed addresses, so that trying to claim an address with a bad word like "ash0le" will fail
// This feature uses the default blacklist from the "bad-words" plugin from here: https://www.npmjs.com/package/bad-words
"enable_bad_word_filter": true
"enable_bad_word_filter": true,
// enable_captcha: Enable/disable using captcha security when filling out and submitting the claim address form
// NOTE: you must also configure and enable one of the options in the main "captcha" settings for this option to function correctly
"enable_captcha": true
},

// orphans_page: a collection of settings that pertain to the orphans page
Expand Down Expand Up @@ -1357,6 +1360,51 @@
"supply": "GETINFO"
},

// captcha: a collection of settings that pertain to the captcha security used by different elements of the explorer
// NOTE: only 1 captcha option can be enabled and used at any given time. If you enable 2 or more options the explorer will use the first available enabled option it finds
"captcha": {
// google_recaptcha3: a collection of settings that pertain to using Google reCAPTCHA v3. Signup to get your site and secret keys here: https://www.google.com/recaptcha/admin
"google_recaptcha3": {
// enabled: Enable/disable the use of Google reCAPTCHA v3 (true/false)
// If set to false, Google reCAPTCHA v3 will be completely disabled as a security feature
"enabled": false,
// pass_score: A numeric score between 0.0 and 1.0 used to deteremine if a particular captcha request passed or failed
// Google reCAPTCHA v3 returns a score value for every captcha request. 1.0 is very likely a good interaction whereas 0.0 is very likely a bot
// Google recommends using 0.5 by default but you may increase the passing score if you are receiving too many automated bot submissions or lower the score if legitimate users are having troubles passing the captcha challenge
"pass_score": 0.5,
// site_key: Enter the SITE KEY value from your Google reCAPTCHA v3 settings here
"site_key": "",
// secret_key: Enter the SECRET KEY value from your Google reCAPTCHA v3 settings here
"secret_key": ""
},
// google_recaptcha2: a collection of settings that pertain to using Google reCAPTCHA v2. Signup to get your site and secret keys here: https://www.google.com/recaptcha/admin
"google_recaptcha2": {
// enabled: Enable/disable the use of Google reCAPTCHA v2 (true/false)
// If set to false, Google reCAPTCHA v2 will be completely disabled as a security feature
"enabled": false,
// captcha_type: Determine the type of captcha to use for security validation
// Valid options:
// checkbox: The "I'm not a robot" Checkbox requires the user to click a checkbox indicating the user is not a robot. This will either pass the user immediately (with No CAPTCHA) or challenge them to validate whether or not they are human
// invisible: The invisible reCAPTCHA badge does not require the user to click on a checkbox. By default only the most suspicious traffic will be prompted to solve a captcha
"captcha_type": "checkbox",
// site_key: Enter the SITE KEY value from your Google reCAPTCHA v2 settings here
"site_key": "",
// secret_key: Enter the SECRET KEY value from your Google reCAPTCHA v2 settings here
"secret_key": ""
},
// hcaptcha: a collection of settings that pertain to using hCaptcha. Signup to get your site and secret keys here: https://dashboard.hcaptcha.com/signup
// NOTE: Only the free "Always Challenge" mode is currently supported
"hcaptcha": {
// enabled: Enable/disable the use of hCaptcha (true/false)
// If set to false, hCaptcha will be completely disabled as a security feature
"enabled": false,
// site_key: Enter the SITE KEY value from your hCaptcha settings here
"site_key": "",
// secret_key: Enter the SECRET KEY value from your hCaptcha settings here
"secret_key": ""
}
},

// labels: a collection of settings that pertain to the list of customized wallet address labels
// Adding entries to this section will display a custom label beside each affected wallet address when displayed in the explorer
// NOTE: You can add as many address labels as desired
Expand Down
Loading

0 comments on commit cf9dce3

Please sign in to comment.