Vote-code Obfuscation

As we are not able to properly encrypt the traffic, let's make the vote-code interception more uncomfortable and effort-intensive.

The traffic interception tool may catch the options list when it is deployed to a client and the vote-code when a user votes.

By randomizing vote-codes on a daily basis we'll make it hard enough to track the votes history of an individual machine or at scale. It is not impossible but becomes a costly IT that should be hard to justify.

This combined with the fact that the vote may only be tight to a machine but not the individual makes the obfuscation mechanism sufficient for the use case.


In [1]:
from itertools import permutations
from hashlib import md5
from time import time
from random import shuffle, choice
    
class VoteCodeObfuscator(object):
    """Provides the obfuscation means for the interceptable traffic"""
    
    def __init__(self, options=None, variants=None):
        """provides basic interfaces for unit testing"""
        self.coded_options = None
        self.variants = variants
        self.options = options
    
    def encode_options(self):
        """Create pseudo-random option keys"""
        salted = []
        for i, opt in enumerate(self.options):
            salted.append(md5(str(i) + str(time())).hexdigest())
        self.coded_options = salted
    
    def create_variants(self):
        """Creates vote-code combinations"""
        temp_variants = []
        salted = []
        for opt in self.options:
            salted.append(md5(opt + str(time())).hexdigest())
        shuffle(self.options)
        for variant in permutations(salted):
            temp_variants.append(variant)
        self.variants = dict(zip(
                [md5(str(x)).hexdigest() for x in temp_variants],
                temp_variants))
        
    def deploy(self):
        """Returns a random variant for deployment"""
        key = choice(self.variants.keys())
        return {"key":key, "options":self.variants[key]}
        
    def decode(self, combination_key, coded_option):
        """Returns the decoded option code
        If the inputs are expired returns False"""
        if combination_key not in self.variants.keys():
            return False
        variant = self.variants[combination_key]
        if coded_option not in variant:
            return False
        return self.options[variant.index(coded_option)]

Application scenario:

The code below is run by a worker (independent process) on a day change event. The resulting variants map is then saved to the disk so that any worker process can access it.


In [2]:
moods_obfuscator = VoteCodeObfuscator(
    options=["opt1", "opt2", "opt3", "opt4"]) 
moods_obfuscator.create_variants()

Each time a client requiests moods widget deployment, the server will reply with a random variant like below:


In [3]:
moods_obfuscator.deploy()


Out[3]:
{'key': 'b55645bd979c9dc9f1a8e18289d4f4d8',
 'options': ('991c25c9c377bb9fd1bb1152d5017be5',
  '785d915eb2c1fbb67b037fa6955a44e3',
  '396f87befcc04b908055d403f5c4ee49',
  'd8d188aaefee9c9ff14ee85f8a140c08')}

When a user votes, the client sends back the variant code and the option code. A worker picks up the message and decodes it to get the actual opton code. An option code is then used to perform +1 operation on the column within the daily metrics that matches the option key


In [4]:
print moods_obfuscator.decode(
    'b55645bd979c9dc9f1a8e18289d4f4d8', 
    '396f87befcc04b908055d403f5c4ee49')


opt2

if a user sends the vote from a widget session that was deployed say a day ago decoding will fail and return False. The same obfuscation logic is used for the "why-bad" poll.


In [5]:
print moods_obfuscator.decode(
    '968c48a1a75ff8beabd6f4ecf0cbf0dc', 
    'edda6cc6dbc632384664964259d6b694')


False