Hi pjcdawkins,

Thanks for this great module.
As I need a voting platform with really secret ballots (ie. no possibility to track the votes in the database), I need to develop an "anonymizer module" (called "election_vote_anonymize").

And it's not that easy as I first need to understand the way the modules interact.

My ideas:

- Creating each time a user votes, two different ballots: one that tracks the user (has already voted or not) and one to which the vote is assigned (secret ballot which I think could be assigned to uid 1 with blank values for request time, ip and so on).
- Assigning the vote to the secret ballot.
- Removing the ability to undo a vote if the module is enabled.

I tried this:

- I created a election_vote_anonymize module
- I added the following functions in my .module file:

function election_vote_anonymize_vote_form($form, &$form_state) {
$form['buttons']['submit']['#submit'] = array('election_vote_anonymize_form_vote', 'election_vote_form_vote');
if (!empty($post->allow_abstention)) {
$form['buttons']['abstain']['#submit'] = array('election_vote_anonymize_form_abstain', 'election_vote_form_abstain');
}
}

And the two following functions: election_vote_anonymize_form_vote and election_vote_anonymize_form_abstain().
In these two functions I create a ballot (I almost copied the similar ones from election_vote.forms.inc).
My problem concerns the election_vote_anonymize_form_vote, which invokes the 'election_vote_' . $election->type . '_save'... If I want to build a separate submodule, it should alter every 'election_vote_' . $election->type . '_save' function, which seems impossible.

Same problem to prevent the "undo vote" link from appearing (but it seems easier to solve).

What do you think of my idea?
Do you think it's possible?

Thanks in advance for your help and advice!

Comments

pjcdawkins’s picture

Hi Antoine_k

Quite busy now - I'll be able to respond to this in more detail later.

I thought I'd just post some fairly relevant facts:

    • There's already a bit of separation in place:
    • "Ballots" record that the person has voted (necessary for preventing multiple votes). Stored in the election_ballot database table.
    • "Votes" are how someone has voted (the politically sensitive part). Stored in the election_vote database table.
  • If you somehow remove the "ballot_id" from the election_vote table, then all votes are completely anonymous, because the link between ballots and votes is broken
  • The ballots (election_ballot) table also contain one item of personal information - the IP address. But you can use the IP Anon module to remove IP addresses (after a configurable period of time).

The feature you propose should be accomplished as a setting in election_vote.module. It would not really be possible in a separate module.

Regarding the use case, I can see why you might want completely anonymous votes. There are disadvantages though:

  • impossible to audit previous votes for security - obviously if there's a security problem you should just re-run the entier election
  • the "undo" feature would not be possible

BTW a colleague of mine suggested an exotic solution a while ago: you could encrypt the "ballot_id" with the user password, or with a separate password set by the voter. That would be extra effort while voting of course.

Antoine_k’s picture

Thanks for your answer and your ideas!

I completely understand that you already have much to do with the other feature requests.
I'm going to try to hack the election_vote module to enable this possibility. If I succeed it, I'll post a patch.

My main problem at the moment is that the field "ballot_id" is indexed in the database (which is a good thing when deleting the votes/ballots), which prevents from filling it with a password or anything else than a real ballot_id (or I misunderstood the concept of indexed db fields).

pjcdawkins’s picture

The column {election_vote}.ballot_id can be left NULL.

function election_vote_schema() {
  // ...
      'ballot_id' => array(
        'description' => 'Relates to {election_ballot}.ballot_id.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => FALSE,
      ),
  // ...
}
pjcdawkins’s picture

StatusFileSize
new3.01 KB

Here's how you might do it (clear caches after applying the patch).

Quite simple really.

By the way, the statistics (as they currently are) break if you use this. And any other DB query that uses a JOIN between the election_vote and election_ballot tables will no longer be reliable.

Antoine_k’s picture

Thanks for your help!

