Fossil SCM

Robot defense uses a mousedown event rather than mouse motion as one of the signals that the request is from a human. This should make robot defense work better for users on mobile.

drh 2022-02-12 00:38 trunk
Commit 8d4e11432d2d54a7c3445b465d14c5ce99e2c10abfe97bbe5e659b31af5da146
+15 -10
--- src/href.js
+++ src/href.js
@@ -12,14 +12,17 @@
1212
**
1313
** {"delay":MILLISECONDS, "mouseover":BOOLEAN}
1414
**
1515
** The <script> must have an id='href-data'. DELAY is the number
1616
** milliseconds delay prior to populating href= and action=. If the
17
-** mouseover boolean is true, then the timer does not start until a
18
-** mouse motion event occurs over top of the document.
17
+** mouseover boolean is true, then the href= rewrite is further delayed
18
+** until the first mousedown event that occurs after the timer expires.
1919
*/
20
-function setAllHrefs(){
20
+var antiRobotOnce = 0;
21
+function antiRobotSetAllHrefs(){
22
+ if( antiRobotOnce ) return;
23
+ antiRobotOnce = 1;
2124
var anchors = document.getElementsByTagName("a");
2225
for(var i=0; i<anchors.length; i++){
2326
var j = anchors[i];
2427
if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
2528
}
@@ -26,21 +29,23 @@
2629
var forms = document.getElementsByTagName("form");
2730
for(var i=0; i<forms.length; i++){
2831
var j = forms[i];
2932
if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
3033
}
34
+}
35
+function antiRobotSetMouseEventHandler(){
36
+ document.getElementsByTagName("body")[0].onmousedown=function(){
37
+ antiRobotSetAllHrefs();
38
+ document.getElementsByTagName("body")[0].onmousedown=null;
39
+ }
3140
}
3241
function antiRobotDefense(){
3342
var x = document.getElementById("href-data");
3443
var jx = x.textContent || x.innerText;
3544
var g = JSON.parse(jx);
36
- var isOperaMini =
37
- Object.prototype.toString.call(window.operamini)==="[object OperaMini]";
38
- if(g.mouseover && !isOperaMini){
39
- document.getElementsByTagName("body")[0].onmousemove=function(){
40
- setTimeout(setAllHrefs, g.delay);
41
- }
45
+ if( g.mouseover ){
46
+ setTimeout(antiRobotSetMouseEventHandler, g.delay);
4247
}else{
43
- setTimeout(setAllHrefs, g.delay);
48
+ setTimeout(antiRobotSetAllHrefs, g.delay);
4449
}
4550
}
4651
antiRobotDefense();
4752
--- src/href.js
+++ src/href.js
@@ -12,14 +12,17 @@
12 **
13 ** {"delay":MILLISECONDS, "mouseover":BOOLEAN}
14 **
15 ** The <script> must have an id='href-data'. DELAY is the number
16 ** milliseconds delay prior to populating href= and action=. If the
17 ** mouseover boolean is true, then the timer does not start until a
18 ** mouse motion event occurs over top of the document.
19 */
20 function setAllHrefs(){
 
 
 
21 var anchors = document.getElementsByTagName("a");
22 for(var i=0; i<anchors.length; i++){
23 var j = anchors[i];
24 if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
25 }
@@ -26,21 +29,23 @@
26 var forms = document.getElementsByTagName("form");
27 for(var i=0; i<forms.length; i++){
28 var j = forms[i];
29 if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
30 }
 
 
 
 
 
 
31 }
32 function antiRobotDefense(){
33 var x = document.getElementById("href-data");
34 var jx = x.textContent || x.innerText;
35 var g = JSON.parse(jx);
36 var isOperaMini =
37 Object.prototype.toString.call(window.operamini)==="[object OperaMini]";
38 if(g.mouseover && !isOperaMini){
39 document.getElementsByTagName("body")[0].onmousemove=function(){
40 setTimeout(setAllHrefs, g.delay);
41 }
42 }else{
43 setTimeout(setAllHrefs, g.delay);
44 }
45 }
46 antiRobotDefense();
47
--- src/href.js
+++ src/href.js
@@ -12,14 +12,17 @@
12 **
13 ** {"delay":MILLISECONDS, "mouseover":BOOLEAN}
14 **
15 ** The <script> must have an id='href-data'. DELAY is the number
16 ** milliseconds delay prior to populating href= and action=. If the
17 ** mouseover boolean is true, then the href= rewrite is further delayed
18 ** until the first mousedown event that occurs after the timer expires.
19 */
20 var antiRobotOnce = 0;
21 function antiRobotSetAllHrefs(){
22 if( antiRobotOnce ) return;
23 antiRobotOnce = 1;
24 var anchors = document.getElementsByTagName("a");
25 for(var i=0; i<anchors.length; i++){
26 var j = anchors[i];
27 if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
28 }
@@ -26,21 +29,23 @@
29 var forms = document.getElementsByTagName("form");
30 for(var i=0; i<forms.length; i++){
31 var j = forms[i];
32 if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
33 }
34 }
35 function antiRobotSetMouseEventHandler(){
36 document.getElementsByTagName("body")[0].onmousedown=function(){
37 antiRobotSetAllHrefs();
38 document.getElementsByTagName("body")[0].onmousedown=null;
39 }
40 }
41 function antiRobotDefense(){
42 var x = document.getElementById("href-data");
43 var jx = x.textContent || x.innerText;
44 var g = JSON.parse(jx);
45 if( g.mouseover ){
46 setTimeout(antiRobotSetMouseEventHandler, g.delay);
 
 
 
 
47 }else{
48 setTimeout(antiRobotSetAllHrefs, g.delay);
49 }
50 }
51 antiRobotDefense();
52
--- src/security_audit.c
+++ src/security_audit.c
@@ -489,12 +489,12 @@
489489
@ <li>Remove the 'h' privilege from the
490490
@ <a href="%R/setup_uedit?id=%d(nobodyId)">'nobody' user</a> so that
491491
@ robots cannot see hyperlinks.
492492
@ <li>Activate <a href="%R/setup_access#autoh">autohyperlink</a> so that
493493
@ human readers can still see hyperlinks even if they are not logged in.
494
- @ Require mouse movement before enabling hyperlinks and set the
495
- @ delay to at least 50 milliseconds.
494
+ @ Set the delay to at least 50 milliseconds and require a mousedown
495
+ @ event for maximum robot defense.
496496
if( anonId>0 ){
497497
@ <li>Perhaps set the 'h' privilege on the
498498
@ <a href="%R/setup_uedit?id=%d(anonId)">'anonymous' user</a> so
499499
@ that humans that have javascript disabled in their browsers can
500500
@ still see hyperlinks if they will log in as "anonymous".
501501
--- src/security_audit.c
+++ src/security_audit.c
@@ -489,12 +489,12 @@
489 @ <li>Remove the 'h' privilege from the
490 @ <a href="%R/setup_uedit?id=%d(nobodyId)">'nobody' user</a> so that
491 @ robots cannot see hyperlinks.
492 @ <li>Activate <a href="%R/setup_access#autoh">autohyperlink</a> so that
493 @ human readers can still see hyperlinks even if they are not logged in.
494 @ Require mouse movement before enabling hyperlinks and set the
495 @ delay to at least 50 milliseconds.
496 if( anonId>0 ){
497 @ <li>Perhaps set the 'h' privilege on the
498 @ <a href="%R/setup_uedit?id=%d(anonId)">'anonymous' user</a> so
499 @ that humans that have javascript disabled in their browsers can
500 @ still see hyperlinks if they will log in as "anonymous".
501
--- src/security_audit.c
+++ src/security_audit.c
@@ -489,12 +489,12 @@
489 @ <li>Remove the 'h' privilege from the
490 @ <a href="%R/setup_uedit?id=%d(nobodyId)">'nobody' user</a> so that
491 @ robots cannot see hyperlinks.
492 @ <li>Activate <a href="%R/setup_access#autoh">autohyperlink</a> so that
493 @ human readers can still see hyperlinks even if they are not logged in.
494 @ Set the delay to at least 50 milliseconds and require a mousedown
495 @ event for maximum robot defense.
496 if( anonId>0 ){
497 @ <li>Perhaps set the 'h' privilege on the
498 @ <a href="%R/setup_uedit?id=%d(anonId)">'anonymous' user</a> so
499 @ that humans that have javascript disabled in their browsers can
500 @ still see hyperlinks if they will log in as "anonymous".
501
+13 -9
--- src/setup.c
+++ src/setup.c
@@ -472,12 +472,13 @@
472472
@ <ol><li>the User-Agent string in the
473473
@ HTTP header indicates that the request is coming from an actual human
474474
@ being, and
475475
@ <li>the user agent is able to
476476
@ run Javascript in order to set the href= attribute of hyperlinks, and
477
- @ <li>mouse movement is detected (optional - see the checkbox below), and
478
- @ <li>a number of milliseconds have passed since the page loaded.</ol>
477
+ @ <li>a number of milliseconds have passed since the page loaded, and
478
+ @ <li>a mousedown event is detected (optional - see the checkbox below)
479
+ @ </ol>
479480
@
480481
@ <p>This setting is designed to give easy access to humans while
481482
@ keeping out robots and spiders.
482483
@ You do not normally want a robot to walk your entire repository because
483484
@ if it does, your server will end up computing diffs and annotations for
@@ -485,20 +486,23 @@
485486
@ every historical check-in, which can use a lot of CPU and bandwidth
486487
@ even for relatively small projects.</p>
487488
@
488489
@ <p>Additional parameters that control this behavior:</p>
489490
@ <blockquote>
490
- onoff_attribute("Require mouse movement before enabling hyperlinks",
491
- "auto-hyperlink-mouseover", "ahmo", 0, 0);
492
- @ <br />
493491
entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
494492
"auto-hyperlink-delay", "ah-delay", "50", 0);
493
+ @ <br />
494
+ onoff_attribute("Also require a mousedown event before enabling hyperlinks",
495
+ "auto-hyperlink-mouseover", "ahmo", 0, 0);
495496
@ </blockquote>
496
- @ <p>For maximum robot defense, the "require mouse movement" should
497
- @ be turned on and the "Delay" should be at least 50 milliseconds.</p>
498
- @ (Properties: "auto-hyperlink",
499
- @ "auto-hyperlink-mouseover", and "auto-hyperlink-delay")</p>
497
+ @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
498
+ @ and "require a mousedown event" should turned on. To test to see that
499
+ @ this mechanism is working, visit the <a href="%R/test_env">/test_env</a>
500
+ @ page (from a separate web browser that is not logged in, even as
501
+ @ "anonymous") and verify that the "g.javascriptHyperlink" value is "1".</p>
502
+ @ <p>(Properties: "auto-hyperlink", "auto-hyperlink-delay", and
503
+ @ "auto-hyperlink-mouseover"")</p>
500504
501505
@ <hr />
502506
onoff_attribute("Require a CAPTCHA if not logged in",
503507
"require-captcha", "reqcapt", 1, 0);
504508
@ <p>Require a CAPTCHA for edit operations (appending, creating, or
505509
--- src/setup.c
+++ src/setup.c
@@ -472,12 +472,13 @@
472 @ <ol><li>the User-Agent string in the
473 @ HTTP header indicates that the request is coming from an actual human
474 @ being, and
475 @ <li>the user agent is able to
476 @ run Javascript in order to set the href= attribute of hyperlinks, and
477 @ <li>mouse movement is detected (optional - see the checkbox below), and
478 @ <li>a number of milliseconds have passed since the page loaded.</ol>
 
479 @
480 @ <p>This setting is designed to give easy access to humans while
481 @ keeping out robots and spiders.
482 @ You do not normally want a robot to walk your entire repository because
483 @ if it does, your server will end up computing diffs and annotations for
@@ -485,20 +486,23 @@
485 @ every historical check-in, which can use a lot of CPU and bandwidth
486 @ even for relatively small projects.</p>
487 @
488 @ <p>Additional parameters that control this behavior:</p>
489 @ <blockquote>
490 onoff_attribute("Require mouse movement before enabling hyperlinks",
491 "auto-hyperlink-mouseover", "ahmo", 0, 0);
492 @ <br />
493 entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
494 "auto-hyperlink-delay", "ah-delay", "50", 0);
 
 
 
495 @ </blockquote>
496 @ <p>For maximum robot defense, the "require mouse movement" should
497 @ be turned on and the "Delay" should be at least 50 milliseconds.</p>
498 @ (Properties: "auto-hyperlink",
499 @ "auto-hyperlink-mouseover", and "auto-hyperlink-delay")</p>
 
 
 
500
501 @ <hr />
502 onoff_attribute("Require a CAPTCHA if not logged in",
503 "require-captcha", "reqcapt", 1, 0);
504 @ <p>Require a CAPTCHA for edit operations (appending, creating, or
505
--- src/setup.c
+++ src/setup.c
@@ -472,12 +472,13 @@
472 @ <ol><li>the User-Agent string in the
473 @ HTTP header indicates that the request is coming from an actual human
474 @ being, and
475 @ <li>the user agent is able to
476 @ run Javascript in order to set the href= attribute of hyperlinks, and
477 @ <li>a number of milliseconds have passed since the page loaded, and
478 @ <li>a mousedown event is detected (optional - see the checkbox below)
479 @ </ol>
480 @
481 @ <p>This setting is designed to give easy access to humans while
482 @ keeping out robots and spiders.
483 @ You do not normally want a robot to walk your entire repository because
484 @ if it does, your server will end up computing diffs and annotations for
@@ -485,20 +486,23 @@
486 @ every historical check-in, which can use a lot of CPU and bandwidth
487 @ even for relatively small projects.</p>
488 @
489 @ <p>Additional parameters that control this behavior:</p>
490 @ <blockquote>
 
 
 
491 entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
492 "auto-hyperlink-delay", "ah-delay", "50", 0);
493 @ <br />
494 onoff_attribute("Also require a mousedown event before enabling hyperlinks",
495 "auto-hyperlink-mouseover", "ahmo", 0, 0);
496 @ </blockquote>
497 @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
498 @ and "require a mousedown event" should turned on. To test to see that
499 @ this mechanism is working, visit the <a href="%R/test_env">/test_env</a>
500 @ page (from a separate web browser that is not logged in, even as
501 @ "anonymous") and verify that the "g.javascriptHyperlink" value is "1".</p>
502 @ <p>(Properties: "auto-hyperlink", "auto-hyperlink-delay", and
503 @ "auto-hyperlink-mouseover"")</p>
504
505 @ <hr />
506 onoff_attribute("Require a CAPTCHA if not logged in",
507 "require-captcha", "reqcapt", 1, 0);
508 @ <p>Require a CAPTCHA for edit operations (appending, creating, or
509
+8 -6
--- src/style.c
+++ src/style.c
@@ -114,17 +114,18 @@
114114
** g.perm.Hyperlink g.javascriptHyperlink Returned anchor format
115115
** ---------------- --------------------- ------------------------
116116
** 0 0 (empty string)
117117
** 0 1 (empty string)
118118
** 1 0 <a href="URL">
119
-** 1 1 <a id="ID">
119
+** 1 1 <a data-href="URL">
120120
**
121121
** No anchor tag is generated if g.perm.Hyperlink is false.
122122
** The href="URL" form is used if g.javascriptHyperlink is false.
123
-** If g.javascriptHyperlink is true then the id="ID" form is used and
124
-** javascript is generated in the footer to cause href values to be
125
-** inserted after the page has loaded. The use of the id="ID" form
123
+** If g.javascriptHyperlink is true then the data-href="URL" and
124
+** href="/honeypot" is generated and javascript is added to the footer
125
+** to cause data-href values to be inserted into href
126
+** after the page has loaded. The use of the data-href="URL" form
126127
** instead of href="URL" is a defense against bots.
127128
**
128129
** If the user lacks the Hyperlink (h) property and the "auto-hyperlink"
129130
** setting is true, then g.perm.Hyperlink is changed from 0 to 1 and
130131
** g.javascriptHyperlink is set to 1 by login_check_credentials(). Thus
@@ -142,12 +143,12 @@
142143
**
143144
** User has "h" auto-hyperlink Returned anchor format
144145
** ------------ -------------- ----------------------
145146
** 0 0 (empty string)
146147
** 1 0 <a href="URL">
147
-** 0 1 <a id="ID">
148
-** 1 1 (can't happen)
148
+** 0 1 <a data-href="URL">
149
+** 1 1 <a href="URL">
149150
**
150151
** The name of these routines are deliberately kept short so that can be
151152
** easily used within @-lines. Example:
152153
**
153154
** @ %z(href("%R/artifact/%s",zUuid))%h(zFN)</a>
@@ -1375,10 +1376,11 @@
13751376
@ g.zTop = %h(g.zTop)<br />
13761377
@ g.zPath = %h(g.zPath)<br />
13771378
@ g.userUid = %d(g.userUid)<br />
13781379
@ g.zLogin = %h(g.zLogin)<br />
13791380
@ g.isHuman = %d(g.isHuman)<br />
1381
+ @ g.javascriptHyperlink = %d(g.javascriptHyperlink)<br />
13801382
if( g.nRequest ){
13811383
@ g.nRequest = %d(g.nRequest)<br />
13821384
}
13831385
if( g.nPendingRequest>1 ){
13841386
@ g.nPendingRequest = %d(g.nPendingRequest)<br />
13851387
--- src/style.c
+++ src/style.c
@@ -114,17 +114,18 @@
114 ** g.perm.Hyperlink g.javascriptHyperlink Returned anchor format
115 ** ---------------- --------------------- ------------------------
116 ** 0 0 (empty string)
117 ** 0 1 (empty string)
118 ** 1 0 <a href="URL">
119 ** 1 1 <a id="ID">
120 **
121 ** No anchor tag is generated if g.perm.Hyperlink is false.
122 ** The href="URL" form is used if g.javascriptHyperlink is false.
123 ** If g.javascriptHyperlink is true then the id="ID" form is used and
124 ** javascript is generated in the footer to cause href values to be
125 ** inserted after the page has loaded. The use of the id="ID" form
 
126 ** instead of href="URL" is a defense against bots.
127 **
128 ** If the user lacks the Hyperlink (h) property and the "auto-hyperlink"
129 ** setting is true, then g.perm.Hyperlink is changed from 0 to 1 and
130 ** g.javascriptHyperlink is set to 1 by login_check_credentials(). Thus
@@ -142,12 +143,12 @@
142 **
143 ** User has "h" auto-hyperlink Returned anchor format
144 ** ------------ -------------- ----------------------
145 ** 0 0 (empty string)
146 ** 1 0 <a href="URL">
147 ** 0 1 <a id="ID">
148 ** 1 1 (can't happen)
149 **
150 ** The name of these routines are deliberately kept short so that can be
151 ** easily used within @-lines. Example:
152 **
153 ** @ %z(href("%R/artifact/%s",zUuid))%h(zFN)</a>
@@ -1375,10 +1376,11 @@
1375 @ g.zTop = %h(g.zTop)<br />
1376 @ g.zPath = %h(g.zPath)<br />
1377 @ g.userUid = %d(g.userUid)<br />
1378 @ g.zLogin = %h(g.zLogin)<br />
1379 @ g.isHuman = %d(g.isHuman)<br />
 
1380 if( g.nRequest ){
1381 @ g.nRequest = %d(g.nRequest)<br />
1382 }
1383 if( g.nPendingRequest>1 ){
1384 @ g.nPendingRequest = %d(g.nPendingRequest)<br />
1385
--- src/style.c
+++ src/style.c
@@ -114,17 +114,18 @@
114 ** g.perm.Hyperlink g.javascriptHyperlink Returned anchor format
115 ** ---------------- --------------------- ------------------------
116 ** 0 0 (empty string)
117 ** 0 1 (empty string)
118 ** 1 0 <a href="URL">
119 ** 1 1 <a data-href="URL">
120 **
121 ** No anchor tag is generated if g.perm.Hyperlink is false.
122 ** The href="URL" form is used if g.javascriptHyperlink is false.
123 ** If g.javascriptHyperlink is true then the data-href="URL" and
124 ** href="/honeypot" is generated and javascript is added to the footer
125 ** to cause data-href values to be inserted into href
126 ** after the page has loaded. The use of the data-href="URL" form
127 ** instead of href="URL" is a defense against bots.
128 **
129 ** If the user lacks the Hyperlink (h) property and the "auto-hyperlink"
130 ** setting is true, then g.perm.Hyperlink is changed from 0 to 1 and
131 ** g.javascriptHyperlink is set to 1 by login_check_credentials(). Thus
@@ -142,12 +143,12 @@
143 **
144 ** User has "h" auto-hyperlink Returned anchor format
145 ** ------------ -------------- ----------------------
146 ** 0 0 (empty string)
147 ** 1 0 <a href="URL">
148 ** 0 1 <a data-href="URL">
149 ** 1 1 <a href="URL">
150 **
151 ** The name of these routines are deliberately kept short so that can be
152 ** easily used within @-lines. Example:
153 **
154 ** @ %z(href("%R/artifact/%s",zUuid))%h(zFN)</a>
@@ -1375,10 +1376,11 @@
1376 @ g.zTop = %h(g.zTop)<br />
1377 @ g.zPath = %h(g.zPath)<br />
1378 @ g.userUid = %d(g.userUid)<br />
1379 @ g.zLogin = %h(g.zLogin)<br />
1380 @ g.isHuman = %d(g.isHuman)<br />
1381 @ g.javascriptHyperlink = %d(g.javascriptHyperlink)<br />
1382 if( g.nRequest ){
1383 @ g.nRequest = %d(g.nRequest)<br />
1384 }
1385 if( g.nPendingRequest>1 ){
1386 @ g.nPendingRequest = %d(g.nPendingRequest)<br />
1387

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button