// API callback
related_results_labels_thumbs({"version":"1.0","encoding":"UTF-8","feed":{"xmlns":"http://www.w3.org/2005/Atom","xmlns$openSearch":"http://a9.com/-/spec/opensearchrss/1.0/","xmlns$blogger":"http://schemas.google.com/blogger/2008","xmlns$georss":"http://www.georss.org/georss","xmlns$gd":"http://schemas.google.com/g/2005","xmlns$thr":"http://purl.org/syndication/thread/1.0","id":{"$t":"tag:blogger.com,1999:blog-4603622075690903012"},"updated":{"$t":"2023-10-23T04:18:30.735-07:00"},"category":[{"term":"AWS"},{"term":"AWS Certified Solutions Architect"},{"term":"Architect"},{"term":"Certification"},{"term":"AWS Certified Developer"},{"term":"AWS Certified SysOps Administrator"},{"term":"EC2"},{"term":"Course"},{"term":"Development"},{"term":"Cognito"},{"term":"Tutorial"},{"term":"SysOps Administration"},{"term":"CloudFront"},{"term":"News"},{"term":"S3"},{"term":"AutoScaling"},{"term":"Cordova"},{"term":"DynamoDB"},{"term":"ELB"},{"term":"IAM"},{"term":"Node.JS"},{"term":"PhoneGap"},{"term":"Security"},{"term":"Shared Responsibility"},{"term":"VPC"},{"term":"Amazon Aurora"},{"term":"Android"},{"term":"Denial of Service"},{"term":"EBS"},{"term":"IOS"},{"term":"NodeJS"},{"term":"RDS"},{"term":"AWS IoT"},{"term":"AWS Mobile Hub"},{"term":"Amazon Inspector"},{"term":"Amazon QuickSight"},{"term":"AngularJS"},{"term":"CloudTrail"},{"term":"Cloudformation"},{"term":"Dynamic"},{"term":"ECS"},{"term":"India"},{"term":"Internet of Things"},{"term":"Kinesis Firehose"},{"term":"MEAN"},{"term":"MariaDB"},{"term":"PIOPS"},{"term":"PhantomJS"},{"term":"ReactJS"},{"term":"Region"},{"term":"Route 53"},{"term":"Selenium"},{"term":"Trusted Adviser"},{"term":"WAF"},{"term":"Web Application Firewall"},{"term":"YAML"},{"term":"app"},{"term":"example"}],"title":{"type":"text","$t":"BackSpace Academy Blog"},"subtitle":{"type":"html","$t":"A blog about all things Amazon Web Services (AWS) and Cloud Certification."},"link":[{"rel":"http://schemas.google.com/g/2005#feed","type":"application/atom+xml","href":"http:\/\/learn-aws.blogspot.com\/feeds\/posts\/default"},{"rel":"self","type":"application/atom+xml","href":"http:\/\/www.blogger.com\/feeds\/4603622075690903012\/posts\/default\/-\/NodeJS?alt=json-in-script\u0026max-results=6"},{"rel":"alternate","type":"text/html","href":"http:\/\/learn-aws.blogspot.com\/search\/label\/NodeJS"},{"rel":"hub","href":"http://pubsubhubbub.appspot.com/"}],"author":[{"name":{"$t":"BackSpace Academy"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/15061292652079774775"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"16","height":"16","src":"https:\/\/img1.blogblog.com\/img\/b16-rounded.gif"}}],"generator":{"version":"7.00","uri":"http://www.blogger.com","$t":"Blogger"},"openSearch$totalResults":{"$t":"2"},"openSearch$startIndex":{"$t":"1"},"openSearch$itemsPerPage":{"$t":"6"},"entry":[{"id":{"$t":"tag:blogger.com,1999:blog-4603622075690903012.post-96012945605615497"},"published":{"$t":"2016-10-03T06:10:00.000-07:00"},"updated":{"$t":"2016-10-05T18:44:02.277-07:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"AngularJS"},{"scheme":"http://www.blogger.com/atom/ns#","term":"AWS"},{"scheme":"http://www.blogger.com/atom/ns#","term":"CloudFront"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Dynamic"},{"scheme":"http://www.blogger.com/atom/ns#","term":"EC2"},{"scheme":"http://www.blogger.com/atom/ns#","term":"MEAN"},{"scheme":"http://www.blogger.com/atom/ns#","term":"NodeJS"},{"scheme":"http://www.blogger.com/atom/ns#","term":"ReactJS"}],"title":{"type":"text","$t":"Super Fast Dynamic Websites with CloudFront, ReactJS, and NodeJS - Part 1"},"content":{"type":"html","$t":"\u003Cbr \/\u003E\nCloudFront should be an essential component of any web based application deployment. It not only instantly provides super low-latency performance, it also dramatically reduces server costs while providing maximum server uptime.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nCreating low latency static websites with CloudFront is a relatively simple process. You simply upload your site to S3 and create a CloudFront distribution for it. This is great for HTML5 websites and static blogs such as Jeckyl. But what about dynamic sites that need real time information presented to the end user? A different strategy is clearly required. Much has been published about different methods of caching dynamic websites but, I will present the most common sense and reliable technique to achieve this end.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Ch3\u003E\nServer Side v Browser Side Rendering\u003C\/h3\u003E\nIf you read any book on NodeJS you will no doubt find plenty of examples of rendering Jade templates with Express on the server. If you are still doing this, then you are wasting valuable server resources. A far better option is to render on the browser side. There are a number of frameworks specifically for this, the most popular being Facebook's ReactJS and Google's AngularJS (see \u003Ca href=\"http:\/\/stateofjs.com\/2016\/frontend\/\" target=\"_blank\"\u003EState of JS report\u003C\/a\u003E). I personally use ReactJS and the example will be in ReactJS, but either is fine.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nCreating your site using ReactJS or AngularJS and uploading it to your NodeJS public directory will shift the rendering of your site from your server to the client's browser. Users of your app will no longer be waiting for rendered pages and will see pages appear with the click of a button.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nYou can now create a CloudFront distribution for your ReactJS or AngularJS site.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nAlthough pages may be rendered instantly in the browser, any dynamic data required for the pages will be cached by CloudFront. We most probably do not want our dynamic data cached. We will still need a solution for delivering this data to the browser.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Ch3\u003E\nHandling Dynamic Data\u003C\/h3\u003E\n\u003Cbr \/\u003E\nAlthough there are many elaborate techniques published for handling dynamic data with CloudFront, the best way is to deliver this data without caching at all from CloudFront.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nNot all HTTP methods are cached by CloudFront, only responses to GET and HEAD requests (although you can also configure CloudFront to cache responses to OPTIONS requests). If we use a different HTTP method, such as POST, PUT or DELETE \u0026nbsp;the request will not be cached by Cloudfront. CloudFront will simply proxy these requests back to our server.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nOur EC2 NodeJS server can now be used to respond to requests for dynamic data by creating an API for our application that responds to POST requests from the client browser.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003E\n\u003Ca href=\"https:\/\/2.bp.blogspot.com\/-5Be6dKuaOHQ\/V_JXJqPXlvI\/AAAAAAAAAXA\/xfP1-VLIz9sdkFol9-7ttradYMsWlTCaQCLcB\/s1600\/pptCD4.pptm%2B%255BAutosaved%255D.png\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003E\u003Cimg border=\"0\" height=\"360\" src=\"https:\/\/2.bp.blogspot.com\/-5Be6dKuaOHQ\/V_JXJqPXlvI\/AAAAAAAAAXA\/xfP1-VLIz9sdkFol9-7ttradYMsWlTCaQCLcB\/s640\/pptCD4.pptm%2B%255BAutosaved%255D.png\" width=\"640\" \/\u003E\u003C\/a\u003E\u003C\/div\u003E\n\u003Cdiv style=\"text-align: center;\"\u003E\n\u003Cbr \/\u003E\u003C\/div\u003E\n\u003Cbr \/\u003E\nSome of you might be wondering why I haven't used serverless technology such as AWS Lambda or API Gateway. Rest assured I will be posting another series using this but, I consider EC2 as the preferred technology for most applications. First of all, costs are rarely mentioned in the serverless discussion. If you have an application that has significant traffic, the conventional EC2\/ELB architecture will be the most cost effective. Secondly, many modern web applications are utilising websocket connections. Connections like this are possible with EC2 directly and also behind an ELB when utilizing proxy protocol. This is not possible with serverless technology as connections are short lived.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nIn the next post in this series we will set up our NodeJS server on EC2, create a CloudFront distribution and, create our API for handling dynamic data.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nBe sure to subscribe to the blog so that you can get the latest updates.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nFor more AWS training and tutorials check out\u0026nbsp;\u003Ca href=\"https:\/\/backspace.academy\/\"\u003Ebackspace.academy\u003C\/a\u003E\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Ca href=\"https:\/\/backspace.academy\/\" target=\"_blank\"\u003E\u003Cimg src=\"https:\/\/backspace.academy\/assets\/img\/logo.svg\" \/\u003E\u003C\/a\u003E"},"link":[{"rel":"replies","type":"application/atom+xml","href":"http:\/\/learn-aws.blogspot.com\/feeds\/96012945605615497\/comments\/default","title":"Post Comments"},{"rel":"replies","type":"text/html","href":"http:\/\/learn-aws.blogspot.com\/2016\/10\/super-fast-dynamic-websites-with.html#comment-form","title":"0 Comments"},{"rel":"edit","type":"application/atom+xml","href":"http:\/\/www.blogger.com\/feeds\/4603622075690903012\/posts\/default\/96012945605615497"},{"rel":"self","type":"application/atom+xml","href":"http:\/\/www.blogger.com\/feeds\/4603622075690903012\/posts\/default\/96012945605615497"},{"rel":"alternate","type":"text/html","href":"http:\/\/learn-aws.blogspot.com\/2016\/10\/super-fast-dynamic-websites-with.html","title":"Super Fast Dynamic Websites with CloudFront, ReactJS, and NodeJS - Part 1"}],"author":[{"name":{"$t":"BackSpace Academy"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/15061292652079774775"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"16","height":"16","src":"https:\/\/img1.blogblog.com\/img\/b16-rounded.gif"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/2.bp.blogspot.com\/-5Be6dKuaOHQ\/V_JXJqPXlvI\/AAAAAAAAAXA\/xfP1-VLIz9sdkFol9-7ttradYMsWlTCaQCLcB\/s72-c\/pptCD4.pptm%2B%255BAutosaved%255D.png","height":"72","width":"72"},"thr$total":{"$t":"0"}},{"id":{"$t":"tag:blogger.com,1999:blog-4603622075690903012.post-6695927890245298364"},"published":{"$t":"2016-09-04T20:51:00.004-07:00"},"updated":{"$t":"2016-09-26T06:29:18.130-07:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"AWS"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Denial of Service"},{"scheme":"http://www.blogger.com/atom/ns#","term":"IAM"},{"scheme":"http://www.blogger.com/atom/ns#","term":"NodeJS"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Security"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Shared Responsibility"}],"title":{"type":"text","$t":"Shared Responsibility 2 - Using Dynamic CSS Selectors to stop the bots."},"content":{"type":"html","$t":"\u003Cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003E\n\u003Ca href=\"https:\/\/media.licdn.com\/mpr\/mpr\/AAEAAQAAAAAAAATwAAAAJGU3YjNlNGFmLTc1NDQtNDJiMi1iYWYwLTRiOTA2ZTNjNjY0MQ.jpg\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003E\u003Cimg border=\"0\" height=\"364\" src=\"https:\/\/media.licdn.com\/mpr\/mpr\/AAEAAQAAAAAAAATwAAAAJGU3YjNlNGFmLTc1NDQtNDJiMi1iYWYwLTRiOTA2ZTNjNjY0MQ.jpg\" width=\"640\" \/\u003E\u003C\/a\u003E\u003C\/div\u003E\n\u003Cbr \/\u003E\nIn my \u003Ca href=\"http:\/\/blog.backspace.academy\/2016\/08\/shared-responsibility-stopping-threats.html\"\u003Elast post\u003C\/a\u003E I talked about techniques to stop malicious web automation services at the source before they reach AWS infrastructure. Now we will get our hands dirty with some code to put it into action. Don't worry if you are not an experienced coder, you should still be able to follow along.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Ch3\u003E\nHow do Bot scripts work?\u003C\/h3\u003E\nA rendered web page contains a Document Object Model (DOM). The DOM defines all the elements on the page such as forms and input fields. Bots mimic a real user that enters information in fields, clicks on buttons etc. To do this the bot needs to identify the relevant elements in the DOM. DOM elements are identified using CSS selectors. Bot scripts consist of a series of steps that detail CSS selectors and what action to perform on them.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nThe DOM structure and elements of a page can be quickly identified using a browser. Pressing F12 in your browser will launch developer tools with this information:\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003E\n\u003Ca href=\"https:\/\/developer.chrome.com\/devtools\/images\/devtools-window.png\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003E\u003Cimg border=\"0\" height=\"230\" src=\"https:\/\/developer.chrome.com\/devtools\/images\/devtools-window.png\" width=\"640\" \/\u003E\u003C\/a\u003E\u003C\/div\u003E\n\u003Cbr \/\u003E\nTo see specific details of a DOM element simply right click on the element on the page and select 'inspect':\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003E\n\u003Ca href=\"https:\/\/1.bp.blogspot.com\/-ZBXiW_-KUqE\/V8yof19_FhI\/AAAAAAAAAV0\/xn6pkztN924SlSN7lEQPVJPrSLsPOya-gCLcB\/s1600\/dom-inspect.png\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003E\u003Cimg border=\"0\" src=\"https:\/\/1.bp.blogspot.com\/-ZBXiW_-KUqE\/V8yof19_FhI\/AAAAAAAAAV0\/xn6pkztN924SlSN7lEQPVJPrSLsPOya-gCLcB\/s1600\/dom-inspect.png\" \/\u003E\u003C\/a\u003E\u003C\/div\u003E\n\u003Cbr \/\u003E\nThis will open up the developer tools with the element identified. You can get the CSS selector for the element easily by again right clicking on the element in the developer tools:\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003E\n\u003Ca href=\"https:\/\/2.bp.blogspot.com\/-p9sbSmdhswM\/V8ypdqXodEI\/AAAAAAAAAV4\/mUrbDihE3nMDUAzCJ_EXNJI60fVM6Kt-wCLcB\/s1600\/dom-selector.png\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003E\u003Cimg border=\"0\" src=\"https:\/\/2.bp.blogspot.com\/-p9sbSmdhswM\/V8ypdqXodEI\/AAAAAAAAAV4\/mUrbDihE3nMDUAzCJ_EXNJI60fVM6Kt-wCLcB\/s1600\/dom-selector.png\" \/\u003E\u003C\/a\u003E\u003C\/div\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nNote that this will be only one representation of the element as a CSS selector (generally the shortest one). There are a number of ways an element can be defined as a CSS selector including:\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cul\u003E\n\u003Cli\u003Eid name\u003C\/li\u003E\n\u003Cli\u003Einput name\u003C\/li\u003E\n\u003Cli\u003Eclass names\u003C\/li\u003E\n\u003Cli\u003EDOM traversal e.g. defining its chain of parent elements in the DOM\u003C\/li\u003E\n\u003Cli\u003EText inside the element using Jquery ':contains'.\u003C\/li\u003E\n\u003C\/ul\u003E\n\u003Cbr \/\u003E\n\u003Ch3\u003E\nDynamic CSS Selectors\u003C\/h3\u003E\nTo make life difficult to develop bot scripts you can use dynamic CSS selectors. Instead of creating the same CSS selectors each time your page is rendered, you can look at changing these randomly each time.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nWhen using NodeJS and Express this is quite straightforward as your are already rendering pages on the server. Simply introduce some code to mix this up a bit.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Ch3\u003E\nLet's Start\u003C\/h3\u003E\n\u003Cbr \/\u003E\nFirst of all set up an EC2 instance with NodeJS and Express set up to render pages. If you are unsure you can view the video below:\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Ca href=\"https:\/\/vimeo.com\/145017165\" target=\"_blank\"\u003Ehttps:\/\/vimeo.com\/145017165\u003C\/a\u003E\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nTo save you typing, the \u003Ca href=\"https:\/\/gist.github.com\/BackSpaceTech\/41ed0973b96cf1360dc788ad5219a7b9\" target=\"_blank\"\u003Ecode is available at Gist\u003C\/a\u003E\u0026nbsp;(also Blogger tends to screw up code when it is published).\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nNow let's change index.js to create a simple login form.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nPoint your browser to the public IP address of your instance to check everything is ok. e.g. xxx.xxx.xxx.xxx:8080\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nNow change the index.js file to include a dynamicCSS function :\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cpre class=\"brush: js\"\u003Eapp.get('\/', function(request, response) {\n  response.send(dynamicCSS())\n})\n\nfunction dynamicCSS(){\n x = ''\n x += '\u003Cform\u003E\n';\n x += '\u003Cbr \/\u003E\n\u003Ch1\u003E\nPlease Login\u003C\/h1\u003E\n;\n x += '\u003Cinput id=\"username\" name=\"username\" placeholder=\"Enter Username\" type=\"text\" \/\u003E';\n x += '\u003Cinput id=\"password\" name=\"password\" placeholder=\"Enter Password\" type=\"password\" \/\u003E'; \n x += '\u003Cbutton type=\"submit\"\u003ELog in\u003C\/button\u003E'\n x += '\u003C\/form\u003E\n';\n return x\n}\n\u003C\/pre\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nNow do npm start at the command line of your ec2 instance and refresh the browser page. You will now see our very simple login form:\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003E\n\u003Ca href=\"https:\/\/1.bp.blogspot.com\/-p9LLTCAWglQ\/V8zMfrp6eDI\/AAAAAAAAAWM\/gahpx2v92NIfU6tRi2aH8owa7SbkCO8mQCLcB\/s1600\/login.PNG\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003E\u003Cimg border=\"0\" height=\"74\" src=\"https:\/\/1.bp.blogspot.com\/-p9LLTCAWglQ\/V8zMfrp6eDI\/AAAAAAAAAWM\/gahpx2v92NIfU6tRi2aH8owa7SbkCO8mQCLcB\/s320\/login.PNG\" width=\"320\" \/\u003E\u003C\/a\u003E\u003C\/div\u003E\n\u003Cbr \/\u003E\nThe problem with this form is that it is really easy to identify the dom elements required to login. The id, name, placeholder all refer to username or password.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nNow let's change our code and introduce dynamically created CSS selectors.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cpre class=\"brush: js\"\u003Evar loginElements = {\n username: '',\n password: ''\n }\n\nfunction dynamicCSS(){\n var username = randomString()\n var password = randomString()\n loginElements.username = username\n loginElements.password = password \n x = ''\n x += '\u003Cform\u003E\n'\n x += '\u003Cbr \/\u003E\n\u003Ch1\u003E\nPlease Login\u003C\/h1\u003E\n'\n x += '\u003Cinput id=\"' + username + '\" name=\"' + username + '\" placeholder=\"Enter Username\" type=\"text\" \/\u003E'\n x += '\u003Cinput id=\"' + password + '\" name=\"' + password + '\" placeholder=\"Enter Password\" type=\"password\" \/\u003E'\n x += '\u003Cbutton type=\"submit\"\u003ELog in\u003C\/button\u003E'\n x += '\u003C\/form\u003E\n'\n return x\n}\n\nfunction randomString(){\n chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split('')\n chars.sort(function() {\n   return 0.5 - Math.random()\n })\n return chars.splice(0, 8).toString().replace(\/,\/g, '')\n}\n\u003C\/pre\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nThis now generates a random string for the id and name tags of the input elements. This makes it not possible to use these in a reliable bot script. If you do npm start again and view the the view the element in developer tools you can see the random strings.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nWe now need to look at the other ways our elements can be identified as CSS selectors. As you can see the text \"username\" and \"password\" is still used in the placeholders and input type tag. Also the DOM structure itself doesn't change dynamically, making it possible to reference the element through traversing the DOM structure.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nWe will address both problems by creating random decoy input elements with the same parameters. The CSS position property will allow us to stack them on top of each other so that the decoy elements are not visible on the page:\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cpre class=\"brush: js\"\u003Eapp.get('\/', function(request, response) {\n  response.send(dynamicCSS())\n})\n\nvar loginElements = {\n  username: '',\n  password: ''\n}\n\nfunction dynamicCSS(){\n  var username, password\n  x = ''\n  x += '\u003Cform\u003E\n'\n  x += '\u003Cbr \/\u003E\n\u003Ch1\u003E\nPlease Login\u003C\/h1\u003E\n'\n  y = Math.floor((Math.random()*5)) + 2\n  for (var a=0; a\u003Cy a=\"\" class=\"username\" id=\"' + username + '\" input=\"\" name=\"' + username + '\" password=\"randonString()\" placeholder=\"Enter Username\" type=\"text\" username=\"randonString()\" x=\"\"\u003E'\n    x += '\u003Cinput class=\"password\" id=\"' + password + '\" name=\"' + password + '\" placeholder=\"Enter Password\" type=\"password\" \/\u003E'\n    loginElements.username = username\n    loginElements.password = password\n  }\n  x += '\u003Cbutton class=\"btnSubmit\" type=\"submit\"\u003ELog in\u003C\/button\u003E'\n  x += '\u003C\/y\u003E\u003C\/form\u003E\n'\n  return x\n}\n\nfunction randonString(){\n  chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split('')\n  chars.sort(function() {\n  return 0.5 - Math.random()\n  })\n  return chars.splice(0, 8).toString().replace(\/,\/g, '')\n}\n\n\u003C\/pre\u003E\n\u003Cbr \/\u003E\n\u003Cdiv\u003E\n\u003Cbr \/\u003E\u003C\/div\u003E\nNow when you view the DOM in your browser developer tools, \u0026nbsp;you can see the decoy input elements created underneath the real input element. If you refresh your browser you will see a different number of elements created each time (between 1 and 5 created).\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003E\n\u003Ca href=\"https:\/\/3.bp.blogspot.com\/-znOd_64MkUE\/V8zn0_MJ-AI\/AAAAAAAAAWc\/AWrsqzkNfp4eWSuw80Uha-3sJjFp_-QQgCLcB\/s1600\/dom-dynamic.png\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003E\u003Cimg border=\"0\" src=\"https:\/\/3.bp.blogspot.com\/-znOd_64MkUE\/V8zn0_MJ-AI\/AAAAAAAAAWc\/AWrsqzkNfp4eWSuw80Uha-3sJjFp_-QQgCLcB\/s1600\/dom-dynamic.png\" \/\u003E\u003C\/a\u003E\u003C\/div\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nThe bot creator can no longer use the username and password placeholders or input types to identify the elements. They can also not use the DOM structure to traverse through the DOM as this is changing also. As pointed out by a reader of this post (thanks Vadim!), you should also put some random inputs after to handle jquery \":last\". A good place would be underneath your logo.\u003Cbr \/\u003E\n\u003Cpre class=\"brush: js\"\u003Ey = Math.floor((Math.random()*5)) + 2\nfor (var a=0; a\u003Cy a=\"\" class=\"username\" id=\"' + username + '\" input=\"\" name=\"' + username + '\" password=\"randonString()\" placeholder=\"Enter Username\" type=\"text\" username=\"randonString()\" x=\"\"\u003E'\n  x += '\u003Cinput class=\"password\" id=\"' + password + '\" name=\"' + password + '\" placeholder=\"Enter Password\" type=\"password\" \/\u003E'\n  loginElements.username = username\n  loginElements.password = password\n}\nfor (var a=0; a\u003Cy a=\"\" class=\"username\" id=\"' + username + '\" input=\"\" name=\"' + username + '\" password=\"randonString()\" placeholder=\"Enter Username\" type=\"text\" username=\"randonString()\" x=\"\"\u003E'\n  x += '\u003Cinput class=\"password\" id=\"' + password + '\" name=\"' + password + '\" placeholder=\"Enter Password\" type=\"password\" \/\u003E'\n  document.getElementById(username).style.visibility = \"hidden\";\n  document.getElementById(password).style.visibility = \"hidden\";\n}\n\u003C\/y\u003E\u003C\/y\u003E\u003C\/pre\u003E\n\u003Cbr \/\u003E\nThe next thing a bot script can do is click on an x-y position on the screen. We can handle this by randomly changing the position of the elements.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cpre class=\"brush: js\"\u003Evar loginElements = {\n username: '',\n password: ''\n }\n\nfunction dynamicCSS(){\n  var username, password\n  x = ''\n  if ((Math.random()*2) \u0026gt; 1)\n    x += '\u003Cstyle\u003E.btnSubmit,.password,.username{position:absolute;left:10}.username{top:50px}.password{top:80px}.btnSubmit{top:110px}\u003C\/style\u003E'\n  else\n    x += '\u003Cstyle\u003E.btnSubmit,.password,.username{position:absolute;left:10}.username{top:80px}.password{top:110px}.btnSubmit{top:140px}\u003C\/style\u003E'\n  x += '\u003Cform\u003E\n'\n  x += '\u003Cbr \/\u003E\n\u003Ch1\u003E\nPlease Login\u003C\/h1\u003E\n'\n  y = Math.floor((Math.random()*5)) + 2\n  for (var a=0; a\u003Cy a=\"\" class=\"username\" id=\"' + username + '\" input=\"\" name=\"' + username + '\" password=\"randonString()\" placeholder=\"Enter Username\" type=\"text\" username=\"randonString()\" x=\"\"\u003E'\n    x += '\u003Cinput class=\"password\" id=\"' + password + '\" name=\"' + password + '\" placeholder=\"Enter Password\" type=\"password\" \/\u003E'\n    loginElements.username = username\n    loginElements.password = password\n  }\n  x += '\u003Cbutton class=\"btnSubmit\" type=\"submit\"\u003ELog in\u003C\/button\u003E'\n  x += '\u003C\/y\u003E\u003C\/form\u003E\n'\n  return x\n}\nfunction randomString(){\n chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split('')\n chars.sort(function() {\n   return 0.5 - Math.random()\n })\n return chars.splice(0, 8).toString().replace(\/,\/g, '')\n}\n\u003C\/pre\u003E\n\u003Cbr \/\u003E\n\u003Cdiv\u003E\n\u003Cbr \/\u003E\u003C\/div\u003E\n\u003Cdiv\u003E\nThe position of the input elements is now random. This currently only has two positions but you can elaborate on this to create many possible combinations of positions. You may also make your login form inside a modal window that changes position on the screen.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nIf want to go further you can look having two login forms, username followed by password. Or even better, randomly change between the two.\u003C\/div\u003E\n\u003Cdiv\u003E\n\u003Cbr \/\u003E\u003C\/div\u003E\n\u003Cdiv\u003E\nWe have now addressed the possible techniques a bot creator can use to identify your input elements and login to your site.\u003C\/div\u003E\n\u003Cdiv\u003E\n\u003Cbr \/\u003E\u003C\/div\u003E\n\u003Cdiv\u003E\nCongratulations, you made it to the end!\u003C\/div\u003E\n\u003Ch3\u003E\nWhat's next?\u003C\/h3\u003E\nIn \u003Ca href=\"http:\/\/blog.backspace.academy\/2016\/09\/shared-responsibility-3-identify-and.html\" target=\"_blank\"\u003Emy next post\u003C\/a\u003E I will introduce techniques to identify bots and then look at launching a counter attack on the bot to crash it after it has been positively identified.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nBe sure to subscribe to the blog so that you can get the latest updates.\u003Cbr \/\u003E\n\u003Cbr \/\u003E\nFor more AWS training and tutorials check out\u0026nbsp;\u003Ca href=\"https:\/\/backspace.academy\/\"\u003Ebackspace.academy\u003C\/a\u003E\n\u003Ca href=\"https:\/\/backspace.academy\/\" target=\"_blank\"\u003E\u003Cimg src=\"https:\/\/backspace.academy\/assets\/img\/logo.svg\" \/\u003E\u003C\/a\u003E"},"link":[{"rel":"replies","type":"application/atom+xml","href":"http:\/\/learn-aws.blogspot.com\/feeds\/6695927890245298364\/comments\/default","title":"Post Comments"},{"rel":"replies","type":"text/html","href":"http:\/\/learn-aws.blogspot.com\/2016\/09\/shared-responsibility-2-using-dynamic.html#comment-form","title":"2 Comments"},{"rel":"edit","type":"application/atom+xml","href":"http:\/\/www.blogger.com\/feeds\/4603622075690903012\/posts\/default\/6695927890245298364"},{"rel":"self","type":"application/atom+xml","href":"http:\/\/www.blogger.com\/feeds\/4603622075690903012\/posts\/default\/6695927890245298364"},{"rel":"alternate","type":"text/html","href":"http:\/\/learn-aws.blogspot.com\/2016\/09\/shared-responsibility-2-using-dynamic.html","title":"Shared Responsibility 2 - Using Dynamic CSS Selectors to stop the bots."}],"author":[{"name":{"$t":"BackSpace Academy"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/15061292652079774775"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"16","height":"16","src":"https:\/\/img1.blogblog.com\/img\/b16-rounded.gif"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/1.bp.blogspot.com\/-ZBXiW_-KUqE\/V8yof19_FhI\/AAAAAAAAAV0\/xn6pkztN924SlSN7lEQPVJPrSLsPOya-gCLcB\/s72-c\/dom-inspect.png","height":"72","width":"72"},"thr$total":{"$t":"2"}}]}});