From bb54f780060788cb833bdcef0e1f8e001d2d8199 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 13 Oct 2013 18:14:18 +0100 Subject: [PATCH] EVEIGB_SECURE_HEADERS mode. This enables some basic validation on headers coming into the client and validation of the data in the headers. Hopefully to provide a few extra roadblocks against fake/corrupt requests. --- README.md | 18 ++++++--- eveigb/middleware.py | 54 ++++++++++++++++---------- eveigb/tests.py | 92 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 133 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index fc96fc8..9a3d927 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ This library/application allows you to make use of EVE's in-game browser and mos Installation ------------ -1. Install the package with setup.py -2. Add 'eveigb' to your INSTALLED_APPS in your settings.py -3. Add eveigb.middleware.IGBMiddleware to MIDDLEWARE_CLASSES -4. Add eveigb.context_processors.igb to TEMPLATE_CONTEXT_PROCESSORS if you wish to make use of the template variables +1. Install the package with `setup.py` +2. Add `eveigb` to your `INSTALLED_APPS` in your `settings.py` +3. Add `eveigb.middleware.IGBMiddleware` to `MIDDLEWARE_CLASSES` +4. Add `eveigb.context_processors.igb` to `TEMPLATE_CONTEXT_PROCESSORS` if you wish to make use of the template variables Usage @@ -19,5 +19,11 @@ Usage The context processor makes a few variables available in your templates: -* is_igb - Indicates if the client is a EVE IGB client -* is_igb_trusted - Indicates if the client has trusted the site +* `is_igb` - Indicates if the client is a EVE IGB client +* `is_igb_trusted` - Indicates if the client has trusted the site + + +Options +------- + +* `EVEIGB_SECURE_HEADERS` - This will attempt to validate, as much as it can, the client as a IGB client. \ No newline at end of file diff --git a/eveigb/middleware.py b/eveigb/middleware.py index a3fe79e..3441b38 100644 --- a/eveigb/middleware.py +++ b/eveigb/middleware.py @@ -1,23 +1,26 @@ +from django.conf import settings + +# List of IGB headers, their type, and if they're expected in a all requests EVE_IGB_HEADERS = [ - 'HTTP_EVE_SERVERIP', - 'HTTP_EVE_CHARNAME', - 'HTTP_EVE_CHARID', - 'HTTP_EVE_CORPNAME', - 'HTTP_EVE_CORPID', - 'HTTP_EVE_ALLIANCENAME', - 'HTTP_EVE_ALLIANCEID', - 'HTTP_EVE_REGIONNAME', - 'HTTP_EVE_CONSTELLATIONNAME', - 'HTTP_EVE_SOLARSYSTEMNAME', - 'HTTP_EVE_STATIONNAME', - 'HTTP_EVE_STATIONID', - 'HTTP_EVE_CORPROLE', - 'HTTP_EVE_SHIPNAME', - 'HTTP_EVE_SHIPTYPEID', - 'HTTP_EVE_SHIPTYPENAME', - 'HTTP_EVE_SHIPID', - 'HTTP_EVE_SOLARSYSTEMID', - 'HTTP_EVE_WARFACTIONID', + ('HTTP_EVE_SERVERIP', False, 'str'), + ('HTTP_EVE_CHARNAME', True, 'str'), + ('HTTP_EVE_CHARID', True, 'int'), + ('HTTP_EVE_CORPNAME', True, 'str'), + ('HTTP_EVE_CORPID', True, 'int'), + ('HTTP_EVE_ALLIANCENAME', False, 'str'), + ('HTTP_EVE_ALLIANCEID', False, 'int'), + ('HTTP_EVE_REGIONNAME', True, 'str'), + ('HTTP_EVE_CONSTELLATIONNAME', True, 'str'), + ('HTTP_EVE_SOLARSYSTEMNAME', True, 'str'), + ('HTTP_EVE_STATIONNAME', False, 'str'), + ('HTTP_EVE_STATIONID', False, 'int'), + ('HTTP_EVE_CORPROLE', True, 'int'), + ('HTTP_EVE_SHIPNAME', True, 'str'), + ('HTTP_EVE_SHIPTYPEID', True, 'int'), + ('HTTP_EVE_SHIPTYPENAME', True, 'str'), + ('HTTP_EVE_SHIPID', True, 'int'), + ('HTTP_EVE_SOLARSYSTEMID', True, 'int'), + ('HTTP_EVE_WARFACTIONID', False, 'int'), ] @@ -32,11 +35,22 @@ class IGBMiddleware(object): request.is_igb_trusted = False if 'EVE-IGB' in request.META.get('HTTP_USER_AGENT', ''): + if getattr(settings, 'EVEIGB_SECURE_HEADERS', False): + for hdr, req, typ in EVE_IGB_HEADERS: + if not req: + continue + if not hdr in request.META: + return + if typ == 'int': + try: + long(request.META.get(hdr)) + except ValueError: + return request.is_igb = True if request.META.get('HTTP_EVE_TRUSTED', 'No') == 'Yes': request.is_igb_trusted = True - for header in EVE_IGB_HEADERS: + for header, req, typ in EVE_IGB_HEADERS: if header in request.META: setattr(request, header.replace('HTTP_', '').lower(), request.META.get(header)) diff --git a/eveigb/tests.py b/eveigb/tests.py index f383eec..7fda789 100644 --- a/eveigb/tests.py +++ b/eveigb/tests.py @@ -9,6 +9,8 @@ class IGBMiddlewareTest(TestCase): def setUp(self): self.im = IGBMiddleware() + self.igb_user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27 Safari/532.0 EVE-IGB' + self.non_igb_user_agent = 'Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405' def request_factory(self, meta=None): rq = Mock(path="/") @@ -19,7 +21,7 @@ class IGBMiddlewareTest(TestCase): def test_invalid_browser_igb(self): request = self.request_factory({ - 'HTTP_USER_AGENT': 'Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405' + 'HTTP_USER_AGENT': self.non_igb_user_agent }) self.im.process_request(request) self.assertEqual(request.is_igb, False) @@ -27,7 +29,7 @@ class IGBMiddlewareTest(TestCase): def test_invalid_browser_igb_fake_trust(self): request = self.request_factory({ - 'HTTP_USER_AGENT': 'Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405', + 'HTTP_USER_AGENT': self.non_igb_user_agent, 'HTTP_EVE_TRUSTED': 'Yes', }) self.im.process_request(request) @@ -36,7 +38,7 @@ class IGBMiddlewareTest(TestCase): def test_valid_igb_no_trust(self): request = self.request_factory({ - 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27 Safari/532.0 EVE-IGB', + 'HTTP_USER_AGENT': self.igb_user_agent }) self.im.process_request(request) self.assertEqual(request.is_igb, True) @@ -44,9 +46,89 @@ class IGBMiddlewareTest(TestCase): def test_valid_igb_trust(self): request = self.request_factory({ - 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27 Safari/532.0 EVE-IGB', + 'HTTP_USER_AGENT': self.igb_user_agent, 'HTTP_EVE_TRUSTED': 'Yes', }) self.im.process_request(request) self.assertEqual(request.is_igb, True) - self.assertEqual(request.is_igb_trusted, True) \ No newline at end of file + self.assertEqual(request.is_igb_trusted, True) + + def test_valid_igb_trust_extra_headers(self): + request = self.request_factory({ + 'HTTP_USER_AGENT': self.igb_user_agent, + 'HTTP_EVE_TRUSTED': 'Yes', + 'HTTP_EVE_CHARID': '12345678', + 'HTTP_EVE_CORPNAME': 'Llama Inc.', + 'HTTP_EVE_CORPID': '3456789', + 'HTTP_EVE_ALLIANCENAME': 'Fandab', + }) + self.im.process_request(request) + self.assertEqual(request.is_igb, True) + self.assertEqual(request.is_igb_trusted, True) + self.assertEquals(request.eve_charid, '12345678') + self.assertEquals(request.eve_corpname, 'Llama Inc.') + self.assertEquals(request.eve_corpid, '3456789') + self.assertEquals(request.eve_alliancename, 'Fandab') + + def test_valid_igb_trust_invalid_secure(self): + with self.settings(EVEIGB_SECURE_HEADERS=True): + request = self.request_factory({ + 'HTTP_USER_AGENT': self.igb_user_agent, + 'HTTP_EVE_TRUSTED': 'Yes', + 'HTTP_EVE_CHARID': '12345678', + 'HTTP_EVE_CORPNAME': 'Llama Inc.', + 'HTTP_EVE_CORPID': '3456789', + 'HTTP_EVE_ALLIANCENAME': 'Fandab', + }) + self.im.process_request(request) + self.assertEqual(request.is_igb, False) + self.assertEqual(request.is_igb_trusted, False) + + def test_valid_igb_trust_secure(self): + with self.settings(EVEIGB_SECURE_HEADERS=True): + request = self.request_factory({ + 'HTTP_USER_AGENT': self.igb_user_agent, + 'HTTP_EVE_TRUSTED': 'Yes', + 'HTTP_EVE_CHARNAME': 'Bob McBobbington', + 'HTTP_EVE_CHARID': '123456789', + 'HTTP_EVE_CORPNAME': 'Bob Inc.', + 'HTTP_EVE_CORPID': '456789', + 'HTTP_EVE_REGIONNAME': 'Delve', + 'HTTP_EVE_CONSTELLATIONNAME': 'Somewhere', + 'HTTP_EVE_SOLARSYSTEMNAME': 'NOL', + 'HTTP_EVE_CORPROLE': '0', + 'HTTP_EVE_SHIPNAME': 'Llamageddon', + 'HTTP_EVE_SHIPTYPEID': '12345', + 'HTTP_EVE_SHIPTYPENAME': 'Eeep', + 'HTTP_EVE_SHIPID': '1234435', + 'HTTP_EVE_SOLARSYSTEMID': '1234234', + }) + self.im.process_request(request) + self.assertEqual(request.is_igb, True) + self.assertEqual(request.is_igb_trusted, True) + self.assertEquals(request.eve_charid, '123456789') + self.assertEquals(request.eve_corpname, 'Bob Inc.') + self.assertEquals(request.eve_corpid, '456789') + + def test_valid_igb_trust_secure_invalid_data(self): + with self.settings(EVEIGB_SECURE_HEADERS=True): + request = self.request_factory({ + 'HTTP_USER_AGENT': self.igb_user_agent, + 'HTTP_EVE_TRUSTED': 'Yes', + 'HTTP_EVE_CHARNAME': 'Bob McBobbington', + 'HTTP_EVE_CHARID': 'xyz', + 'HTTP_EVE_CORPNAME': 'Bob Inc.', + 'HTTP_EVE_CORPID': '456789', + 'HTTP_EVE_REGIONNAME': 'Delve', + 'HTTP_EVE_CONSTELLATIONNAME': 'Somewhere', + 'HTTP_EVE_SOLARSYSTEMNAME': 'NOL', + 'HTTP_EVE_CORPROLE': '0', + 'HTTP_EVE_SHIPNAME': 'Llamageddon', + 'HTTP_EVE_SHIPTYPEID': '12345', + 'HTTP_EVE_SHIPTYPENAME': 'Eeep', + 'HTTP_EVE_SHIPID': '1234435', + 'HTTP_EVE_SOLARSYSTEMID': '1234234', + }) + self.im.process_request(request) + self.assertEqual(request.is_igb, False) + self.assertEqual(request.is_igb_trusted, False) \ No newline at end of file