📜 ⬆️ ⬇️

We prohibit the use of known UserJS

Introduction


UserJS provides users with a convenient and simple mechanism for modifying web pages, precisely because of this, many users automate their actions using UserJS, and sometimes bypass weak security systems.
Browser-based online games suffer the most from UserJS users, many of whom have already launched a war against UserJS. For example, in the Travian game, fake hidden web forms are used, which sometimes appear along with the usual ones, UserJS scripts written without taking this feature into account are mistaken and send data through the fake form, for which the player immediately receives punishment.
I would like to immediately note that you can only fight with well-known UserJS scripts, the solution shown is not universal and cannot protect against any script.
Today, I am submitting to your court my own method of dealing with user scripts - MD5 hashing followed by comparison. The main acting roles are played by JavaScript and PHP.

Legend


Suppose we have a website with a simple authorization form, but users have got into the habit of not entering their login, but using the UserJS script, which adds an additional button to the form, clicking on which automatically enters their login. This example was taken just for clarity, on-site authorization form may be, for example, the form of sending troops in an online strategy, and UserJS-script - bot, automatically sending troops.

Initial data


So, our form looks like this:


Users use the following UserJS in Opera browser:
Copy Source | Copy HTML window.opera.addEventListener( 'AfterEvent.load' , function (e){ if (e. event .target instanceof Document){ e.preventDefault(); document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ; } }, false );
  1. Copy Source | Copy HTML window.opera.addEventListener( 'AfterEvent.load' , function (e){ if (e. event .target instanceof Document){ e.preventDefault(); document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ; } }, false );
  2. Copy Source | Copy HTML window.opera.addEventListener( 'AfterEvent.load' , function (e){ if (e. event .target instanceof Document){ e.preventDefault(); document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ; } }, false );
  3. Copy Source | Copy HTML window.opera.addEventListener( 'AfterEvent.load' , function (e){ if (e. event .target instanceof Document){ e.preventDefault(); document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ; } }, false );
  4. Copy Source | Copy HTML window.opera.addEventListener( 'AfterEvent.load' , function (e){ if (e. event .target instanceof Document){ e.preventDefault(); document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ; } }, false );
  5. Copy Source | Copy HTML window.opera.addEventListener( 'AfterEvent.load' , function (e){ if (e. event .target instanceof Document){ e.preventDefault(); document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ; } }, false );
  6. Copy Source | Copy HTML window.opera.addEventListener( 'AfterEvent.load' , function (e){ if (e. event .target instanceof Document){ e.preventDefault(); document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ; } }, false );

')
Or in FireFox for the GreaceMonkey extension:
Copy Source | Copy HTML // ==UserScript== // @name test1 // @namespace test // @include http://localhost/anti-userjs/test1.php // ==/UserScript== document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ;
  1. Copy Source | Copy HTML // ==UserScript== // @name test1 // @namespace test // @include http://localhost/anti-userjs/test1.php // ==/UserScript== document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ;
  2. Copy Source | Copy HTML // ==UserScript== // @name test1 // @namespace test // @include http://localhost/anti-userjs/test1.php // ==/UserScript== document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ;
  3. Copy Source | Copy HTML // ==UserScript== // @name test1 // @namespace test // @include http://localhost/anti-userjs/test1.php // ==/UserScript== document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ;
  4. Copy Source | Copy HTML // ==UserScript== // @name test1 // @namespace test // @include http://localhost/anti-userjs/test1.php // ==/UserScript== document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ;
  5. Copy Source | Copy HTML // ==UserScript== // @name test1 // @namespace test // @include http://localhost/anti-userjs/test1.php // ==/UserScript== document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ;
  6. Copy Source | Copy HTML // ==UserScript== // @name test1 // @namespace test // @include http://localhost/anti-userjs/test1.php // ==/UserScript== document.forms[ 0 ].innerHTML+= '<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">' ;


After enabling this script, users receive this type of authorization form:


Clicking on the “AutoLogin” button, as seen from the source code, results in filling in the “login” field.
Let's try to prohibit the user to use this script.

Displays authorization form


We will display the authorization form from a PHP file, frame it with a div tag, and also cache all the data inside this tag using PHP. Hash will be saved in the session for follow-up:
Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  1. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  2. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  3. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  4. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  5. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  6. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  7. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  8. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  9. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  10. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  11. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  12. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  13. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  14. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  15. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  16. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  17. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>
  18. Copy Source | Copy HTML <div id= "main" > <? ob_start(); ?> <form method= "POST" > : <input name= "login" type= "text" ><br> : <input name= "password" type= "password" ><br> <input name= "md5" type= "hidden" ><br> <input value= "" type= "submit" > </form> <? $data =ob_get_contents(); ob_end_flush(); $data =strtr( $data , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); $data =preg_replace( "/[ \t\n\r]/" , '' , $data ); $md5 =md5( $data ); $_SESSION [ 'md5' ]= $md5 ; ?> </div>


A couple of words about the processing of content - Opera and FireFox browsers in their own way are izglyayutsya over HTML-code, so processing it through JavaScript leads to different results. So, Opera brings all HTML tags to uppercase letters, and FireFox changes parameters in tags according to the standard. Therefore, it is important to write valid HTML-controlled code.
To eliminate various processing of the register of tags and the transfer of lines, all spaces, tabs and line breaks are removed from the contents, as well as all English letters are given in lower case.
Another important note is that the document encoding must be UTF-8, with CP1251 JavaScript in browsers and PHP I work differently.

