├── requirements.txt ├── start.sh ├── gunicorn.conf.py ├── settings.py ├── .gitignore ├── app.py ├── README.md └── README /requirements.txt: -------------------------------------------------------------------------------- 1 | geventhttpclient 2 | gunicorn 3 | flask 4 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | gunicorn -c gunicorn.conf.py app:app 4 | -------------------------------------------------------------------------------- /gunicorn.conf.py: -------------------------------------------------------------------------------- 1 | bind = "127.0.0.1:10101" 2 | workers = 10 3 | backlog = 2048 4 | #pidfile = "/home/recaptcher/run/server.pid" 5 | preload_app = True 6 | -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | DEBUG=False 2 | RE_SECRETS = { 'example-domain1.com': 'per_domain_recaptcha_secret_key1', 3 | 'example-domain2.com': 'per_domain_recaptcha_secret_key2' } 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import json 5 | from geventhttpclient import HTTPClient, URL 6 | from flask import Flask, request, redirect, abort 7 | 8 | import settings 9 | 10 | app = Flask(__name__) 11 | 12 | def get_client_addr(): 13 | if not request.headers.getlist("X-Real-IP"): 14 | ip = request.remote_addr 15 | else: 16 | ip = request.headers.getlist("X-Real-IP")[0] 17 | return ip 18 | 19 | def check_recaptcha(secret, resp, ip): 20 | try: 21 | url = URL('https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s&ip=%s' % (secret, resp, ip)) 22 | http = HTTPClient.from_url(url) 23 | response = http.get(url.request_uri) 24 | if response.status_code == 200: 25 | raw_res = response.read() 26 | res = json.loads(raw_res) 27 | if res.get('success'): 28 | return True 29 | except: 30 | pass 31 | return False 32 | 33 | @app.route('/', methods=['POST']) 34 | def handler(): 35 | domain = request.headers.get('Testcookie-Domain', '') 36 | nexturl = request.headers.get('Testcookie-Nexturl', '/') 37 | cookie_name = request.headers.get('Testcookie-Name') 38 | cookie_val = request.headers.get('Testcookie-Value') 39 | secret = settings.RE_SECRETS.get(domain) 40 | if not cookie_name or not cookie_val or not secret: 41 | abort(500) 42 | ip = get_client_addr() 43 | if check_recaptcha(secret, request.form['g-recaptcha-response'], ip): 44 | resp = redirect(nexturl) 45 | resp.set_cookie(cookie_name, cookie_val) 46 | return resp 47 | return redirect(nexturl) 48 | 49 | if __name__ == '__main__': 50 | import logging 51 | 52 | logging.basicConfig(level=logging.DEBUG) 53 | 54 | app.debug = True 55 | 56 | app.run('localhost', 10101) 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Description 2 | =========== 3 | 4 | **testcookie-recaptcha-processor** is a simple web server proxying recaptcha responses to Google. 5 | Server should be used with [testcookie-nginx-module](http://github.com/kyprizel/testcookie-nginx-module) for setting cookies after solving recaptchas. 6 | 7 | Note 8 | ==== 9 | 10 | This project is Proof-of-Contept for those people, who ask me to add "Captcha functionality" to testcookie-nginx-module. 11 | 12 | Installation 13 | ============ 14 | 15 | Grab and install libraries: 16 | *) pip install -r requirements.txt 17 | *) start.sh 18 | 19 | Build nginx with testcookie-nginx-module, use example configuration. 20 | Run testcookie-recaptcha-processor, run nginx. 21 | 22 | 23 | Example configuration 24 | ===================== 25 | 26 | server { 27 | listen 80; 28 | server_name domain.com; 29 | 30 | testcookie off; 31 | testcookie_name BPC; 32 | testcookie_secret keepmescret; 33 | testcookie_session $remote_addr; 34 | testcookie_arg attempt; 35 | testcookie_max_attempts 3; 36 | testcookie_fallback /cookies.html?backurl=http://$host$request_uri; 37 | testcookie_get_only on; 38 | testcookie_redirect_via_refresh on; 39 | testcookie_refresh_template "
"; 40 | 41 | location = /captcha { 42 | testcookie var; 43 | proxy_set_header Testcookie-Domain "domain.com"; 44 | proxy_set_header Testcookie-Value $testcookie_set; 45 | proxy_set_header Testcookie-Nexturl $testcookie_nexturl; 46 | proxy_set_header Testcookie-Name "BPC"; 47 | proxy_set_header X-Real-IP $remote_addr; 48 | proxy_pass http://127.0.0.1:10101/; 49 | } 50 | 51 | location / { 52 | testcookie on; 53 | proxy_set_header Host $host; 54 | proxy_set_header X-Real-IP $remote_addr; 55 | proxy_pass http://127.0.0.1:8080; 56 | } 57 | 58 | location = /cookies.html { 59 | root /var/www/public_html; 60 | } 61 | 62 | } 63 | 64 | 65 | Sources 66 | ======= 67 | 68 | Available on github at [kyprizel/testcookie-recaptcha-processor] 69 | (