It works perfectly well even if I still think of your colleague's idea (encrypting the ballot_id with the user pwd as hash salt).
As I understand the way the module is implemented in the DB, it would require altering the election_vote db scheme to change the ballot_id into a varchar(128) (to be able to record a hashed ballot_id).

Antoine_k’s picture

Thanks for your help!

It works perfectly well even if I still think of your colleague's idea (encrypting the ballot_id with the user pwd as hash salt).
As I understand the way the module is implemented in the DB, it would require altering the election_vote db scheme to change the ballot_id into a varchar(128) (to be able to record a hashed ballot_id).

pjcdawkins’s picture

Yes that would be how you'd do it.

Meanwhile, I'm not sure about whether this should be added to Election.

It's quite a strange feature, and it adds a bit of confusion (votes are kept anonymous anyway, just not from those with database access and a bit of SQL knowledge).

Those with admin + database access will normally be able to see a whole lot of personal/sensitive information about a website's users - thus usually this kind of access should be highly restricted at that level. Beyond that, sometimes encryption is used (with credit card numbers or user passwords) but it seems over the top to do it with other information.

Votes are peculiar in that nearly all the information needs to be read very easily - the main bit that's sensitive is the relationship between a vote and the voter. Encrypting a relationship seems to be quite exotic to me. And who should be allowed to decrypt this? Just the voter? Or administrators/developers too?

Antoine_k’s picture

You're right, it's not worth and a bit extravagant... I'm happy with your solution!

Just a small add-on to your patch: it's worth to add the

    if (!empty($post->settings['anonymize'])) {
      $ballot_id = NULL;
    }

to the election_vote_form_abstain function (in election_vote.forms.inc).

Thanks again for your time.

bircher’s picture

Hello

I was looking for the exact same option.
And I had pretty much the same idea as Antoine_k for the implementation.

Although I agree that the administrator and whoever has access to the database, already has access to all sorts of sensitive data, it is not truly a secret vote if it is possible to check the relationship.

On the other hand, even i you hide the relation is is not "secret" for someone monitoring the database. (all you would have to do is track changes in the two tables with some other dirty script and there you go knowing who voted for what)
So acknowledging this limitation I think it would be a nice compromise to "anonymize" the election after the election has been closed and then disallow reopening it. This would solve the issue with the changing the minds while the election is ongoing. Yet afterwards it is not possible any more to track the voters.

Its just an idea...

liam morland’s picture

Version: 7.x-1.0-beta19 » 7.x-1.x-dev
pjcdawkins’s picture

On the other hand, even i you hide the relation is is not "secret" for someone monitoring the database. (all you would have to do is track changes in the two tables with some other dirty script and there you go knowing who voted for what)

Indeed - actually, knowing the time someone voted could reveal their identity, if you were also watching something else, like "Session opened for @name" watchdog messages. That's why the election_vote table doesn't store timestamps.

I think we can aim to add extra protection or anonymizing to cover the case of people who might have accessed a copy of the database (maliciously or otherwise). To protect properly against actual site admins would be pretty much impossible.

liam morland’s picture

The site could be monitored outside of Drupal for admin access during an election and any such access would raise an alarm.

liam morland’s picture

The column election_ballot.value can currently cause a vote to not be counted. This needs to be taken into consideration if election_vote.ballot_id is to be allowed to be null.

liam morland’s picture

Title: Anonymize the ballots (secret ballot) » Make election_vote records anonymous
liam morland’s picture

Status: Active » Needs review
StatusFileSize
new6.61 KB

This does FPTP and referendum, but not STV, which requires more elaborate changes to make it work.

@#8: $ballot_id is not used again in the function, so I don't see why that extra code is needed.

  • Liam Morland committed cd839a0 on 7.x-1.x
    Issue #2006326 by pjcdawkins, Liam Morland: Make election_vote records...
liam morland’s picture

Assigned: Unassigned » liam morland
Status: Needs review » Fixed

This is fixed for all built-in types except STV, which will be done in #2834454: Make election_vote records anonymous for STV.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.