|
116 | 116 |
|
117 | 117 | ### 6. Timing Attack in Login - User Enumeration |
118 | 118 |
|
| 119 | +> [!NOTE] |
| 120 | +> ✅ **Done**: Login now uses a precomputed dummy hash to ensure password |
| 121 | +> verification always occurs, maintaining consistent response times regardless |
| 122 | +> of user existence. |
| 123 | +
|
119 | 124 | **Location**: `app/managers/user.py:172-199` |
120 | 125 |
|
121 | 126 | - **Issue**: Different execution paths leak information through timing: |
|
136 | 141 |
|
137 | 142 | ### 7. Timing Attack in Token Type Checking |
138 | 143 |
|
| 144 | +> [!NOTE] |
| 145 | +> ✅ **Done**: Token type comparisons now use `secrets.compare_digest()` for |
| 146 | +> constant-time comparison. Note: The timing difference (nanoseconds) is |
| 147 | +> negligible compared to network jitter (milliseconds), making this purely |
| 148 | +> defense-in-depth rather than addressing a realistic threat vector. |
| 149 | +
|
139 | 150 | **Location**: `app/managers/auth.py:191, 265, 380` |
140 | 151 |
|
141 | 152 | - **Issue**: String comparison `if payload["typ"] != "refresh"` may be vulnerable |
|
208 | 219 |
|
209 | 220 | ### 12. KeyError Throws 500 Instead of 401 |
210 | 221 |
|
| 222 | +> [!NOTE] |
| 223 | +> ✅ **Done**: All JWT claim accesses now use `payload.get()` with explicit |
| 224 | +> None checks in all token validation flows (refresh, verify, reset, |
| 225 | +> get_jwt_user). Malformed tokens missing 'sub' or 'typ' claims now properly |
| 226 | +> return 401 Unauthorized instead of 500 Internal Server Error. |
| 227 | +
|
211 | 228 | **Location**: `app/managers/auth.py:191, 265, 380` |
212 | 229 |
|
213 | 230 | - **Issue**: `payload["typ"]` / `payload["sub"]` are accessed directly in |
|
446 | 463 |
|
447 | 464 | ### 30. Timing Attack in API Key Validation |
448 | 465 |
|
| 466 | +> [!NOTE] |
| 467 | +> **Not fixing**: The prefix (`"fta_"`) is public, documented information that |
| 468 | +> provides no security value. Timing differences (nanoseconds) are completely |
| 469 | +> buried by network jitter. The actual secret (32-byte random key) is compared |
| 470 | +> via HMAC hash lookup in the database. No security benefit from constant-time |
| 471 | +> prefix comparison. |
| 472 | +
|
449 | 473 | **Location**: `app/managers/api_key.py:136-137` |
450 | 474 |
|
451 | 475 | - **Issue**: String prefix comparison `if not raw_key.startswith(cls.KEY_PREFIX)` |
|
568 | 592 | | Priority | Count | Must Fix Before Production? | |
569 | 593 | |--------------|---------------|-------------------------------------| |
570 | 594 | | **CRITICAL** | 5 (4 closed) | ✅ YES - Security vulnerabilities | |
571 | | -| **High** | 9 (0 closed) | ✅ YES - Important security/quality | |
| 595 | +| **High** | 9 (3 closed) | ✅ YES - Important security/quality | |
572 | 596 | | **Medium** | 14 (0 closed) | ⚠️ Recommended - Hardening needed | |
573 | 597 | | **Low** | 5 (0 closed) | 💡 Optional - Nice to have | |
574 | 598 |
|
@@ -600,12 +624,12 @@ rate limiting, token validation, and API key scope enforcement. |
600 | 624 |
|
601 | 625 | ### Sprint 2 - High Priority (Next Week) |
602 | 626 |
|
603 | | -1. **Fix timing attacks** (#6, #7) - Login and token validation |
| 627 | +1. ✅ **Fix timing attacks** (#6, #7) - Login and token validation |
604 | 628 | 2. **Implement token revocation** (#8) - Add jti claims + blacklist |
605 | 629 | 3. **Add database index** (#16) - `api_key.user_id` |
606 | 630 | 4. **Refactor token encoding** (#15) - Remove code duplication |
607 | 631 | 5. **Fix password reset reuse** (#9) - One-time tokens |
608 | | -6. **Add KeyError protection** (#12) - Use `payload.get()` |
| 632 | +6. ✅ **Add KeyError protection** (#12) - Use `payload.get()` |
609 | 633 | 7. **Add JWT format guards** (#13) - to `get_jwt_user()` |
610 | 634 |
|
611 | 635 | ### Sprint 3 - Hardening (Next 2 Weeks) |
|
0 commit comments