|
56 | 56 | TURNSTILE_SECRET_KEY = os.environ.get('TURNSTILE_SECRET_KEY', '') |
57 | 57 | TURNSTILE_SITE_KEY = os.environ.get('TURNSTILE_SITE_KEY', '') |
58 | 58 |
|
| 59 | +def verify_turnstile(token, remote_ip, context=''): |
| 60 | + """共用 Turnstile 驗證函式。回傳 (success: bool, error_response or None)。""" |
| 61 | + if not TURNSTILE_SECRET_KEY: |
| 62 | + return True, None |
| 63 | + if not token: |
| 64 | + return False, (jsonify({'success': False, 'message': '請完成人機驗證'}), 400) |
| 65 | + try: |
| 66 | + verify_res = http_requests.post( |
| 67 | + 'https://challenges.cloudflare.com/turnstile/v0/siteverify', |
| 68 | + data={ |
| 69 | + 'secret': TURNSTILE_SECRET_KEY, |
| 70 | + 'response': token, |
| 71 | + 'remoteip': remote_ip |
| 72 | + }, |
| 73 | + timeout=10 |
| 74 | + ) |
| 75 | + verify_data = verify_res.json() |
| 76 | + if not verify_data.get('success'): |
| 77 | + logger.warning(f"Turnstile verification failed ({context}): {verify_data}") |
| 78 | + return False, (jsonify({'success': False, 'message': '人機驗證失敗,請重試'}), 403) |
| 79 | + except Exception as e: |
| 80 | + logger.error(f"Turnstile verification error ({context}): {e}") |
| 81 | + return False, (jsonify({'success': False, 'message': '驗證服務暫時無法使用,請稍後再試'}), 500) |
| 82 | + return True, None |
| 83 | + |
59 | 84 |
|
60 | 85 | if not os.path.exists(SHARED_FOLDER): |
61 | 86 | os.makedirs(SHARED_FOLDER) |
@@ -129,26 +154,9 @@ def login(): |
129 | 154 |
|
130 | 155 | # Turnstile 驗證 |
131 | 156 | turnstile_token = data.get('cf-turnstile-response', '') |
132 | | - if TURNSTILE_SECRET_KEY: |
133 | | - if not turnstile_token: |
134 | | - return jsonify({'success': False, 'message': '請完成人機驗證'}), 400 |
135 | | - try: |
136 | | - verify_res = http_requests.post( |
137 | | - 'https://challenges.cloudflare.com/turnstile/v0/siteverify', |
138 | | - data={ |
139 | | - 'secret': TURNSTILE_SECRET_KEY, |
140 | | - 'response': turnstile_token, |
141 | | - 'remoteip': request.remote_addr |
142 | | - }, |
143 | | - timeout=10 |
144 | | - ) |
145 | | - verify_data = verify_res.json() |
146 | | - if not verify_data.get('success'): |
147 | | - logger.warning(f"Turnstile verification failed for user {username}: {verify_data}") |
148 | | - return jsonify({'success': False, 'message': '人機驗證失敗,請重試'}), 403 |
149 | | - except Exception as e: |
150 | | - logger.error(f"Turnstile verification error: {e}") |
151 | | - return jsonify({'success': False, 'message': '驗證服務暫時無法使用,請稍後再試'}), 500 |
| 157 | + ok, err = verify_turnstile(turnstile_token, request.remote_addr, context=f'login:{username}') |
| 158 | + if not ok: |
| 159 | + return err |
152 | 160 |
|
153 | 161 | try: |
154 | 162 | success, message, cookies, student_no, token = GradeFetcher.login_and_get_tokens(username, data.get('password')) |
@@ -262,6 +270,12 @@ def create_share_link(): |
262 | 270 | if not data: |
263 | 271 | return jsonify({'error': 'No data provided'}), 400 |
264 | 272 |
|
| 273 | + # Turnstile 驗證 |
| 274 | + turnstile_token = data.pop('cf-turnstile-response', '') |
| 275 | + ok, err = verify_turnstile(turnstile_token, request.remote_addr, context='share') |
| 276 | + if not ok: |
| 277 | + return err |
| 278 | + |
265 | 279 | # Generate 15-char random URL-safe ID with special chars |
266 | 280 | # Using A-Z, a-z, 0-9, -, _, ., ~ |
267 | 281 | chars = string.ascii_letters + string.digits + "-_.~" |
|
0 commit comments