📜 ⬆️ ⬇️

Nginx module to combat DDoS, put a cookie through Flash

After publishing an article about the nginx module designed to combat bots, I received a lot of feedback in which people asked for Flash support.
I was sure that with due effort anyone could implement these functions on their own, as third-party applications, without changing the code of the module itself, but no one did, so I had to do PoC.

Initially, I wanted the module to be in the form of a designer, and no one has canceled the KISS principle, so it was decided to implement the entire client-side in the form of third-party applications. Nginx is doing well with proxying, so the easiest way is to write them as separate HTTP services.
This provides the following benefits:


What had to add


For the main directive of the testcookie-filter module , besides the previous values ​​of “on” and “off”, now another one has been added - “var”. If the module works in the “var” mode, then for this location:


What is it for


Thus, you can now pass the correct cookie value to your application via the HTTP header:
location = /testcookie.swf { testcookie var; proxy_pass http://127.0.0.1:1234/; proxy_set_header Testcookie-Value $testcookie_set; proxy_set_header Testcookie-Valid $testcookie_ok; proxy_set_header Testcookie-Name "BPC"; } 

Why through the header, and not through param in the case of Flash?
So the value does not appear in clear form in the client part, it can not be extracted, for example, by regexp.
')

And yet, Flash?


I believe that the method of installing cookies via Flash is not the most correct, but since users want it - why not do it.
I dismissed the idea to make a static SWF and substitute values ​​into it, like it was done in Roboo , right away - easy to disassemble, difficult to modify, and so on.
Therefore, it was decided to give the user the opportunity to make life harder for the attacker.
For this, there are 2 files in the project:
cookie_encoder.py (Python):
 def encode_cookie(cookie_value): key = 42 res = '' for x in cookie_value: res += chr(ord(x) ^ key) return res 

cookie_decoder.as (ActionScript):
 function flash_cookie_crypt_routine(str) { var result; for (var i = 0; i < str.length; i++) { result += String.fromCharCode(str.charCodeAt(i) ^ 42); } return result; } getURL("javascript:void(document.cookie='#TESTCOOKIE_NAME#=" + flash_cookie_crypt_routine("#TESTCOOKIE_VALUE#") + "');void(location.href='" + nexturl + "');"); 

As it is not difficult to guess, the first one is for encoding the value (in the example xory is 42), the second is for decoding on the client side (again xory is 42).

ActionScript is dynamically built into SWF using libming . By the way, I had to patch it up a bit, so the project will work only with my fork, at least until the maintainers stop finding fault with the names of the functions and approve of my pull.

The service framework uses fapws3 , a fast asynchronous web server for Python.

All together on the old coreduo, in one process, gives its ~ 3k req / s with concurency 1k, without any optimization and caching. In production, you can use the nginx proxy cache, so the SWF will be generated for each client only once, distributed by nginx and the service itself will be quite difficult to put.

Together


Configuration example:
 server { listen 80; server_name domain.com; testcookie off; testcookie_name BPC; testcookie_secret keepmescret; testcookie_session $remote_addr; testcookie_arg attempt; testcookie_max_attempts 3; testcookie_fallback /cookies.html?backurl=http://$host$request_uri; testcookie_get_only on; testcookie_redirect_via_refresh on; 

# connect testcookie.swf via swfobject (yes, I'm lazy)
# use testcookie_refresh_template directive,
# pass the value of $ testcookie_nexturl through param
  testcookie_refresh_template '<html><body><script type="text/javascript" src="/swfobject.js"></script><script type="text/javascript">swfobject.embedSWF("/testcookie.swf", "cookie_installer", "100", "100", "9.0.0", "/expressInstall.swf", {"nexturl":"$testcookie_nexturl"});</script><div id="cookie_installer">welcome screen</div></body></html>'; 

# disable cookies
# for fallback URL, swfobject.js and expressInstall.swf
  location = /cookies.html { root /var/www/public_html; } location = /swfobject.js { gzip on; gzip_min_length 1000; gzip_types text/plain; root /usr/local/nginx/root; } location = /expressInstall.swf { testcookie off; gzip on; gzip_min_length 1000; gzip_types text/plain; root /usr/local/nginx/root; } 

# by this location the service dynamically generates SWF
  location = /testcookie.swf { #  testcookie   var testcookie var; #  testcookie-flash-processor proxy_pass http://127.0.0.1:1234/; #    cookies proxy_set_header Testcookie-Value $testcookie_set; #    ? #    SWF  , #      cookie proxy_set_header Testcookie-Valid $testcookie_ok; #  cookie -     #   testcookie_name proxy_set_header Testcookie-Name "BPC"; } 

# basic location with backend address
  location / { testcookie on; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://127.0.0.1:8080; } } 

Captcha is screwed in the same way.

Source texts


Source texts with installation instructions and documentation are available on github under the BSD license.
Patches, add-ons, tests and bug reports are welcome.

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


All Articles