JavaScript control


So, the form is displayed, its checksum is counted, now you need to calculate it from the user’s side and make sure that UserJS didn’t intervene. To do this, we add an event to onLoad and call the JS function:
Copy Source | Copy HTML > function checkuserjs(){ var data=document.getElementsByTagName( 'div' )[ 0 ].innerHTML; data=strtr(data, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); data=data.replace(/[ \t\n\r]/g, '' ); document.forms[ 0 ].md5.value=md5(data); }

  1. Copy Source | Copy HTML > function checkuserjs(){ var data=document.getElementsByTagName( 'div' )[ 0 ].innerHTML; data=strtr(data, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); data=data.replace(/[ \t\n\r]/g, '' ); document.forms[ 0 ].md5.value=md5(data); }

  2. Copy Source | Copy HTML > function checkuserjs(){ var data=document.getElementsByTagName( 'div' )[ 0 ].innerHTML; data=strtr(data, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); data=data.replace(/[ \t\n\r]/g, '' ); document.forms[ 0 ].md5.value=md5(data); }

  3. Copy Source | Copy HTML > function checkuserjs(){ var data=document.getElementsByTagName( 'div' )[ 0 ].innerHTML; data=strtr(data, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); data=data.replace(/[ \t\n\r]/g, '' ); document.forms[ 0 ].md5.value=md5(data); }

  4. Copy Source | Copy HTML > function checkuserjs(){ var data=document.getElementsByTagName( 'div' )[ 0 ].innerHTML; data=strtr(data, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); data=data.replace(/[ \t\n\r]/g, '' ); document.forms[ 0 ].md5.value=md5(data); }

  5. Copy Source | Copy HTML > function checkuserjs(){ var data=document.getElementsByTagName( 'div' )[ 0 ].innerHTML; data=strtr(data, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); data=data.replace(/[ \t\n\r]/g, '' ); document.forms[ 0 ].md5.value=md5(data); }

  6. Copy Source | Copy HTML > function checkuserjs(){ var data=document.getElementsByTagName( 'div' )[ 0 ].innerHTML; data=strtr(data, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); data=data.replace(/[ \t\n\r]/g, '' ); document.forms[ 0 ].md5.value=md5(data); }

  7. Copy Source | Copy HTML > function checkuserjs(){ var data=document.getElementsByTagName( 'div' )[ 0 ].innerHTML; data=strtr(data, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); data=data.replace(/[ \t\n\r]/g, '' ); document.forms[ 0 ].md5.value=md5(data); }

Copy Source | Copy HTML > function checkuserjs(){ var data=document.getElementsByTagName( 'div' )[ 0 ].innerHTML; data=strtr(data, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' , 'abcdefghijklmnopqrstuvwxyz' ); data=data.replace(/[ \t\n\r]/g, '' ); document.forms[ 0 ].md5.value=md5(data); }



In it, I used several ready-made functions - md5 and strtr, I will not post them in the article, you can find them in the attached file.
On the user side, Java Script performs the same actions as the PHP script above, the result — the content hash — is entered into the form.

Server side processing


The case remains for small - check the data:
Copy Source | Copy HTML if ( $_POST ){ if ( $_POST [ 'md5' ]!= $_SESSION [ 'md5' ]) die ( ' UserJS !' ); echo ' ' ; }
  1. Copy Source | Copy HTML if ( $_POST ){ if ( $_POST [ 'md5' ]!= $_SESSION [ 'md5' ]) die ( ' UserJS !' ); echo ' ' ; }
  2. Copy Source | Copy HTML if ( $_POST ){ if ( $_POST [ 'md5' ]!= $_SESSION [ 'md5' ]) die ( ' UserJS !' ); echo ' ' ; }
  3. Copy Source | Copy HTML if ( $_POST ){ if ( $_POST [ 'md5' ]!= $_SESSION [ 'md5' ]) die ( ' UserJS !' ); echo ' ' ; }
  4. Copy Source | Copy HTML if ( $_POST ){ if ( $_POST [ 'md5' ]!= $_SESSION [ 'md5' ]) die ( ' UserJS !' ); echo ' ' ; }
  5. Copy Source | Copy HTML if ( $_POST ){ if ( $_POST [ 'md5' ]!= $_SESSION [ 'md5' ]) die ( ' UserJS !' ); echo ' ' ; }


Thus, we catch unscrupulous users (note the GreaseMonkey indicator):


Conclusion


The example I mentioned demonstrates the very approach to prohibiting the use of UserJS, when developing solutions based on it, you need to understand the main point - it is not possible to prohibit the use of any UserJS, therefore you need to monitor the laid out scripts that affect our sites and develop protection against them. Since such scripts are being made with enthusiasm, there is a great chance that a new version of the script, bypassing our protection, if it comes out, will not be soon. The same Travian demonstrates this - the simplest protection with fake forms was introduced half a year ago, there is still no new version of UserJS scripts.

Application: anti-userjs.tar.gz 3708 bytes

Source: https://habr.com/ru/post/107396/


All Articles