Mercurial > templog
comparison web/secure.py @ 301:47c259458160
merge
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sat, 06 Jul 2019 19:17:21 +0800 |
parents | f7261dd970da |
children | a99631597f65 |
comparison
equal
deleted
inserted
replaced
300:65a6b56fd18a | 301:47c259458160 |
---|---|
9 | 9 |
10 import bottle | 10 import bottle |
11 | 11 |
12 import config | 12 import config |
13 | 13 |
14 __all__ = ["get_csrf_blob", "check_csrf_blob", "setup_csrf", "get_user_hash", | 14 __all__ = [ |
15 "check_user_hash"] | 15 "get_csrf_blob", |
16 "check_csrf_blob", | |
17 "setup_csrf", | |
18 "check_cookie", | |
19 "init_cookie", | |
20 ] | |
21 | |
22 AUTH_COOKIE = 'templogauth' | |
23 AUTH_COOKIE_LEN = 16 | |
16 | 24 |
17 HASH=hashlib.sha1 | 25 HASH=hashlib.sha1 |
18 | 26 |
19 CLEAN_RE = re.compile('[^a-z0-9A-Z]') | 27 CLEAN_RE = re.compile('[^a-z0-9A-Z]') |
20 | 28 |
21 def clean_hash(h): | 29 def cookie_hash(c): |
22 return CLEAN_RE.sub('', h.lower()) | 30 return hashlib.sha256(c).hexdigest() |
23 | 31 |
24 def get_user_hash(): | 32 def init_cookie(): |
33 """ Generates a new httponly auth cookie if required. | |
34 Returns the hash of the cookie (new or existing) | |
25 """ | 35 """ |
26 Uses the following apache config. | 36 c = bottle.request.get_cookie(AUTH_COOKIE) |
27 Needs a separate port or IP to no-certificate SSL, SNI isn't good enough. | 37 if not c: |
38 c = binascii.hexlify(os.urandom(AUTH_COOKIE_LEN)) | |
39 bottle.response.set_cookie(AUTH_COOKIE, c, secure=True, httponly=True) | |
40 return cookie_hash(c) | |
28 | 41 |
29 <location /~matt/templog/set> | 42 def check_cookie(allowed_users): |
30 Require all granted | 43 c = bottle.request.get_cookie(AUTH_COOKIE) |
31 SSLVerifyClient optional_no_ca | 44 if not c: |
32 SSLVerifyDepth 1 | 45 return False |
33 SSLOptions +StdEnvVars +ExportCertData +OptRenegotiate | 46 return cookie_hash(c) in allowed_users |
34 </location> | |
35 """ | |
36 | |
37 verify = bottle.request.environ.get('SSL_CLIENT_VERIFY', '') | |
38 if not (verify == 'GENEROUS' or verify == 'SUCCESS'): | |
39 return 'FAILVERIFY' | |
40 blob = bottle.request.environ.get('SSL_CLIENT_CERT') | |
41 if not blob: | |
42 return 'NOCERT' | |
43 | |
44 b64 = ''.join(l for l in blob.split('\n') | |
45 if not l.startswith('-')) | |
46 | |
47 return HASH(binascii.a2b_base64(b64)).hexdigest() | |
48 | |
49 def check_user_hash(allowed_users): | |
50 current_hash = clean_hash(get_user_hash()) | |
51 for a in allowed_users: | |
52 if current_hash == clean_hash(a): | |
53 return True | |
54 return False | |
55 | 47 |
56 def setup_csrf(): | 48 def setup_csrf(): |
57 NONCE_SIZE=16 | 49 NONCE_SIZE=16 |
58 global _csrf_fd, _csrf_key | 50 global _csrf_fd, _csrf_key |
59 _csrf_fd = os.fdopen(os.open('%s/csrf.dat' % config.DATA_PATH, os.O_RDWR | os.O_CREAT, 0600), 'r+') | 51 _csrf_fd = os.fdopen(os.open('%s/csrf.dat' % config.DATA_PATH, os.O_RDWR | os.O_CREAT, 0600), 'r+') |
70 # keep the lock open until we go away | 62 # keep the lock open until we go away |
71 | 63 |
72 | 64 |
73 def get_csrf_blob(): | 65 def get_csrf_blob(): |
74 expiry = int(config.CSRF_TIMEOUT + time.time()) | 66 expiry = int(config.CSRF_TIMEOUT + time.time()) |
75 content = '%s-%s' % (get_user_hash(), expiry) | 67 content = '%s-%s' % (init_cookie(), expiry) |
76 mac = hmac.new(_csrf_key, content).hexdigest() | 68 mac = hmac.new(_csrf_key, content).hexdigest() |
77 return "%s-%s" % (content, mac) | 69 return "%s-%s" % (content, mac) |
78 | 70 |
79 def check_csrf_blob(blob): | 71 def check_csrf_blob(blob): |
80 toks = blob.split('-') | 72 toks = blob.split('-') |
81 if len(toks) != 3: | 73 if len(toks) != 3: |
82 print>>sys.stderr, "wrong toks" | 74 print>>sys.stderr, "wrong toks" |
83 return False | 75 return False |
84 | 76 |
85 user, expiry, mac = toks | 77 user, expiry, mac = toks |
86 if user != get_user_hash(): | 78 if user != init_cookie(): |
87 print>>sys.stderr, "wrong user" | 79 print>>sys.stderr, "wrong user" |
88 return False | 80 return False |
89 | 81 |
90 try: | 82 try: |
91 exp = int(expiry) | 83 exp = int(expiry) |