comparison web/secure.py @ 293:d15dda1b1f76

merge
author Matt Johnston <matt@ucc.asn.au>
date Sat, 06 Jul 2019 18:29:45 +0800
parents f7261dd970da
children a99631597f65
comparison
equal deleted inserted replaced
292:28eb733cb803 293:d15dda1b1f76
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 = open('%s/csrf.dat' % config.DATA_PATH, 'r+') 51 _csrf_fd = os.fdopen(os.open('%s/csrf.dat' % config.DATA_PATH, os.O_RDWR | os.O_CREAT, 0600), 'r+')
60 52
61 try: 53 try:
62 fcntl.lockf(_csrf_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) 54 fcntl.lockf(_csrf_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
63 os.fchmod(_csrf_fd.fileno(), 0600)
64 _csrf_fd.write("%d-%s" % (os.getpid(), binascii.hexlify(os.urandom(NONCE_SIZE)))) 55 _csrf_fd.write("%d-%s" % (os.getpid(), binascii.hexlify(os.urandom(NONCE_SIZE))))
65 _csrf_fd.flush() 56 _csrf_fd.flush()
66 _csrf_fd.seek(0) 57 _csrf_fd.seek(0)
67 except IOError: 58 except IOError:
68 pass 59 pass
71 # keep the lock open until we go away 62 # keep the lock open until we go away
72 63
73 64
74 def get_csrf_blob(): 65 def get_csrf_blob():
75 expiry = int(config.CSRF_TIMEOUT + time.time()) 66 expiry = int(config.CSRF_TIMEOUT + time.time())
76 content = '%s-%s' % (get_user_hash(), expiry) 67 content = '%s-%s' % (init_cookie(), expiry)
77 mac = hmac.new(_csrf_key, content).hexdigest() 68 mac = hmac.new(_csrf_key, content).hexdigest()
78 return "%s-%s" % (content, mac) 69 return "%s-%s" % (content, mac)
79 70
80 def check_csrf_blob(blob): 71 def check_csrf_blob(blob):
81 toks = blob.split('-') 72 toks = blob.split('-')
82 if len(toks) != 3: 73 if len(toks) != 3:
83 print>>sys.stderr, "wrong toks" 74 print>>sys.stderr, "wrong toks"
84 return False 75 return False
85 76
86 user, expiry, mac = toks 77 user, expiry, mac = toks
87 if user != get_user_hash(): 78 if user != init_cookie():
88 print>>sys.stderr, "wrong user" 79 print>>sys.stderr, "wrong user"
89 return False 80 return False
90 81
91 try: 82 try:
92 exp = int(expiry) 83 exp = int(expiry)