[{"data":1,"prerenderedAt":562},["ShallowReactive",2],{"i-lucide:menu":3,"i-lucide:arrow-up-right":8,"i-lucide:moon":10,"i-lucide:sun":12,"i-lucide:rss":14,"i-simple-icons:github":16,"i-simple-icons:linkedin":18,"post-\u002F2015\u002Fsecurity-of-online-voting-systems":21,"surround-\u002F2015\u002Fsecurity-of-online-voting-systems":549,"i-lucide:arrow-left":558,"i-lucide:arrow-right":560},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":7},0,24,false,"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 5h16M4 12h16M4 19h16\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":9},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M7 7h10v10M7 17L17 7\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":11},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":13},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"4\"\u002F>\u003Cpath d=\"M12 2v2m0 16v2M4.93 4.93l1.41 1.41m11.32 11.32l1.41 1.41M2 12h2m16 0h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":15},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M4 11a9 9 0 0 1 9 9M4 4a16 16 0 0 1 16 16\"\u002F>\u003Ccircle cx=\"5\" cy=\"19\" r=\"1\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":17},"\u003Cpath fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":19,"hidden":20},"\u003Cpath fill=\"currentColor\" d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037c-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85c3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.06 2.06 0 0 1-2.063-2.065a2.064 2.064 0 1 1 2.063 2.065m1.782 13.019H3.555V9h3.564zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0z\"\u002F>",true,{"id":22,"title":23,"body":24,"category":537,"comments":538,"date":539,"description":33,"excerpt":538,"extension":540,"image":538,"meta":541,"navigation":20,"path":542,"readingTime":543,"seo":544,"stem":545,"subtitle":546,"tags":538,"wordCount":547,"__hash__":548},"posts\u002F2015\u002Fsecurity-of-online-voting-systems.md","Security of Online Voting Systems",{"type":25,"value":26,"toc":521},"minimark",[27,34,51,57,60,63,68,71,76,79,85,88,93,96,99,103,106,111,114,119,122,124,128,131,136,139,158,163,166,181,187,189,193,205,216,219,224,227,232,241,243,247,256,259,263,266,270,282,292,295,316,319,323,326,330,339,343,358,362,365,373,379,382,423,426,430,433,437,440,444,457,463,466,472,475,479,482,486,489,492,506,510,514,517],[28,29,30],"p",{},[31,32,33],"em",{},"Note: This article was written to increase developer awareness to different attack vectors, not as a how-to for attacking existing systems.",[28,35,36,37,44,45,50],{},"In 2012 ",[38,39,43],"a",{"href":40,"rel":41},"http:\u002F\u002Fdelfi.ee",[42],"nofollow","delfi.ee"," (an Estonian media outlet) organized a \"",[38,46,49],{"href":47,"rel":48},"http:\u002F\u002Fnoortehaal.delfi.ee\u002Fnews\u002Felu\u002Fhaaleta-kes-on-selle-aasta-kuumim-tudengimees?id=64977888",[42],"Hot male student","\" competition. Visitors of the website could cast their votes to five candidates of different universities. The code was poorly written and so the winner of the competition wasn't necessarily the best looking candidate, but the one who had the support of tech-savvy fellow students.",[28,52,53],{},[54,55],"img",{"alt":49,"src":56},"\u002Fcontent\u002F2015\u002F04\u002Fhot_student.png",[28,58,59],{},"Online voting systems are often implemented with naive security considerations. \"Why would anyone ever delete their cookies?\", thinks the blue-eyed developer who identifies visitors by cookies. I'll tell you why: because they can.",[28,61,62],{},"Think about how people could exploit your system and be the person in the team who's most enthusiastic about tearing apart your own code. You ship the code to the real world - don't assume all visitors will play nice with the system.",[64,65,67],"h1",{"id":66},"attack-vectors-to-online-voting-systems","Attack vectors to online voting systems",[28,69,70],{},"Below is a list of different vulnerabilities most commonly found in voting implementations.",[72,73,75],"h2",{"id":74},"plain-refresh","Plain refresh",[28,77,78],{},"The website displays a voting area. The user presses \"submit\" and is shown a thank-you message. The website saves no information about the vote being processed, not to its own database, not into the browser.",[28,80,81],{},[82,83,84],"strong",{},"Vulnerability: No checks whatsoever",[28,86,87],{},"The website has no idea whether the visitor has already voted, hence the voting form is shown and processed for each request.",[28,89,90],{},[82,91,92],{},"Attack: Repeat the request",[28,94,95],{},"No special knowlege is needed to attack this system. You can refresh the browser and vote again (spam F5) or write a short script to automate it, just sending curl requests from bash.",[97,98],"hr",{},[72,100,102],{"id":101},"forget-the-cookiejar","Forget the cookiejar",[28,104,105],{},"The website sets a special cookie into the visitors browser to mark the user as 'you have voted'. This is the most common strategy for letting visitors vote only once. The server trusts the visitors browser to store the 'already voted' flag.",[28,107,108],{},[82,109,110],{},"Vulnerability: Trusting user input",[28,112,113],{},"User input should never be trusted. Everything sent by the user to the server can be tampered with, especially if the only check is the presence of a cookie.",[28,115,116],{},[82,117,118],{},"Attack: Forget the cookie",[28,120,121],{},"It is trivial for a user to remove the cookie from his browser or to write a script that just doesn't save and return cookies.",[97,123],{},[72,125,127],{"id":126},"ip-based-memory","IP based memory",[28,129,130],{},"The server saves the voters IP into a database of IPs who have voted. Before processing the vote, the visitors IP is checked against this database and if a match is found, an error is shown (you have already voted).",[28,132,133],{},[82,134,135],{},"Vulnerability: Failing to identify voters",[28,137,138],{},"The assumption here is that IP equals person. This is not the case:",[140,141,142,146,149,152,155],"ul",{},[143,144,145],"li",{},"Many internet connections are set up with a dynamic IP",[143,147,148],{},"A person has more than one devices (phone, laptop, tablet)",[143,150,151],{},"Visiting different hotspots (coffees) yields different WAN IPs",[143,153,154],{},"VPNs provide different IPs",[143,156,157],{},"Oftentimes, several devices share a single external IP",[28,159,160],{},[82,161,162],{},"Attack: Use different IPs",[28,164,165],{},"Take your laptop and visit twenty different coffee shops: you don't even have to sit down, just connect from outside the coffee house, agree to the ToS, open the browser, vote, disconnect and move to the next coffee (hint: automate).",[28,167,168,169,174,175,180],{},"Sign up for a VPN service such as ",[38,170,173],{"href":171,"rel":172},"https:\u002F\u002Fprivateinternetaccess.com",[42],"Private Internet Access"," or install ",[38,176,179],{"href":177,"rel":178},"https:\u002F\u002Fwww.torproject.org\u002F",[42],"Tor",". You'll get access to dozens of different IPs.",[28,182,183],{},[54,184],{"alt":185,"src":186},"VPN locations","\u002Fcontent\u002F2015\u002F04\u002Fpia_locations.png",[97,188],{},[72,190,192],{"id":191},"request-forgery","Request forgery",[194,195,196],"blockquote",{},[28,197,198,199,204],{},"Cross-Site Scripting (",[38,200,203],{"href":201,"rel":202},"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FCross-site_Scripting_%28XSS%29",[42],"XSS",") attacks are a type of injection, in which malicious scripts are injected into otherwise benign and trusted web sites",[194,206,207],{},[28,208,209,210,215],{},"Cross-Site Request Forgery (",[38,211,214],{"href":212,"rel":213},"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FCSRF",[42],"CSRF",") is an attack that forces an end user to execute unwanted actions on a web application in which they're currently authenticated",[28,217,218],{},"The voting page has a javascript injection vulnerability or does not implement CSRF checks.",[28,220,221],{},[82,222,223],{},"Vulnerability: Failure to protect against XSS and CSRF",[28,225,226],{},"The site does not filter user input for javascript injection and accepts requests to internal endpoints from external sources.",[28,228,229],{},[82,230,231],{},"Attack: XSS and CSRF",[28,233,234,235,240],{},"Try to inject javascript into the voting page itself (comments section) or do it to another popular page (either under your control or with XSS vulnerability). The malicious JavaScript would make a XHR request to the voting page and vote on the unsuspecting visitors behalf. The recent ",[38,236,239],{"href":237,"rel":238},"http:\u002F\u002Farstechnica.com\u002Fsecurity\u002F2015\u002F03\u002Fmassive-denial-of-service-attack-on-github-tied-to-chinese-government",[42],"Chinese DDoS to GitHub"," is an example of a similar approach: unsuspecting visitors actually performed requests to an external site.",[97,242],{},[64,244,246],{"id":245},"defense-strategies","Defense strategies",[28,248,249,250,255],{},"There is no magic bullet that works for every use case. How you protect your system depends on your needs. Requiring all voters to be signed in with their  ",[38,251,254],{"href":252,"rel":253},"http:\u002F\u002Fid.ee\u002Findex.php?id=30470",[42],"ID card"," guarantees that everyone votes exactly once, but will lower poll participation rate drastically. Oftentimes it's just a compromise on 'reasonable' level of protection.",[28,257,258],{},"Here are some things you can do.",[72,260,262],{"id":261},"store-state","Store state",[28,264,265],{},"Don't allow multiple votes from the same source. Select a good enough authentication scheme (\"how do I identify a unique user?\") and don't allow the same source to vote twice.",[72,267,269],{"id":268},"use-dynamic-values-for-submission","Use dynamic values for submission",[28,271,272,273,277,278,281],{},"If your voting system has a ",[274,275,276],"code",{},"voteId"," (the ID of the current poll) and ",[274,279,280],{},"selectionId"," (the choice that the user wants to vote for), don't make their values easily guessable and\u002For static.",[283,284,289],"pre",{"className":285,"code":287,"language":288},[286],"language-text","https:\u002F\u002Fexample.com\u002Farticle\u002F124\u002Fpoll\u002Fsubmit?voteId=32&selectionId=option2\n","text",[274,290,287],{"__ignoreMap":291},"",[28,293,294],{},"Using static values enables the attacker to just copy them into their attack script.",[28,296,297,298,303,304,306,307,309,310,315],{},"Either use ",[38,299,302],{"href":300,"rel":301},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRolling_code",[42],"rolling codes"," (dynamic, unique ",[274,305,276],{}," and ",[274,308,280],{},") for each page rendering or include an invisible form field with a dynamic, unique value (a ",[38,311,314],{"href":312,"rel":313},"https:\u002F\u002Fcodex.wordpress.org\u002FWordPress_Nonces",[42],"nonce",") which is verified on form submit by the server.",[28,317,318],{},"This technique can be defeated with web scraping: the attacker would first request the poll page HTML and then submit the vote with the unique token parsed out of the original response and into the vote request.",[72,320,322],{"id":321},"require-authentication","Require authentication",[28,324,325],{},"Require your voters to log in before voting. This loses the anonymity of the poll - you will get less results -, but increases the certainty that the results won't be tampered with. A user can only vote once, since the state is tied with his user account. The effectiveness of this approach is dependant on your new user signup policy: how difficult is it for a bot to create a new, temporary, fake account?",[72,327,329],{"id":328},"protect-against-xss-and-csrf","Protect against XSS and CSRF",[28,331,332,333,338],{},"Filter user input for XSS. Implement ",[38,334,337],{"href":335,"rel":336},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSame-origin_policy",[42],"same-origin policy"," and protect against CSRF.",[72,340,342],{"id":341},"add-a-turing-test","Add a Turing test",[28,344,345,346,351,352,357],{},"Add a ",[38,347,350],{"href":348,"rel":349},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCAPTCHA",[42],"CAPTCHA"," to the vote submission. This will annoy legitimate users, but will cause problems to attackers. This method of protection is ",[38,353,356],{"href":354,"rel":355},"http:\u002F\u002Fresources.infosecinstitute.com\u002Fintroduction-to-automated-captcha-solving\u002F",[42],"not bulletproof",", but will probably deter many script kiddies.",[72,359,361],{"id":360},"dont-trust-user-input","Don't trust user input",[28,363,364],{},"Never store state on the user's browser. Don't use cookies to mark that the user has voted.",[28,366,367,372],{},[38,368,371],{"href":369,"rel":370},"http:\u002F\u002Fvikerraadio.err.ee",[42],"vikerraadio.err.ee"," stores the voter information in a cookie:",[28,374,375],{},[54,376],{"alt":377,"src":378},"vikerraadio cookie","\u002Fcontent\u002F2015\u002F04\u002Fvoter_cookie.png",[28,380,381],{},"URL decoding the value of the cookie gives this:",[283,383,387],{"className":384,"code":385,"language":386,"meta":291,"style":291},"language-json shiki shiki-themes github-light github-dark","[{\"optionId\":\"4cad67ef-12d5-4380-b26a-23066afb2a09\",\"lastVoted\":\"2015-04-19T10:30:10.050Z\"}]\n","json",[274,388,389],{"__ignoreMap":291},[390,391,394,398,402,405,409,412,415,417,420],"span",{"class":392,"line":393},"line",1,[390,395,397],{"class":396},"sVt8B","[{",[390,399,401],{"class":400},"sj4cs","\"optionId\"",[390,403,404],{"class":396},":",[390,406,408],{"class":407},"sZZnC","\"4cad67ef-12d5-4380-b26a-23066afb2a09\"",[390,410,411],{"class":396},",",[390,413,414],{"class":400},"\"lastVoted\"",[390,416,404],{"class":396},[390,418,419],{"class":407},"\"2015-04-19T10:30:10.050Z\"",[390,421,422],{"class":396},"}]\n",[28,424,425],{},"We can see that the cookie contains the vote timestamp and option value. What might happen if I delete this cookie? Can I vote again?",[72,427,429],{"id":428},"implement-ip-blacklist","Implement IP blacklist",[28,431,432],{},"Compare voter IP against a database of known VPN providers. This will reduce the amount of requests that can come in from a proxy-IP, but is a dangerous ground to tread on: discrimination of privacy-aware individuals can blow up in your face. You're basically saying that since the visitor values their privacy, they can not use your site.",[64,434,436],{"id":435},"identifying-automated-votes","Identifying automated votes",[28,438,439],{},"Let's say that an attacker writes a script that makes 100 requests to the target voting server and adds 100 votes to his preferred option. As the developer of the system, how do you distinguish fake votes from the real ones?",[72,441,443],{"id":442},"look-at-the-request-content","Look at the request content",[28,445,446,447,450,451,456],{},"A script kiddie might be 'clever' enough to write a PHP script, but does he actually understand how HTTP or the library he's using works? For example, the ",[274,448,449],{},"User-Agent"," HTTP header of a popular PHP HTTP client library ",[38,452,455],{"href":453,"rel":454},"https:\u002F\u002Fguzzle.readthedocs.org\u002Fen\u002Flatest\u002F",[42],"Guzzle"," looks like this:",[283,458,461],{"className":459,"code":460,"language":288},[286]," Guzzle\u002F\u003CGuzzle_Version> curl\u002F\u003Ccurl_version> PHP\u002F\u003CPHP_VERSION>\n",[274,462,460],{"__ignoreMap":291},[28,464,465],{},"while Chrome typically sends something like this:",[283,467,470],{"className":468,"code":469,"language":288},[286],"Mozilla\u002F5.0 (X11; Linux x86_64) AppleWebKit\u002F537.36 (KHTML, like Gecko) Chrome\u002F42.0.2311.90 Safari\u002F537.36\n",[274,471,469],{"__ignoreMap":291},[28,473,474],{},"This information is sent to the server and can be stored in logs. The server might automatically reject suspicious requests based on the request headers or notify administrators.",[72,476,478],{"id":477},"timing","Timing",[28,480,481],{},"So you want your script to add 100 votes? Will you send 100 requests as quickly as possible or will you space them out evenly inside 24 hours? As with the request contents, it's easy to identify requests by their arrival time. Seeing 100 incoming votes in the space of a few seconds raises some questions.",[64,483,485],{"id":484},"practical-example","Practical example",[28,487,488],{},"This example demonstrates the adding of votes to a vulnerable system. The target system identifies voters by IP (no IP can vote twice).",[28,490,491],{},"The attack script adds three votes to a specific option and works as follows:",[140,493,494,497,500,503],{},[143,495,496],{},"A connection to the local Tor service is established (HTTP requests will be proxied through Tor)",[143,498,499],{},"A HTTP POST request to the target voting system is sent. The system rejects the request, since the current IP has already voted",[143,501,502],{},"The script requests a new IP address from Tor",[143,504,505],{},"A HTTP POST request to the target voting system is sent. The system accepts the vote",[507,508],"asciinema",{"id":509},"18935",[64,511,513],{"id":512},"morale","Morale",[28,515,516],{},"Be aware that your voting implementation can be hacked - but you can make it more time-consuming if you implement elementary safeguards.",[518,519,520],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":291,"searchDepth":522,"depth":522,"links":523},2,[524,525,526,527,528,529,530,531,532,533,534,535,536],{"id":74,"depth":522,"text":75},{"id":101,"depth":522,"text":102},{"id":126,"depth":522,"text":127},{"id":191,"depth":522,"text":192},{"id":261,"depth":522,"text":262},{"id":268,"depth":522,"text":269},{"id":321,"depth":522,"text":322},{"id":328,"depth":522,"text":329},{"id":341,"depth":522,"text":342},{"id":360,"depth":522,"text":361},{"id":428,"depth":522,"text":429},{"id":442,"depth":522,"text":443},{"id":477,"depth":522,"text":478},"Software Development",null,"2015-04-16","md",{},"\u002F2015\u002Fsecurity-of-online-voting-systems","about 8 minutes",{"title":23,"description":33},"2015\u002Fsecurity-of-online-voting-systems","How To Get 100 Votes",1553,"ls0oizjIxx0NRrSqkzLFq-2mzDeaPLtN7TzlucxhhRA",[550,554],{"title":551,"path":552,"stem":553,"children":-1},"How Most Developer Recruiting Ads Look Like Nowadays","\u002F2015\u002Fhow-most-developer-recruiting-ads-look-like-nowadays","2015\u002Fhow-most-developer-recruiting-ads-look-like-nowadays",{"title":555,"path":556,"stem":557,"children":-1},"Open Office And Interruptions? Post A \"Go Away!\" Sign","\u002F2015\u002Fdo-not-disturb","2015\u002Fdo-not-disturb",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":559},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m12 19l-7-7l7-7m7 7H5\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":561},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7l7 7l-7 7\"\u002F>",1790886933982]