1
1
from __future__ import annotations
2
2
3
+ import functools
3
4
import hashlib
5
+ import hmac
4
6
import json
5
7
import logging
6
8
import os .path
12
14
from io import BytesIO
13
15
from itertools import islice
14
16
from typing import ClassVar
17
+ from typing import Concatenate
15
18
16
19
import tornado .escape
17
20
import tornado .web
@@ -206,7 +209,54 @@ class APIError(tornado.web.HTTPError):
206
209
pass
207
210
208
211
209
- class RequestHandler (tornado .web .RequestHandler ):
212
+ class AuthRequestHandler (tornado .web .RequestHandler ):
213
+ AUTH_COOKIE_NAME = "mitmproxy-auth"
214
+ AUTH_COOKIE_VALUE = b"y"
215
+
216
+ def __init_subclass__ (cls , ** kwargs ):
217
+ """Automatically wrap all request handlers with `_require_auth`."""
218
+ for method in cls .SUPPORTED_METHODS :
219
+ method = method .lower ()
220
+ fn = getattr (cls , method )
221
+ if fn is not tornado .web .RequestHandler ._unimplemented_method :
222
+ setattr (cls , method , AuthRequestHandler ._require_auth (fn ))
223
+
224
+ @staticmethod
225
+ def _require_auth [** P , R ](
226
+ fn : Callable [Concatenate [AuthRequestHandler , P ], R ],
227
+ ) -> Callable [Concatenate [AuthRequestHandler , P ], R | None ]:
228
+ @functools .wraps (fn )
229
+ def wrapper (
230
+ self : AuthRequestHandler , * args : P .args , ** kwargs : P .kwargs
231
+ ) -> R | None :
232
+ if not self .current_user :
233
+ self .require_setting ("auth_token" , "AuthRequestHandler" )
234
+ if not hmac .compare_digest (
235
+ self .get_query_argument ("token" , default = "invalid" ),
236
+ self .settings ["auth_token" ],
237
+ ):
238
+ self .set_status (403 )
239
+ self .render ("login.html" )
240
+ return None
241
+ self .set_signed_cookie (
242
+ self .AUTH_COOKIE_NAME ,
243
+ self .AUTH_COOKIE_VALUE ,
244
+ expires_days = 400 ,
245
+ httponly = True ,
246
+ samesite = "Strict" ,
247
+ )
248
+ return fn (self , * args , ** kwargs )
249
+
250
+ return wrapper
251
+
252
+ def get_current_user (self ) -> bool :
253
+ return (
254
+ self .get_signed_cookie (self .AUTH_COOKIE_NAME , min_version = 2 )
255
+ == self .AUTH_COOKIE_VALUE
256
+ )
257
+
258
+
259
+ class RequestHandler (AuthRequestHandler ):
210
260
application : Application
211
261
212
262
def write (self , chunk : str | bytes | dict | list ):
@@ -298,7 +348,7 @@ def get(self):
298
348
self .write (dict (commands = flowfilter .help ))
299
349
300
350
301
- class WebSocketEventBroadcaster (tornado .websocket .WebSocketHandler ):
351
+ class WebSocketEventBroadcaster (tornado .websocket .WebSocketHandler , AuthRequestHandler ):
302
352
# raise an error if inherited class doesn't specify its own instance.
303
353
connections : ClassVar [set [WebSocketEventBroadcaster ]]
304
354
@@ -717,6 +767,34 @@ class GZipContentAndFlowFiles(tornado.web.GZipContentEncoding):
717
767
}
718
768
719
769
770
+ handlers = [
771
+ (r"/" , IndexHandler ),
772
+ (r"/filter-help(?:\.json)?" , FilterHelp ),
773
+ (r"/updates" , ClientConnection ),
774
+ (r"/commands(?:\.json)?" , Commands ),
775
+ (r"/commands/(?P<cmd>[a-z.]+)" , ExecuteCommand ),
776
+ (r"/events(?:\.json)?" , Events ),
777
+ (r"/flows(?:\.json)?" , Flows ),
778
+ (r"/flows/dump" , DumpFlows ),
779
+ (r"/flows/resume" , ResumeFlows ),
780
+ (r"/flows/kill" , KillFlows ),
781
+ (r"/flows/(?P<flow_id>[0-9a-f\-]+)" , FlowHandler ),
782
+ (r"/flows/(?P<flow_id>[0-9a-f\-]+)/resume" , ResumeFlow ),
783
+ (r"/flows/(?P<flow_id>[0-9a-f\-]+)/kill" , KillFlow ),
784
+ (r"/flows/(?P<flow_id>[0-9a-f\-]+)/duplicate" , DuplicateFlow ),
785
+ (r"/flows/(?P<flow_id>[0-9a-f\-]+)/replay" , ReplayFlow ),
786
+ (r"/flows/(?P<flow_id>[0-9a-f\-]+)/revert" , RevertFlow ),
787
+ (r"/flows/(?P<flow_id>[0-9a-f\-]+)/(?P<message>request|response|messages)/content.data" , FlowContent ),
788
+ (r"/flows/(?P<flow_id>[0-9a-f\-]+)/(?P<message>request|response|messages)/content/(?P<content_view>[0-9a-zA-Z\-\_%]+)(?:\.json)?" , FlowContentView ),
789
+ (r"/clear" , ClearAll ),
790
+ (r"/options(?:\.json)?" , Options ),
791
+ (r"/options/save" , SaveOptions ),
792
+ (r"/state(?:\.json)?" , State ),
793
+ (r"/processes" , ProcessList ),
794
+ (r"/executable-icon" , ProcessImage ),
795
+ ] # fmt: skip
796
+
797
+
720
798
class Application (tornado .web .Application ):
721
799
master : mitmproxy .tools .web .master .WebMaster
722
800
@@ -734,43 +812,12 @@ def __init__(
734
812
debug = debug ,
735
813
autoreload = False ,
736
814
transforms = [GZipContentAndFlowFiles ],
815
+ auth_token = secrets .token_hex (16 ),
737
816
)
738
817
739
818
self .add_handlers ("dns-rebind-protection" , [(r"/.*" , DnsRebind )])
740
819
self .add_handlers (
741
820
# make mitmweb accessible by IP only to prevent DNS rebinding.
742
821
r"^(localhost|[0-9.]+|\[[0-9a-fA-F:]+\])$" ,
743
- [
744
- (r"/" , IndexHandler ),
745
- (r"/filter-help(?:\.json)?" , FilterHelp ),
746
- (r"/updates" , ClientConnection ),
747
- (r"/commands(?:\.json)?" , Commands ),
748
- (r"/commands/(?P<cmd>[a-z.]+)" , ExecuteCommand ),
749
- (r"/events(?:\.json)?" , Events ),
750
- (r"/flows(?:\.json)?" , Flows ),
751
- (r"/flows/dump" , DumpFlows ),
752
- (r"/flows/resume" , ResumeFlows ),
753
- (r"/flows/kill" , KillFlows ),
754
- (r"/flows/(?P<flow_id>[0-9a-f\-]+)" , FlowHandler ),
755
- (r"/flows/(?P<flow_id>[0-9a-f\-]+)/resume" , ResumeFlow ),
756
- (r"/flows/(?P<flow_id>[0-9a-f\-]+)/kill" , KillFlow ),
757
- (r"/flows/(?P<flow_id>[0-9a-f\-]+)/duplicate" , DuplicateFlow ),
758
- (r"/flows/(?P<flow_id>[0-9a-f\-]+)/replay" , ReplayFlow ),
759
- (r"/flows/(?P<flow_id>[0-9a-f\-]+)/revert" , RevertFlow ),
760
- (
761
- r"/flows/(?P<flow_id>[0-9a-f\-]+)/(?P<message>request|response|messages)/content.data" ,
762
- FlowContent ,
763
- ),
764
- (
765
- r"/flows/(?P<flow_id>[0-9a-f\-]+)/(?P<message>request|response|messages)/"
766
- r"content/(?P<content_view>[0-9a-zA-Z\-\_%]+)(?:\.json)?" ,
767
- FlowContentView ,
768
- ),
769
- (r"/clear" , ClearAll ),
770
- (r"/options(?:\.json)?" , Options ),
771
- (r"/options/save" , SaveOptions ),
772
- (r"/state(?:\.json)?" , State ),
773
- (r"/processes" , ProcessList ),
774
- (r"/executable-icon" , ProcessImage ),
775
- ],
822
+ handlers , # type: ignore # https://github.com/tornadoweb/tornado/pull/3455
776
823
)
0 commit comments